# 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:** Accepted (maintainer-ratified 2026-07-05)
- **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
