eleven / ocho Public

docs: emergency runbook for critical security releases under the bake window (#830)

The clock stays; expediting is box-side. Writes down the invariant that keeps
every override honest (it must ride a different credential than the signing
key the delay defends against), the step-by-step procedure, the honest cost,
and the two designed-but-unbuilt rungs (consent-based expedite, second-key
co-signature) so the rationale lives in the repo, not in a chat transcript.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell 226aa050d53aa35ba5f158a377f207094799c4f7 parent 6be4dce
1 file changed, +75 −0
  • docs/server-releases.md +75 −0
diff --git a/docs/server-releases.md b/docs/server-releases.md
index f938f33..90c2caf 100644
--- a/docs/server-releases.md
+++ b/docs/server-releases.md
@@ -156,6 +156,81 @@ The mechanics (`bake.go`):
(Watch → Custom → Releases), so a publish you didn't make lands in your
inbox through a channel the release key never touches.
+### Critical security releases — the emergency runbook
+
+A critical fix wants to reach the fleet in minutes; the bake window makes
+beta/stable wait days. Both are security properties, and the resolution is
+**not** a faster feed: the clock stays untouched, and expediting is done from
+the box side. The rule that keeps every current and future override honest:
+
+> **An override is safe exactly when it cannot be exercised with the secret
+> the delay defends against.** The bake window defends against a compromised
+> *release signing key*; every expedite path below rides *box root* or an
+> *instance admin's deliberate act* — independent credentials. A same-key
+> override (a `critical: true` field in the manifest) is the one thing never
+> to build: it is the first flag an attacker with the key would set, and it
+> converts the whole defence into decoration.
+
+The procedure, for a fix that can't wait out the hold:
+
+1. **Land the fix on `main`** (normal PR flow — an emergency is when review
+ discipline pays, not when it lapses).
+2. **Publish and converge the edge fleet** — edge has no hold, so this is
+ already the whole story for every edge-riding space:
+
+ ```sh
+ ./deploy/release-server.sh publish "security: <one line>" && \
+ ./deploy/release-server.sh ship
+ ```
+
+3. **Promote to beta and stable.** The channel heads move immediately; only
+ *installation* is held. This also fires the release-alert push to every
+ subscribed space's admins — for self-hosted boxes, that push (and the
+ admin console's "available" line) *is* the expedite channel: their
+ operator applies it with their own root, on their own judgement.
+4. **Expedite boxes you operate** (requires root on each box — that's the
+ point):
+
+ ```sh
+ lchat update run --ignore-bake # whole box: install held heads now
+ lchat deploy <host> --version <ver> # or surgically, one instance
+ ```
+
+ `--ignore-bake` skips only the bake clock; pins, major pins, and
+ updates-off are still honoured.
+5. **Say what happened.** The release notes and the changelog entry should
+ name the fix plainly (members see the changelog banner after their space
+ updates). If the vuln is sensitive, "security fix — update promptly" is
+ enough; silent security releases train nobody to act on the next alert.
+
+What this costs, honestly: a self-hosted box whose operator ignores the push
+installs the fix only when the hold elapses (up to ~3.75 days on stable with
+maximum jitter). That is the deliberate trade — the window pays for itself
+the day a *malicious* release ships, and a malicious binary on an E2EE
+messenger (key exfiltration) is unrecoverable in a way a bounded patch lag
+is not. Expect social engineering in both directions: an attacker will label
+malware "critical security fix" precisely to make humans expedite it, which
+is why the expedite paths end at a human with an independent credential
+rather than at a feed flag.
+
+Two designed-but-unbuilt rungs, for when self-hoster numbers make
+"wait for each operator" unacceptable (build in this order):
+
+- **Consent-based expedite**: make the release alert actionable — an
+ "Install now" button in the admin console writes a one-shot, per-version
+ consent the box updater honours. The override power distributes to each
+ space's human; an attacker with the signing key would have to socially
+ engineer every admin individually. No new keys, no publisher power.
+- **Second-key co-signature**: a separate *emergency key*, stored apart from
+ the release key (offline / second operator — the value is that no single
+ machine compromise yields both), whose co-signature shortens the hold.
+ Three rules keep it from becoming the attack: the co-sig **binds the exact
+ sha and expires within hours** (no hoarding, no replay onto a later build);
+ the reduced hold is a **floor, never zero** (2–6h + jitter — the earliest
+ boxes stay canaries even in an emergency); and expedited releases alert
+ **louder, not quieter**, so abuse of the mechanism is the most visible
+ event in the system.
+
### Major version pinning
Releases carry two names: the **sha** (`version`, the artifact id everything