# ADR-0009 — Spend/abuse gate: honest re-scope of the structural claim post-0007 - **Status:** Accepted (maintainer-ratified 2026-07-03) - **Date:** 2026-07-03 - **Origin:** [2026-07-03 adversarial review](../reviews/2026-07-03-adversarial-review.md), finding D4 / recommendation R3. - **Amends:** [ADR-0002](0002-north-star-and-fused-core.md) (pillar table), `README.md` (pillar 3), `docs/PORT_PLAN.md` (§10 toll-fraud row), `docs/ARCHITECTURE.md` (in-boundary spend/abuse gate bullet) ## Context ADR-0002 promoted the spend/abuse gate to *constitutive* with the argument: "a runaway brain can't exceed pacing or spend because it doesn't hold the wire — **structurally impossible for a 3-vendor stack**." Under [ADR-0003](0003-sip-rust-native-trunk.md) (rutster terminates the carrier trunk) that claim was structural: rutster held the wire. [ADR-0007](0007-trunk-rented-transport.md) changed the topology. Under rented transport, **rutster does not hold the wire either — the CPaaS does.** The gate's mechanism became "rutster holds the provider call-control credential and the brain doesn't," which is an IAM/configuration property, not a structural one: 1. Any orchestrator that holds the provider credentials can make the same claim (a Python framework with a spend counter and scoped credentials). 2. The CPaaS itself ships spend limits. 3. A provider credential scoped too widely, or leaked outside rutster, bypasses the gate entirely — rutster cannot see, let alone stop, provider-API calls it doesn't mediate. Meanwhile the ADR-0007 architecture *is* a 3-vendor stack (CPaaS + rutster + brain), making the unamended pillar self-describing. The claim must be narrowed to what is actually true before it is repeated in public-facing material. ## Decision The spend/abuse gate remains **FOB and constitutive** ([ADR-0008](0008-fob-and-green-zone.md): security-constitutive), but its guarantees are restated precisely: **What the gate structurally guarantees (true post-0007):** 1. **Credential isolation** — the brain never holds provider credentials. Every call-origination, transfer, or hangup the brain wants happens through rutster's API, where the gate sits. The brain cannot spend *through rutster* beyond the gate, ever. 2. **Unskippable on mediated egress** — spend/pacing checks are in-process with the tap and the provider call-control client. There is no deployment topology in which brain-initiated actions reach the provider without passing the gate. (Contrast: a bolt-on spend service in a 3-vendor chain can be *routed around*; an in-boundary gate cannot — for traffic rutster mediates.) 3. **Media-plane enforcement** — pacing, half-duplex, and playout caps are enforced over media rutster terminates. The brain *proposes* audio; the core *disposes*. A flooding or overlapping brain is throttled at the playout ring regardless of what it sends. 4. **Audit co-location** — every allowed/denied spend decision is recorded at the same trust boundary that made it, so the audit log and the enforcement point cannot diverge. **What the gate does NOT guarantee (stated so we don't kid ourselves):** - It cannot constrain spend on provider credentials used *outside* rutster (mis-scoped keys, console access, a second integration holding the same key). **Deployment guidance must pair the gate with provider-side caps** (defense in depth) and per-integration credential scoping. - The "structurally impossible for a 3-vendor stack" phrasing is **retired**. The honest comparative claim: *in-boundary co-location makes the gate unskippable for everything rutster mediates, and rutster mediates everything the brain can do* — a property a bolt-on spend service cannot offer, but not a physical monopoly on the wire. ## Propagation (on acceptance) | Doc | Change | |---|---| | ADR-0002 | Add amendment note to the pillar table pointing here | | `README.md` pillar 3 | Replace "doesn't hold the wire — structurally impossible for a 3-vendor stack" with the credential-isolation + unskippable-mediation phrasing | | `docs/ARCHITECTURE.md` | Same fix in the "In-boundary spend / abuse gate" bullet | | `docs/PORT_PLAN.md` §10 | Same fix in the toll-fraud/spend row | ## Consequences - **Positive:** the security narrative survives adversarial scrutiny (the previous phrasing invited a one-tweet rebuttal); the gate's real guarantees — credential isolation, unskippable mediation, media-plane throttling, audit co-location — are strong and defensible; deployment guidance gains the provider-side-caps pairing, which operators need anyway. - **Negative:** the pitch loses a superlative ("structurally impossible") in exchange for precision; step 6's scope grows slightly (the gate should also *meter* — per-call cost attribution into the CDR — per the [market feature scan F5](../reviews/2026-07-03-market-feature-scan.md), since integrator unit economics run on cost-per-contained-call). ## Amendment 2026-07-04 — enforcement locality ≠ accounting locality - **Status:** Proposed (pending maintainer ratification) - **Origin:** [2026-07-04 scalability & infra-fit review](../reviews/2026-07-04-scalability-infra-review.md), finding M5. Guarantee 2 above prescribes where the **check** sits — in-process with the tap and the provider call-control client. It is silent on where the **accounting state** lives, and at N>1 core instances that silence becomes a correctness trap: per-instance counters make every spend/pacing cap silently N× the fleet intent, and toll-fraud thresholds never trip because attempts spread across instances. Clarification, binding on the step-6 implementation: 1. **Enforcement is in-process** (unchanged — constitutive; guarantee 2 stands). 2. **Accounting is shared.** The gate is built against a ledger trait with atomic check-and-reserve semantics from day one: an in-memory implementation for single-node, a Valkey-backed one ([ADR-0005](0005-event-bus.md)) for fleets. The check path never assumes counter locality. 3. **ADR-0005 constraint 2 is not a counter-argument.** "The bus is not the source of truth for billing- or call-loss-critical state" governs the *durable CDR*. Live enforcement counters (spend/pacing/rate state) are ephemeral control state — exactly what Valkey KV is for. Losing them on a Valkey restart degrades fail-safe (re-count from zero, provider-side caps as the backstop per this ADR's deployment guidance) — not to billing corruption. ## References - [ADR-0002](0002-north-star-and-fused-core.md) — the pillar this amends - [ADR-0007](0007-trunk-rented-transport.md) — the topology change that invalidated the phrasing - [ADR-0008](0008-fob-and-green-zone.md) — FOB test the gate still passes (security-constitutive) - [Adversarial review 2026-07-03](../reviews/2026-07-03-adversarial-review.md) — finding D4