Multiplex Wire Protocol
The fabric's transport contract: one long-lived WSS connection per paired device into the cloud at /ws/device, NDJSON envelopes both directions, routed by sessionId + corrId. Implementation in src/fabric/envelope.ts, src/server/ws-multiplex.ts, src/server/device-router.ts, src/daemon/transport.ts, src/daemon/envelope.ts.
Envelope structure
interface Envelope<P = unknown> {
type: EnvelopeType;
sessionId: SessionId;
corrId: CorrId;
ts: number;
payload: P;
}Decoding rejects any frame missing required fields or with type outside KNOWN_TYPES set in src/fabric/envelope.ts.
Envelope catalog (KNOWN_TYPES)
| Type | Direction | Purpose |
|---|---|---|
session.start | cloud → device | Spawn harness session |
session.stdout / session.stderr | device → cloud | Stream output |
session.exit | device → cloud | Session ended |
session.cancel | cloud → device | Cancel session |
session.rejected | device → cloud | Daemon refused |
session.status / session.status.reply | both | Probe |
heartbeat | device → cloud | Capabilities + telemetry every 20s |
ack | cloud → device | Per-envelope receipt |
error | either | Structured error |
tmux.list / tmux.list.reply | both | List tmux sessions |
tmux.attach.* | both | Live PTY relay |
files.list / files.list.reply | both | Cross-device XFER browse |
waystation.scan / waystation.scan.reply | both | Claude Code session telemetry |
openclaw.status / openclaw.status.reply | both | Gateway status |
hermes.status / hermes.status.reply | both | Hermes drill-down |
spine.run / spine.run.reply | both | ReturnPro spine relay |
spine.state / spine.state.reply | both | Spine state |
agent.event | device → cloud | Office map (agent activity) |
feed.items | device → cloud | Feed content |
Multiplex over WSS
Cloud-side WSMultiplex maintains:
- One device per
deviceId: new connection for existing device evicts old witherrorenvelope (corrId: "device-replaced") and closes (code 4040) - Many browsers: each tab gets stable
browserConnId
JWT presented via Sec-WebSocket-Protocol: fabric-jwt-<token>. At handshake, acceptDevice checks vault_recipients.revoked_at (T4).
Routing is corrId-keyed. Pending-request map stores {corrId, fromKind, fromId, toKind, toId, expiresAt}; TTL 30 minutes.
Heartbeats and capabilities
Device sends heartbeat every 20s with {capabilities, installedHarnesses, ramFreeMb, concurrentSessionCount}. absorbHeartbeat recomputes capabilities = new Set([...pairCapabilities, ...runtimeCapabilities]) — operator pair-time tags are authoritative; runtime daemon-detected tags are additive.
Connection table fields:
| Field | Source | Used by |
|---|---|---|
pairCapabilities | Pair-time row | Merged into capabilities |
runtimeCapabilities | Heartbeat | Merged |
installedHarnesses | Heartbeat | Scheduler filter |
ramFreeMb | Heartbeat | Scheduler score |
lastSeenAt | Activity | Score + eviction |
hostname | Heartbeat | Roster display |
tmux.list
requestTmuxList in src/server/device-router.ts:
- Build envelope
{type:"tmux.list", sessionId:"device-control", corrId:UUID, payload:{}, ts:now()} - Call
multiplex.dispatchToDevice()with response sink - Daemon executes
tmux list-sessions, parses, sendstmux.list.reply - Cloud returns
TmuxListResultto route layer
tmux.attach
Live PTY relay (8da8e3a):
| Frame | Direction | Payload |
|---|---|---|
tmux.attach.start | cloud → device | {session, cols, rows} |
tmux.attach.data | both | {data: base64} |
tmux.attach.resize | cloud → device | {attachId, cols, rows} |
tmux.attach.end | either | {reason?} |
LiveAttaches registry separate from ActiveSessions — different lifecycle (no exit code, no vault binding).
MOTHER bridge
MOTHER queries are session.start envelopes with harness: "mother-llm". mother-llm adapter has empty cred-allowlist; only HOME, PATH, OLLAMA_HOST, OLLAMA_MODELS, CUDA_VISIBLE_DEVICES inherited. Default model: qwen2.5-coder:7b.
Capability-aware routing (Phase 12-1)
pickDeviceForSpec in src/server/device-router.ts:149:
- Implied-capability injection: harnesses like
mother-llmimply same-named capability - Explicit target short-circuit:
spec.deviceTarget !== "auto"returns that device - Hard filter:
session.harness ∈ device.installedHarnessesANDrequiredCapabilities ⊆ device.capabilities - Soft score: weighted sum normalized [0,1]:
ramHeadroom(0.50)inverseLoad(0.30)heartbeatFreshness(0.20)
- Tie-break: lexicographic deviceId
Returns {ok: true, match: {deviceId, reason, conn, score, scoredCandidates}} on success.
Source
- Envelope serde:
src/fabric/envelope.ts - Cloud multiplex:
src/server/ws-multiplex.ts - Scheduler + bridges:
src/server/device-router.ts - Daemon transport:
src/daemon/transport.ts - Daemon envelope:
src/daemon/envelope.ts - Charter §7, §9:
~/.openclaw/workspace/optimalOS/docs/superpowers/specs/2026-05-03-fabric-charter.md