PayCargo · AI-Sandbox · Process

6.bWebSocket pattern — server push for vibe apps

Specced doctrine for real-time bidirectional channels. Captured before the first consumer demands the capability; built only when one does. Defines the SDK surface, contracts, audit + cost model, and the substrate primitives so the design is ready when the use case shows up.
Block: 6.b (Console doctrine extension) · State: Specced, no code · First consumer: TBD (live system alerts; multi-user collaboration; long-running agent progress)
Specced

The design lives here until a real vibe app demands the capability. Engineering team builds the SDK package + substrate API Gateway WebSocket API at that moment, against the spec already agreed.

1. When WebSockets are the right tool

NeedWebSocket?Alternative
Streaming AI response (chatbot)NoLambda Function URL Response Streaming (already in Step 6.a)
Cron / scheduled updates (every minute or slower)NoClient 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 updatesMaybeSSE over Lambda Function URL streaming (simpler, one-way)

2. The substrate primitive

Shared platform resource (one per AWS region):

Per-app: a Lambda function that handles the vibe-app's WebSocket actions (similar shape to the HTTP route handler).

3. SDK surface — vibe-scripter facing

Browser side

// 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.

Server side

// 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.

4. Contracts that carry over

ContractHow it applies to WebSockets
governance_cellWebSocket connection table encrypted with the substrate's audit CMK; vibe app's outbound messages tagged with the app's governance cell
three_layer_defenseAPI 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_piiPII detector runs on every incoming + outgoing WebSocket payload; lockbox helpers used for any PII reveal
audit_emit_requiredEvery $connect, $disconnect, $default, and broadcast emits a structured audit event
cost_attributePer-connection-minute and per-message costs tagged with app + cell + purpose

5. Authentication invariants

6. Cost model

7. When this gets built

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.

8. Doctrine

Step 6.b doctrine: "Spec ahead of demand, build at demand."

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.