# 0035 — Fatigue control derived from the log and enforced on the phone; grants bound to the requesting session - Status: accepted (amends 0017 §5; amends 0016 §1 and 0023 §1 on the role of the session capability); §2 scope sentence and §3 overlap block amended by 0047; §3 number formula and banner semantics amended by 0050 (six digits, no trust styling, never coalesced) - Date: 2026-10-22 - Area: protocol | product ## Context Spec review round 1 (HUMAN-FACTORS-R1-1, -R1-6, -R1-7, AGENT-HOST-R1-9, -R1-10, -R1-11): - Every fatigue control lived in daemon memory and was keyed on a session the attacker mints (`session.open` or no token). Restarting the daemon wiped it. Unanswered requests expire without counting as denials, so the natural reaction to a storm — ignoring it — never throttled anything. The PWA storm banner fired only above the daemon's own cap. This is the Uber 2022 push-bombing pattern. - ADR 0016 §1 / 0023 §1 require a per-session capability; v2 made it optional and grants were per host, so a lease approved for "claude in ~/proj-a" was usable by any program on the host, while the approval screen suggested otherwise. - Number matching was requester-chosen and pick-1-of-3. ## Decision 1. **Fatigue state is a function of the verified log**, computed identically by the daemon (to refuse) and by the PWA (to collapse and silence), so neither restart nor new sessions reset it. Per **host and handle**: - an **outcome** is a denial entry, or a request whose `expires` passed without grant or denial ("expired unanswered", a soft denial); - back-off after an outcome: the host may not request that handle for 60 s, doubling per further outcome within 1 h, capped at 30 min; - **quarantine:** 3 outcomes for a host within 10 min ⇒ the daemon refuses new requests from that host for 30 min (`quarantined`); the PWA shows one collapsed **storm card** for the host, auto-hides further requests from it during the window (they expire unanswered) and offers "Mute this host for N h" (local PWA setting in the PoC); - the PWA storm banner fires at ≥ 3 requests from one host in 10 min, counting expired ones (below the daemon cap of 3 pending); - times come from entry time fields bounded by the relay's ±300 s hygiene check and the device's own clock; the rule is a throttle, not a security boundary. 2. **Sessions.** `session.open` stays a socket method (any client uid may call it; this is attribution, not authentication). Each request records `H(session token)` (or 32 zero bytes for "no session") and the session label in its sealed RequestDetails. A grant is usable only by callers presenting the **same** session token (or no token, if the request had none). The approval screen states the scope in plain words: "Usable by session '