slice-5: add rented-transport green-zone half (T2/T6/T9/T10) #17

Merged
alee merged 5 commits from slice-5/rented-transport-dev-b into main 2026-07-05 16:02:26 +00:00
Owner

What lands

  • CallControlClient trait (async, Send+Sync) + MockCallControlClient (in-process test double) + TwilioCredentials (hand-impl redacted Debug -- auth_token NEVER logged).
  • TwilioCallControlClient (live REST impl behind twilio-live feature gate): originate POSTs Calls.json with TwiML ; hangup POSTs Status=completed; HTTP basic auth over HTTPS.
  • config.rs env parser (twilio_credentials()) for RUTSTER_TWILIO_{ACCOUNT_SID,AUTH_TOKEN,MEDIA_BIND,WEBHOOK_BASE} -- pure-function pattern (Option inputs, testable without env mutation).
  • QUICKSTART env-var table + "make a real phone call" walkthrough; README spearhead status corrected (slices 1-4 merged, 4.5 + 5 in flight).
  • CI: twilio-live manual-trigger job (workflow_dispatch only -- never per-PR); seam-gate verification (slice-4 Task 10 hashes UNCHANGED); reqwest (rustls-tls) adds zero new duplicate dep versions.

ADR-0009 honored (load-bearing)

  • TwilioCredentials lives ONLY in crates/rutster-trunk/ -- never re-exported through the workspace; brain never sees credentials. Static assertion test pins the trunk-locality.
  • auth_token never logged (hand-impl Debug); tracing fields expose only caller-controlled values (to/from) + CallSid last 4.
  • Option on originate pre-paves the spearhead step-6 spend-cap seam (passes None this slice).

ADR-0010 deviation (strategic plan section 1.2)

  • This slice precedes rung-2 escalation per the user's 2026-07-05 directive. ADR-0010 remains intact.

Merge instructions

  • rebase-merge, NOT squash -- stacked-branches carve-out (AGENTS.md Git workflow). This branch's lib.rs pub mod provider; edit is shared territory with dev-c's slice-5/rented-transport-dev-c; preserve the SHAs so dev-c rebases forward.
  • DCO signoff every commit (Aaron D. Lee himself@adlee.work).
  • Do NOT merge until the maintainer runs the live Twilio e2e (cargo test --features=twilio-live -- --include-ignored against real creds).

