| Need | WebSocket? | Alternative |
|---|---|---|
| Streaming AI response (chatbot) | No | Lambda Function URL Response Streaming (already in Step 6.a) |
| Cron / scheduled updates (every minute or slower) | No | Client polling with cache headers |
| Server → browser push of system alerts (sub-second) | Yes | — |
| Multi-user collaboration (shared cursors, presence) | Yes | — |
| Long-running agent progress (per-step status pushes) | Yes | — |
| Live dashboards with high-frequency tile updates | Maybe | SSE over Lambda Function URL streaming (simpler, one-way) |
Shared platform resource (one per AWS region):
API Gateway WebSocket API at wss://realtime.<platform-domain>$connect route: authorizer Lambda validates the JWT (X-Auth-Token, intra-platform SSO from §13); stores connection metadata in DDB$disconnect route: removes the connection metadata$default route: dispatches by message envelope's app + action fields to the right vibe-app Lambdaai-sandbox-ws-connections: connectionId (PK), userId, cognitoGroups, connectedAt, lastSeen, appsActive (list of vibe apps subscribed to)Per-app: a Lambda function that handles the vibe-app's WebSocket actions (similar shape to the HTTP route handler).
// vibecode/sdk/v1/spa/socket.js
import { connect } from '/sdk/v1/spa/socket.js';
const socket = connect();
socket.on('alert', (event) => { /* render alert */ });
socket.on('thread.updated', (event) => { /* refresh thread */ });
socket.emit('chat.typing', { threadId });
// Auto-reconnect, exponential backoff, ping/pong heartbeat all internal.
// vibecode/sdk/v1/lambda/socket.ts
import { broadcast } from 'vibecode-sdk';
// From any Lambda handler — vibe scripter writes:
await broadcast({
users: ['userId-abc', 'userId-def'],
payload: { type: 'alert', severity: 'high', message: '...' },
});
// Or by group:
await broadcast({
groups: ['admins'],
payload: { type: 'alert', severity: 'high', message: '...' },
});
Internal: the broadcast helper looks up active connections from the DDB connections table, dispatches via API Gateway Management API, handles per-message retries and DLQ for permanently-disconnected receivers.
| Contract | How it applies to WebSockets |
|---|---|
governance_cell | WebSocket connection table encrypted with the substrate's audit CMK; vibe app's outbound messages tagged with the app's governance cell |
three_layer_defense | API Gateway uses Cognito authorizer at $connect (org SCP allows API Gateway with JWT-based auth); per-message $default routes still validate identity from connection metadata |
lockbox_only_pii | PII detector runs on every incoming + outgoing WebSocket payload; lockbox helpers used for any PII reveal |
audit_emit_required | Every $connect, $disconnect, $default, and broadcast emits a structured audit event |
cost_attribute | Per-connection-minute and per-message costs tagged with app + cell + purpose |
$connect only (subsequent messages inherit the connection's identity from the DDB lookup)Engineering team starts construction when the first vibe app proposes a use case that requires bidirectional or sub-second server push. The most likely first consumer is one of:
The spec above survives until that moment. The build is ~1 day of engineering work against this spec.
WebSockets are a substantive substrate addition. Building them without a real consumer means engineering against assumptions. Specifying them ahead of demand lets the doctrine survive the engineering team's eventual review when the use case lands. The spec is the design; the consumer is the validator. When both exist, the build is straightforward.