# MCP restart audit events and incidents (#665) Restarts and recovery attempts leave a forensic trail. Failed drains and break-glass paths also raise durable Gitea incident issues so unsafe restarts cannot be silently repeated. Parent umbrella: **#655**. Related: impact coordinator **#658**, drain proof **#661**, restart classes **#663**, break-glass **#664**, post-restart reconcile **#662**, vision **#652**, roadmap **#653**, console recovery **#642**. ## Components | Piece | Where | Responsibility | |-------|-------|----------------| | Event schema + emission | `restart_audit.py` | `mcp.restart.*` vocabulary, redacted payload builder, append-only sink via `gitea_audit` | | Fail-closed privileged gate | `restart_audit.require_audit_or_deny` | When `GITEA_AUDIT_LOG` is set and the write fails, privileged apply is denied | | Incident descriptors | `restart_audit.build_incident_descriptor` | Durable follow-up issues (failed drain, break-glass, reconcile unresolved, unguarded) | | Materializer | `restart_audit.materialize_incident` | Injected `create_issue_fn` (network kept out of pure tests) | | Wiring | `gitea_request_mcp_restart` | Correlation id, impact-preview audit, apply-gate / break-glass audit + incident creation | ## Event vocabulary | Event type | When | |------------|------| | `mcp.restart.impact_preview` | Every `gitea_request_mcp_restart` evaluation | | `mcp.restart.drain_enter` | Drain window starts (schema reserved; emit from drain path) | | `mcp.restart.drain_exit` | Drain window ends | | `mcp.restart.drain_proof` | Drain-proof verification result | | `mcp.restart.apply_gate` | Apply hard gate (`dry_run=False`) | | `mcp.restart.break_glass` | Authorized break-glass bypass | | `mcp.restart.post_restart_reconcile` | Post-restart reconcile outcome | | `mcp.restart.narrower_recovery` | Narrower recovery attempt recorded | | `mcp.restart.unguarded_detected` | Unguarded restart path detected | All free text is redacted before sink write or issue body assembly. Emission never raises; callers decide fail-closed policy. ## Correlation Each restart lifecycle mints a short `correlation_id` (`rst-` + 16 hex) shared across impact preview → apply gate → incident descriptors so operators can join the trail. ## Privileged deny-on-audit-fail Rollout policy (issue #665): 1. Configure `GITEA_AUDIT_LOG` so writes land. 2. Only then enforce deny when a privileged restart path cannot audit. When audit is **not** configured, privileged apply still proceeds (no false denials during rollout). When audit **is** configured and the write fails, `apply_authorized` is cleared. ## Incidents | Kind | Trigger | |------|---------| | `restart_failed_drain` | Apply denied by drain hard gate / failed proof | | `restart_break_glass` | Any authorized break-glass apply | | `restart_reconcile_unresolved` | Post-restart reconcile left work unresolved | | `restart_unguarded_detected` | Unguarded restart attempt detected | Break-glass **always** creates an incident descriptor (and a Gitea issue when the create path is available). Failed drain does the same. Incident bodies include correlation id, session, class, scope, proof id, and redacted reasons. Default labels: `mcp-health`, `safety`, `observability`, `status:ready`, `type:bug`, `workflow-hardening`. ## Tool payload surface `gitea_request_mcp_restart` returns: * `correlation_id` — lifecycle join key * `restart_audit.impact_preview_written` — sink success for the preview event * `restart_audit.apply_gate_written` — sink success for apply/break-glass (apply only) * `restart_audit.incident_result` — materialization outcome when an incident was required * `incident` — durable descriptor (when gate requires follow-up) ## Security * No secrets in audit payloads or issue bodies. * This module never restarts a process. * Drain proof verification remains #661; audit only records the decision. * Incident creation failures are recorded in `incident_result.reasons` and never crash the restart evaluation path (audit write failure still fails closed for privileged apply when the sink is enabled). ## Tests See `tests/test_restart_audit.py`: schema, redaction, emission, deny policy, incident materialization mocks, break-glass / failed-drain selection.