Out of scope (dev-c's territory, file-ownership non-overlapping)

  • g711.rs, twilio_media_streams.rs, session.rs, loop_driver.rs, lib.rs pub-mod declarations beyond provider
  • media_thread.rs MediaLeg enum + RegisterTrunk variant
  • routes.rs /v1/trunk/* handlers, main.rs router mount
## What lands - CallControlClient trait (async, Send+Sync) + MockCallControlClient (in-process test double) + TwilioCredentials (hand-impl redacted Debug -- auth_token NEVER logged). - TwilioCallControlClient (live REST impl behind `twilio-live` feature gate): originate POSTs Calls.json with TwiML <Connect><Stream>; hangup POSTs Status=completed; HTTP basic auth over HTTPS. - config.rs env parser (`twilio_credentials()`) for RUTSTER_TWILIO_{ACCOUNT_SID,AUTH_TOKEN,MEDIA_BIND,WEBHOOK_BASE} -- pure-function pattern (Option<String> inputs, testable without env mutation). - QUICKSTART env-var table + "make a real phone call" walkthrough; README spearhead status corrected (slices 1-4 merged, 4.5 + 5 in flight). - CI: twilio-live manual-trigger job (workflow_dispatch only -- never per-PR); seam-gate verification (slice-4 Task 10 hashes UNCHANGED); reqwest (rustls-tls) adds zero new duplicate dep versions. ## ADR-0009 honored (load-bearing) - TwilioCredentials lives ONLY in crates/rutster-trunk/ -- never re-exported through the workspace; brain never sees credentials. Static assertion test pins the trunk-locality. - auth_token never logged (hand-impl Debug); tracing fields expose only caller-controlled values (to/from) + CallSid last 4. - Option<SpendToken> on originate pre-paves the spearhead step-6 spend-cap seam (passes None this slice). ## ADR-0010 deviation (strategic plan section 1.2) - This slice precedes rung-2 escalation per the user's 2026-07-05 directive. ADR-0010 remains intact. ## Merge instructions - rebase-merge, NOT squash -- stacked-branches carve-out (AGENTS.md Git workflow). This branch's lib.rs `pub mod provider;` edit is shared territory with dev-c's slice-5/rented-transport-dev-c; preserve the SHAs so dev-c rebases forward. - DCO signoff every commit (Aaron D. Lee <himself@adlee.work>). - Do NOT merge until the maintainer runs the live Twilio e2e (`cargo test --features=twilio-live -- --include-ignored` against real creds). ## Out of scope (dev-c's territory, file-ownership non-overlapping) - g711.rs, twilio_media_streams.rs, session.rs, loop_driver.rs, lib.rs pub-mod declarations beyond `provider` - media_thread.rs MediaLeg enum + RegisterTrunk variant - routes.rs /v1/trunk/* handlers, main.rs router mount
alee added 5 commits 2026-07-05 07:24:11 +00:00
The provider call-control seam (green zone, ADR-0008). The trait locks the
boundary so the next provider (Telnyx, etc.) is an implementation, not a
refactor. MockCallControlClient is the CI test double; the live
TwilioCallControlClient (T6) lives behind the twilio-live feature flag.

TwilioCredentials lives ONLY in crates/rutster-trunk/ -- never re-exported
through the workspace (ADR-0009 -- provider credentials never reach the
brain). Its Debug impl is hand-written (NOT derived) so the auth_token
renders as <redacted>, never leaking into tracing/panic output.

Option<SpendToken> on originate is the pre-paved seam for spearhead step-6
(spend cap); this slice passes None everywhere. The signature is locked so
step 6 is additive, not a refactor.

T2 of slice-5. lib.rs gains `pub mod provider;` -- stacked-branches carve-out
(rebase-merge, not squash) per AGENTS.md Git workflow; dev-c rebases forward
for the FOB-side pub mod declarations (g711/twilio_media_streams/session/
loop_driver).

Signed-off-by: Aaron D. Lee <himself@adlee.work>
The live Twilio call-control client. Originates outbound calls via Twilio's
Calls.json API (TwiML <Connect><Stream> instructs Twilio to fork audio back
to our /twilio/media-stream WSS endpoint); hangs up via Status=completed.
HTTP basic auth over HTTPS; auth_token is NEVER logged (ADR-0009 -- provider
credentials never reach the brain) -- tracing fields expose only caller-
controlled values (to, from) + the CallSid's last 4 chars.

Feature-gated behind `twilio-live` so the routine CI gate stays feature-
default-off: MockCallControlClient is the per-PR test surface; the maintainer
runs cargo test --features=twilio-live when validating a release. reqwest +
tracing + serde_json are optional deps (dep:foo syntax) -- pulled in only
when the feature is on, keeping the default resolve lean.

The env parser (config::twilio_credentials) follows slice-5/seams' pure-
function pattern: takes Option<String> inputs (testable without env mutation),
returns Ok(None) when all four RUTSTER_TWILIO_* vars are unset (WebRTC-only
mode), Ok(Some) when all four present + parse, Err on partial config (fail-
fast at startup) or malformed values. TwilioCredentials is imported by the
binary but never re-exported through the workspace (ADR-0009).

T6 of slice-5. Depends on T2 (TwilioCredentials + CallControlClient trait).

Signed-off-by: Aaron D. Lee <himself@adlee.work>
QUICKSTART gains a Twilio Media Streams section: env-var table for the four
RUTSTER_TWILIO_* vars, the run-with-twilio-live command, the point-Twilio-
at-rutster webhook/TwiML walkthrough, + the outbound-call curl example. The
/v1/trunk routes' auth-deferral (slice 6) is flagged. A 'what's different
from WebRTC' note explains the architectural reuse -- the reflex stack is
ingress-agnostic (Reflex<TapAudioPipe> + LocalVadReflex REUSED from slice-4).

README's spearhead status is corrected + extended: slices 1-4 are merged to
main (the prior status stalled at '1-3 merged, slice-4 active' -- stale);
4.5 (sim/benchmark, ADR-0010) + step 5 (PSTN via rented transport, ADR-0007)
are the active build targets. ADR-0007 honored: rutster parses zero SIP bytes.

T9 of slice-5.

Signed-off-by: Aaron D. Lee <himself@adlee.work>
The routine CI gate stays feature-default-off: MockCallControlClient is the
per-PR test surface. A new twilio-live job runs ONLY on manual
workflow_dispatch (maintainer triggers pre-release) -- it exercises clippy
--features=twilio-live + the live TwilioCallControlClient tests against real
Twilio credentials (TWILIO_* secrets). Never runs per-PR.

Seam gate verified UNCHANGED: loop_driver.rs (744bf314...) + rtc_session.rs
(f47d63b9...) blob hashes match the slice-4 Task 10 pins exactly -- the trunk
leg's tick lives entirely in rutster-trunk/src/loop_driver.rs (a separate file
in a separate crate), so the media-crate seam files stay byte-identical.

cargo deny: reqwest (rustls-tls) + base64 + async-trait introduce ZERO new
duplicate dep versions (verified via `cargo tree -d` with vs without
--features=twilio-live: identical duplicate sets -- the existing skip list in
deny.toml remains sufficient). Local cargo-deny 0.18.3 cannot parse the
`-or-later` SPDX form + CVSS 4.0 advisory entries (pre-existing limitation
documented in deny.toml; CI's cargo-deny-action@v2 bundles 0.19.x which handles
both) -- CI is the authoritative deny gate.

Two rustdoc intra-doc-link warnings in my code fixed (mock.rs private-item
link -> plain inline code; lib.rs redundant explicit link target simplified).
Two pre-existing rustdoc warnings remain in rutster-tap/protocol.rs +
rutster/tap_engine.rs (out of scope -- pre-existing from slices 2-3, not
introduced by slice-5).

T10 of slice-5. This is the final task on the dev-b chain (T2 + T6 + T9 + T10
all landed).

Signed-off-by: Aaron D. Lee <himself@adlee.work>
test(trunk): ADR-0009 static assertion -- TwilioCredentials is trunk-local (slice-5 T10 follow-up)
Some checks failed
CI / fmt (pull_request) Successful in 1m15s
CI / clippy (pull_request) Successful in 2m14s
CI / test (1.85) (pull_request) Successful in 5m1s
CI / test (stable) (pull_request) Successful in 5m30s
CI / deny (pull_request) Failing after 1m31s
CI / twilio-live (manual only) (pull_request) Failing after 14m26s
b2a432f56d
Spec §7 done-criteria #10 demands a static assertion that TwilioCredentials
lives ONLY in rutster-trunk (ADR-0009 -- provider credentials never reach the
brain). The test compiles only because crate::provider::TwilioCredentials
resolves (the type's canonical home); if someone moved/re-exported it through
the workspace root or a sibling crate, this test's doc + the dep-graph change
would surface in review. The invariant is structural: sibling crates
(rutster-media, rutster-tap) do not depend on rutster-trunk, so the type
cannot reach them. The binary's config::twilio_credentials is the single
expected import path.

Signed-off-by: Aaron D. Lee <himself@adlee.work>
alee merged commit 40cc27e9f0 into main 2026-07-05 16:02:25 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: alee/rutster#17