Step 14
Step 14 — Close gRPC and WebRTC boundaries with cost guards
0 views
Table of contents
Step 14 — Close gRPC and WebRTC boundaries with cost guards
Real-time code is not production-ready merely because a connection succeeds. Internal calls need contracts, deadlines, and fallback. Voice and video also need a product contract for browser permissions, NAT traversal, relay cost, and session lifetime.
The boundary applied to DMDDKSL
Keep gRPC at the service boundary
Java dmddksl-api and the Python voice engine share proto/voice/v1/voice.proto. Synthesize, ListVoices, and GetCapabilities use explicit messages and request_id; the Java client applies a deadline and a bounded executor. If gRPC is unavailable, the client explicitly falls back to the existing REST TTS boundary, so the public HTTP API, database, and SSE text room remain unchanged.
When adding protobuf fields, never reuse an old field number. Use optional, default-safe, and compatible responses so a server can ignore fields it does not know. Timeouts converge to safe errorType values without logging provider bodies or user text.
WebRTC is a temporary voice-review path
The room source of truth is PostgreSQL-backed text messages and SSE notifications. WebSocket carries only room admission and offer, answer, and ICE signaling. Media uses a peer-to-peer audio track. SDP, ICE, and anonymous user IDs are not stored in PostgreSQL, Redis, logs, or analytics.
The current topology is a maximum-eight-participant p2p-mesh voice room. Video tracks are unsupported, and SFU, MCU, and a coturn container are not part of the default deployment. The participant ID is ephemeral and room-scoped; the existing anonymous identity is only validated during the handshake.
The actual cost contract
WEBRTC_VOICE_ENABLED=falseis the default, so microphone permission, WebSocket signaling, and ICE gathering do not start.WEBRTC_ICE_SERVERSand the TURN URL, username, and credential are emitted only when explicitly configured together. The default has no STUN or TURN endpoint.- The API reports
videoEnabled=false; enabling voice cannot open video traffic. - Rooms are capped at eight participants and sessions at 30 minutes by default. Both server and client enforce expiry, signaling rate, and payload limits.
- Media does not traverse Caddy or the Java API. Adding TURN requires prior approval for fixed cost, relay GB, and peak concurrent sessions, followed by relay-ratio measurement and an opt-in rollout.
- Text remains the usable fallback. A voice failure must not delete or block the persisted text room.
Default gate and approved opt-in
keep voice, ICE, TURN, and video closed while text remains available.
confirm fixed cost, relay GB, concurrent-session budget, and measurement first.
close voice and return to text when relay ratio, errors, or session caps exceed the contract.
Acceptance evidence from five perspectives
| Perspective | Evidence |
|---|---|
| Product | Purpose, session cap, cost owner, and unsupported video state are explicit |
| Design | The lock reason, pre-permission state, connection failure, and text fallback are understandable |
| Engineering | Protobuf compatibility, deadlines, signaling auth/rate limits, no SDP persistence, and TTL tests exist |
| User | Text review and recording continue when the cost guard is on or voice fails |
| Operations | gRPC health/capability, WebRTC enabled/TURN state, relay traffic, and rollback criteria are inspectable |
Completion means more than “the browser connected.” The gRPC contract and fallback tests must pass, the WebRTC cost-guard test must prove the default is closed, existing text-room DB/API/frontend smoke must pass, and an environment without cost approval must contain no TURN or video configuration.
Related reading: The gRPC voice boundary, Cost-gated WebRTC voice review
🎉 You finished Production Engineering — Boundaries, Performance, Recovery, and Delivery in 14 Steps
What's next? Pick another course below.