File size: 7,653 Bytes
cd8bd0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# Incident Response Runbook β€” OmniRoute (2026-06-18)

**Status**: Authoritative. The 71-pillar audit (L61) references this doc
for the `Obs > 2.00` gate.
**Owner**: observability-circle (lead: security-circle lead).
**SLOs**: see `docs/PERF_BUDGETS.md` Β§ 1 (top-level SLOs) and
`ops/slos.yaml` (machine-readable form, generated by the Bifrost team).
**Disclosure policy**: see `SECURITY.md` (vulnerability disclosure only,
separate flow).

This runbook is the operational playbook for **non-security** incidents:
outages, latency regressions, error-budget burn, and provider-side
failures. Vulnerability disclosure stays on `SECURITY.md`; do not route
those through this runbook.

---

## 1. Severity ladder

| Sev | Definition | Examples | Page on | Resolve by |
|---|---|---|---|---|
| **SEV-1** | User-visible outage; > 50 % of requests failing or > 2x SLO breach for 5 min. | Cluster down; auth layer broken; 5xx flood. | On-call P0 (immediate) | 4 h |
| **SEV-2** | Significant degradation; 1.5–2x SLO breach for 15 min, or single-tenant impact. | Single provider down; p95 > 1.5x budget; rate-limit runaway. | On-call P1 (15 min) | 24 h |
| **SEV-3** | Latent bug or near-miss; no current user impact but error budget at risk. | Memory leak trending up; circuit breaker tripping on one provider. | Slack `#omniroute-ops` (next standup) | 7 d |
| **SEV-4** | Cosmetic / informational. | Log line noise; non-binding UI glitch. | Next weekly review | Next refactor cycle |

**Burn-rate escalation** (per `docs/PERF_BUDGETS.md` Β§ 1): 6x for 5 min
is SEV-1; 2x for 1 h is SEV-2; sustained < 1x for 7 d demotes to SEV-3.

---

## 2. Detection sources

| Source | Signal | Routing |
|---|---|---|
| Prometheus (`/metrics`) | Counter deltas (5xx, latency) | Alertmanager β†’ PagerDuty |
| Grafana SLO dashboards | SLO burn-rate panels | Slack `#omniroute-ops` |
| Uptime probe (`/api/health/ping`) | 3 consecutive failures from 3 regions | Alertmanager β†’ PagerDuty |
| Dependabot | New CVE in dependency | GitHub issue + Slack `#security` |
| User report (support@) | Manual triage | Slack `#omniroute-triage` |
| Error budget burn alert | `slo_burn_rate > threshold` | Alertmanager |

Prometheus and Alertmanager are configured in the deploy repo (see
`docs/operations/DEPLOY.md` once published; currently inline in
`docker-compose.prod.yml`).

---

## 3. First-15-minutes checklist

When paged, the on-call engineer runs this checklist verbatim. **Do
not** skip steps; each is timed.

1. **0:00** β€” Acknowledge the page in PagerDuty. Stops the escalation
   timer and notifies the secondary.
2. **0:02** β€” Open the [SLO dashboard][dash] and the [incident
   channel][chan] (`#inc-YYYY-MM-DD-slug`). Post a single-line ack
   with the alert name and the time.
3. **0:05** β€” Classify severity per Β§ 1. If SEV-1 or SEV-2, declare
   the incident in the channel and tag `@incident-commander`.
4. **0:08** β€” Capture the alert payload, the most recent deploy SHA,
   and the top 5 slow / erroring endpoints. Post to the channel.
5. **0:12** β€” Decide: **mitigate first, root-cause later**. Choose
   one of:
   - **Roll back** to the last green deploy (`bin/rollback.sh vX.Y.Z`).
   - **Failover** to the healthy replicas (Caddy LB removes the bad
     replica automatically; verify with `curl /api/health/ping`).
   - **Disable** a broken provider connection via `PUT /api/providers/{id}`
     with `{ "isActive": false }` (one-line toggle; safe by default).
6. **0:15** β€” Post the chosen mitigation in the channel. If the page
   is still firing after 5 more minutes, escalate to the secondary.

[chan]: https://phenotype.slack.com/archives/incidents
[dash]: https://grafana.phenotype.internal/d/omniroute-slos

---

