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>
51 lines
2.7 KiB
TOML
51 lines
2.7 KiB
TOML
# crates/rutster-trunk/Cargo.toml
|
|
[package]
|
|
name = "rutster-trunk"
|
|
version = "0.0.0"
|
|
license.workspace = true
|
|
edition.workspace = true
|
|
repository.workspace = true
|
|
description = "Rented carrier transport — CPaaS media-leg ingress; no first-party SIP (spearhead step 5, ADR-0007)."
|
|
|
|
[dependencies]
|
|
# async-trait: lets us declare `async fn` in the CallControlClient trait while
|
|
# still using it as a trait object (`Box<dyn CallControlClient>`) in the route
|
|
# handlers. Native async-fn-in-traits (stable since 1.75) does NOT support
|
|
# `dyn` dispatch without manual desugaring — async_trait rewrites the signature
|
|
# to `fn -> Pin<Box<dyn Future + Send>>` for us (spec §3.4).
|
|
async-trait = { workspace = true }
|
|
# url: the `TwilioCredentials::webhook_base` field is a `url::Url` (the
|
|
# operator's public base URL Twilio calls back). Parsed in `config::twilio_credentials`.
|
|
url = { workspace = true }
|
|
# reqwest + tracing + serde_json are OPTIONAL — only pulled in when the
|
|
# `twilio-live` feature is enabled (the live `TwilioCallControlClient`, T6).
|
|
# This keeps the default CI build (default-features-off) free of reqwest's
|
|
# transitive dep tree, so per-PR `cargo deny check` stays lean + cargo's
|
|
# resolve is fast for the common case. The maintainer's manual
|
|
# `cargo test --features=twilio-live` + the twilio-live CI job (T10) pull them in.
|
|
# `serde_json` parses the Calls.json REST response body (the `sid` field).
|
|
reqwest = { workspace = true, optional = true }
|
|
tracing = { workspace = true, optional = true }
|
|
serde_json = { workspace = true, optional = true }
|
|
|
|
[dev-dependencies]
|
|
# tokio: only the dev-dep is needed for the mock's `#[tokio::test]` attribute
|
|
# (`#[tokio::test]` expands to a `#[test]` that spins up a current-thread
|
|
# runtime + drives the async fn to completion). The library itself is
|
|
# runtime-agnostic: async_trait's desugared futures poll on whatever runtime
|
|
# the caller drives them with — the mock's bodies are synchronous (lock + push,
|
|
# no `.await`), so no tokio dependency leaks into the library's public surface.
|
|
tokio = { workspace = true }
|
|
|
|
[features]
|
|
default = []
|
|
# The live `TwilioCallControlClient` (T6) is 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`
|
|
# + the e2e suite only when validating a release (against real Twilio creds).
|
|
# Spec §1.2 + plan T6 + ADR-0009 (credentials never reach the brain).
|
|
# `dep:reqwest` + `dep:tracing` are the optional-dep enable syntax
|
|
# (`feature = "dep:foo"` enables `foo` without exposing it as an implicit
|
|
# feature named `foo` — the explicit form is forward-compatible + unambiguous).
|
|
twilio-live = ["dep:reqwest", "dep:tracing", "dep:serde_json"]
|