adr: +0007 rent trunk transport +0008 FOB/green-zone doctrine; supersede 0003

ADR-0007 — Trunk/PSTN strategy: rent the transport, no first-party SIP stack.
Supersedes ADR-0003 (Rust-native trunk SIP). The 2026-06 strategic-relevance
review stress-tested ADR-0003 against the 2026 market + solo-build realities
and it did not survive: a trunk SIP/media core is the highest-cost,
lowest-differentiation square on the board (the perceived-quality battle and
the buying criteria both live above the transport), and no production-hardened
pure-Rust SIP stack exists to stand on (ezk is bus-factor-1 / pre-1.0;
str0m self-describes as 'not for production' for SIP).

rutster owns no SIP stack. PSTN reach is rented transport in three layers:
  1. Primary (demo, most users) — CPaaS raw-media fork (Twilio Media Streams,
     Telnyx). Media-leg ingress, core-as-server (parallel to WebRTC ingress
     per ADR-0006). Use the raw-audio fork, *not* managed Voice-AI products
     that would consume the reflex loop.
  2. Graduation (on-prem) — out-of-tree SBC (Kamailio/FreeSWITCH/drachtio +
     rtpengine) B2BUAs carrier SIP into rutster as clean RTP/tap media,
     outside the trust boundary.
  3. Never — a first-party Rust trunk SIP stack. rsip/ezk stay off the
     critical path.

The only things rutster owns in Rust: the call model, the reflex loop, the
agent tap. Everything that touches a carrier is rented (layer 1) or
out-of-tree (layer 2).