## 4. Mitigation runbooks (per failure mode)

### 4.1 Provider outage (single provider down)

1. `PUT /api/providers/{id}` with `{ "isActive": false }` β€” toggles the
   connection off in the registry; all routes re-resolve on next request.
2. Verify p95 returns to budget within 5 min.
3. If all providers for a model are down, **disable the model** (see
   `src/lib/a2a/skills/providerDiscovery.ts` for the disable path).
4. Update the [status page][status] with a banner if the outage
   exceeds 15 min.

[status]: https://status.phenotype.dev

### 4.2 Cluster-wide latency regression

1. Check the most recent deploy (`/api/system/version` returns the running version).
2. If p95 doubled vs the 7-day baseline, **roll back** to the prior
   SHA via `bin/rollback.sh`.
3. If the regression is provider-side, see Β§ 4.1.

### 4.3 Auth layer broken (5xx on /v1/responses for all keys)

1. Check the authz-inventory endpoint:
   `curl https://api.omniroute.dev/api/settings/authz-inventory | jq`.
2. If `policies_active` is empty, restore from the last good backup
   (`bin/restore-policies.sh <sha>`).
3. Roll back if the cause is unclear.

### 4.4 Data-layer incident (sqlite corruption, audit log gap)

1. **Stop the cluster** (`docker compose -f docker-compose.prod.yml
   stop`) β€” preventing further writes is more important than uptime.
2. Snapshot the data volume (`bin/snapshot-data.sh`).
3. Open a SEV-1; this is data-loss territory. Page the data-team.
4. Restore from the last verified backup (see `docs/BACKUP.md` once
   published; currently the runbook is `bin/restore-data.sh <sha>`).

### 4.5 Security incident (vulnerability disclosure)

**Stop.** This is the `SECURITY.md` path, not this runbook. Page the
security on-call (`@security-team`); do not post details to
`#omniroute-ops`.

---

## 5. Communication

| Audience | Channel | Cadence | Owner |
|---|---|---|---|
| Engineering | `#inc-YYYY-MM-DD-slug` | Real-time | Incident commander |
| Status page | `status.phenotype.dev` | Every 30 min during SEV-1/2 | On-call |
| Customers (email) | `announce@phenotype.dev` | At SEV-1 start + resolution | Comms lead |
| Upstream providers | Direct contact | At SEV-1 start | Vendor mgmt |
| Postmortem | `docs/postmortem/YYYY-MM-DD-slug.md` | Within 5 business days | Incident commander |

Postmortem template is at `docs/postmortem/TEMPLATE.md` (forthcoming;
see ADR-024 for the cadence and ADR-029 for the postmortem convention).

---

## 6. On-call rotation

| Role | Primary | Secondary | Rotation |
|---|---|---|---|
| Engineering on-call | security-circle lead | @open-sse | Weekly, Mon 09:00 PDT |
| Security on-call | @security-team | β€” | Weekly |
| Data on-call | @db-team | β€” | Weekly |
| Comms lead | @comms | β€” | As needed |

**Handoff**: every Monday 09:00 PDT, the outgoing on-call posts a
written handoff to the incoming in `#omnirouse-ops-handoff` covering:
open SEV-3/4 items, scheduled maintenance windows, and any
in-flight mitigations.

---

## 7. Postmortem expectations

- **Blameless**. People did the best they could with the information
  they had. Focus on systems, signals, and decision points.
- **Within 5 business days** of resolution. File via
  `gh issue create --label postmortem --label SEV-1` (or `--label SEV-2`).
- **Action items** must be assigned, dated, and tracked in
  `docs/TECH_DEBT.md` (P0 < 30 d, P1 < 90 d per that doc's SLA).
- **Mandatory attendees**: incident commander, on-call, any engineer
  who touched the mitigation, and one person who was *not* involved
  (fresh-eyes review).

---

## 8. Review log

| Date | Reviewer | Change |
|---|---|---|
| 2026-06-18 | security-circle lead | Initial runbook; severity ladder + 15-min checklist + 4.1–4.5 mitigation runbooks. Closes 71-pillar audit L61 (1/3 β†’ 2/3). |
| 2026-07-18 (planned) | observability-circle | Wire on-call rotation into PagerDuty schedule; add the postmortem template. |