ADR-0008 — The FOB and the green zone: the build-vs-reuse doctrine. Names
the boundary criterion implicit across ADRs 0002-0007 as one mechanical rule
every contributor (human or agent) applies the same way.

  - FOB (build in Rust) — admitted only if it passes one of: hot path,
    security-constitutive, differentiating. Current FOB: media termination
    (rutster-media on str0m) · real-time reflexes (VAD/barge-in/jitter/pacing)
    · call model (rutster-call-model) · agent tap (rutster-tap) · spend/abuse
    gate (rutster-spend) · control API + state/bus trait (rutster).
  - Green zone (reuse at arm's length) — its own process/container/trust
    domain, never in the FOB's address space. Admitted when it fails all
    three FOB tests AND a trusted, actively-maintained project already does
    it well. Current green zone: Valkey (ADR-0005) · carrier trunk (ADR-0007)
    · agent brain · reference GUI · object storage · KMS · OTel collector ·
    container supervisor.
  - 'Actively maintained' is the load-bearing gate: excludes a first-party
    Rust SIP stack and webrtc-rs; admits Valkey and str0m-for-WebRTC.
  - When in doubt, default to green zone. The FOB earns its members.

Restates the memory-safety pillar precisely: the FOB is 100% memory-safe
Rust; the green zone is trusted battle-tested OSS kept outside the boundary
— not an over-claim that every byte is Rust.

ADR-0003's status is flipped to Superseded with a forward-pointer to ADR-0007.
Kept as the historical record of the 'own trunk SIP + media termination in
Rust, no SBC shield' plan; reversed under the strategic-relevance review.
This commit is contained in:
opencode controller
2026-06-29 20:26:24 -04:00
parent 5a193871d8
commit 272aa07acd
3 changed files with 269 additions and 1 deletions

View File

@@ -0,0 +1,122 @@
# ADR-0008 — The FOB and the green zone: the build-vs-reuse doctrine
- **Status:** Accepted
- **Date:** 2026-06
- **Origin:** 2026-06 strategic-relevance review — names the boundary doctrine implicit across ADRs
00020007 and makes it a mechanical rule every contributor (human or agent) applies the same way.
- **Amends:** `docs/ARCHITECTURE.md` (adds the organizing metaphor), `AGENTS.md` (adds it to "key decisions")
- **Related:** [ADR-0002](0002-north-star-and-fused-core.md) (the boundary this rule governs),
[ADR-0007](0007-trunk-rented-transport.md) (a green-zone call), [ADR-0005](0005-event-bus.md) (a
green-zone call), [ADR-0006](0006-ingress-posture.md) (arm's-length out-of-tree adapters)
## Context
Across ADRs 00020007 one criterion kept deciding calls without ever being named: **what does rutster
build and own, and what does it reuse?** Fused vertical (0002), rent the trunk (0007), bundle Valkey
(0005), the out-of-tree SBC (0006) — all the same judgment, applied case by case. A solo build cannot
re-derive that judgment per decision, and multiple coding agents now work this repo and need one
explicit, mechanical rule so they classify consistently.
The failure mode this prevents is **not-invented-here**: rebuilding mature, actively-maintained OSS
inside the core and drowning the scarce solo-years in undifferentiated plumbing. The from-scratch Rust
trunk-SIP stack (superseded by [ADR-0007](0007-trunk-rented-transport.md)) was exactly that mistake.
Stated as one image: **stop building a fortress; build a secure base of operations.** Harden and own
the core — the **FOB** (forward operating base). Operate through a trusted, actively-maintained OSS
perimeter — the **green zone**. Leverage, not autarky.
## Decision
Every capability is classified **FOB** or **green zone**.
### FOB — build it, own it, in Rust
Memory-safe Rust, owned, fuzzed, **inside the trust boundary** — the surface you would certify
(PCI / HIPAA / TCPA). A capability is admitted to the FOB **only if it passes at least one** of three
tests:
1. **Hot path** — it runs on the per-call real-time loop, where a GC pause / jitter / an extra network
hop would degrade call quality. *(media termination, jitter buffer, pacing, the reflexes.)*
2. **Security-constitutive** — it *is* the auditable boundary: the spend/abuse gate, the trust-domain
edge, the audit surface. A 3-vendor chain structurally cannot enforce it. *(spend gate, the boundary
itself.)*
3. **Differentiating** — it is the thing no competitor ships and a reason rutster exists. *(the reflex
loop, the agent tap, the contact-center domain logic.)*
The FOB **may link mature OSS for internals** — libopus (codec), `str0m` (sans-IO WebRTC), speexdsp
(DSP) — as **trusted vendored dependencies**. Linking a maintained library to *implement* an FOB
capability is not a violation: you don't rewrite a codec. What makes the capability FOB is that rutster
owns it, it passes an FOB test, and the wire-facing parser is memory-safe Rust. Any vendored dep that
parses hostile bytes is fuzzed.
**Current FOB:** media termination (`rutster-media`, on `str0m`) · real-time reflexes (VAD/barge-in,
jitter, pacing) · call model (`rutster-call-model`) · the agent tap (`rutster-tap`) · the spend/abuse
gate (`rutster-spend`) · the control API + the state/bus **trait** (`rutster`).
### Green zone — reuse it, at arm's length
Trusted, **actively-maintained** OSS or an external managed service, **reused at arm's length** — its
own process, container, or trust domain, **never in the FOB's address space**, so its failures cannot
corrupt the core. A capability belongs here if it **fails all three FOB tests** and a trusted,
maintained project already does it well.
**Current green zone:** Valkey (state store + bus, [ADR-0005](0005-event-bus.md) — bundled in the
all-in-one image, external in the modular layout) · the carrier trunk (CPaaS raw-media fork /
out-of-tree SBC, [ADR-0007](0007-trunk-rented-transport.md)) · the agent brain (OpenAI Realtime or a
self-hosted STT/LLM/TTS) · the reference GUI (admin portal + call-flow builder — a pure API client) ·
object storage (recording/CDR durability) · KMS/Vault (secrets) · the OTel collector · the container
supervisor (tini/s6).
### The "actively maintained" gate is load-bearing
A green-zone dependency the FOB relies on **must** be healthy: maintained, released, production-grade,
not bus-factor-1. This single gate is what:
- **excludes** a first-party Rust SIP stack (`ezk`, bus-factor-1 / pre-1.0 — [ADR-0007](0007-trunk-rented-transport.md))
and `webrtc-rs` (perpetual re-architecture);
- **admits** Valkey (healthy), and `str0m` **for WebRTC** (its maintained sweet spot) — while rejecting
`str0m` for SIP (out of scope there, "not for production").
Re-audit periodically — a project can go cold (e.g. FreeSWITCH's OSS line). A green-zone dep that stops
being maintained is a defect to resolve, not a fixture.
## How to apply (every contributor, human or agent)
Before building a subsystem or adding a dependency, run the test:
1. **Does it pass an FOB test?** (hot-path **or** security-constitutive **or** differentiating)
- **No** → green zone. Find the trusted, actively-maintained OSS/service and integrate it **at arm's
length** (separate process / container / trust domain). **Do not rebuild it in-core.**
- **Yes** → build it in the FOB, in Rust. For *internals* a mature library already solves (codecs,
DSP), link that library as a trusted vendored dep — don't rewrite it. Fuzz any vendored parser that
touches hostile bytes.
2. **Is the OSS you're about to depend on actively maintained / production-grade?** If not, it is not
eligible for anything the FOB depends on — find another, or scope the need away.
When in doubt, default to **green zone**. The FOB earns its members; it does not collect them.
## Consequences
- **Positive:** one mechanical rule, applied identically by every human and agent; the solo-years
concentrate on the FOB (the only part that differentiates); the memory-safety claim becomes *precise*
rather than an over-claim; retroactively consistent with ADRs 00020007.
- **Negative:** the green zone is a dependency-trust surface that must be re-audited ("actively
maintained" can decay); arm's-length integration costs some glue versus pulling things in-process.
- **Restated pillar:** **memory-safe = the FOB** (100% Rust at the boundary that holds the call), with
mature OSS trusted at arm's length around it — *not* a claim that every byte in the deployment is Rust
(libopus, Valkey, the SBC are not).
## Worked example — deployment packaging
The all-in-one container is the doctrine made physical: the **FOB binary** + bundled `valkey-server`
(green zone) + the reference GUI (green zone), supervised in one container; point it at a brain + CPaaS,
`docker run`, taking calls. The modular/compose layout splits the same green-zone services out to scale.
Same FOB binary underneath; only the state/bus backend seam differs (bundled vs external). *(The
deployment topology itself may get its own ADR once that design closes.)*
## References
- [ADR-0002](0002-north-star-and-fused-core.md) — fused per-call vertical + composable platform (the boundary this rule governs)
- [ADR-0007](0007-trunk-rented-transport.md) — rent the trunk (a green-zone classification)
- [ADR-0005](0005-event-bus.md) — Valkey (a green-zone classification)
- [ADR-0006](0006-ingress-posture.md) — out-of-tree adapters (arm's-length integration)