Skip to content
Pagr

Reference

Protocol commands & events

Version 1 of the device protocol: nine command types the cloud may send, eleven event types the bridge sends back, and the exact limits of each. If this page and the schema ever disagree, the schema wins.

Canonical source: packages/protocol/src/schemas.ts in the public bridge repository. The private control plane vendors a copy and has a test that fails if the two drift.

Identifiers#

Every id is a prefix, an underscore, and 32 lowercase hex characters. They are opaque. The cloud never sends a filesystem path, a phone number, or a provider-native id as an identifier.

usr_  dev_  proj_  ses_  cmd_  apr_  att_

The command envelope#

Every command arrives wrapped in an envelope carrying the signature and the id of the key that produced it.

command envelope
{  "keyId": "k2026-08",  "signature": "<base64url Ed25519 over canonicalize(body)>",  "body": {    "version": 1,    "commandId": "cmd_…",    "userId": "usr_…",    "deviceId": "dev_…",    "issuedAt": "2026-08-24T12:00:00.000Z",    "expiresAt": "2026-08-24T12:05:00.000Z",    "nonce": "…at least 16 characters…",    "idempotencyKey": "…at least 8 characters…",    "type": "agent.start_session",    "payload": { }  }}

canonicalize is JSON.stringify with recursively sorted keys, no whitespace and undefined dropped. The verification order the bridge applies is in how the bridge works.

The nine commands#

Cloud to bridge commands
TypeWhat it doesWhat it cannot do
device.probeAsks for a fresh picture: agents, projects, sessions. Returns a device.hello payload.Change anything. It is read-only.
project.listReturns the project summaries.Reveal a filesystem path. Summaries never contain one.
project.removeForgets a project id.Delete files. It only removes a registry entry.
agent.start_sessionStarts Claude Code or Codex in a project you registered locally, with an instruction of up to 8000 characters, up to four image attachments, and an optional read-only flag. The cloud pre-allocates the session id.Name a directory. It sends a proj_… id, which only resolves against your local registry — so an unregistered folder is unreachable.
agent.send_instructionSends follow-up text to an existing session in auto, steer or queue mode, with up to four attachments.Force a steer where the adapter cannot do it. auto falls back to queueing and emits a queued_followup event.
agent.stop_sessionInterrupts a session.Undo work already written to disk.
agent.get_statusReturns session summaries, optionally for one session.Return transcripts, diffs or file contents. Summaries are capped and structured.
agent.respond_to_approvalAnswers a permission prompt the agent raised — carrying the approval id, session id, the provider's request id, the preview hash and the decision.Answer a different request than the one you saw. All four must match the retained request, and entries are single-use.
settings.sync_public_policyUpdates the Tier A auto-approve flag and the approval timeout (30–3600 seconds).Auto-approve Tier B or C. The flag applies to Tier A and nothing else.

Events#

Every event carries { version, eventId, deviceId, at, inReplyTo? } plus its type and payload.

Bridge to cloud events
TypeWhenPayload
device.helloAfter each successful authenticationBridge and macOS version, agent status, project summaries, session summaries
device.heartbeatEvery 20 secondsThe number of active sessions
command.ackExactly once per received commandCommand id, status, optional error code, message and result
project.registered / project.removedA local CLI change, or a project.remove commandThe project summary, or the id
agent.connectionAn adapter's status changesProvider, mode, install and auth state, capabilities
session.updatedA session is created or changes stateThe session summary
session.eventProgress, messages, completion — kinds include started, progress, agent_message, needs_input, completed, failed, stopped, queued_followup, followup_deliveredA summary of at most 2000 characters — never a full transcript or diff
approval.requestedAn agent asked permissionAction type, a preview of at most 1500 characters, its hash, the deterministic hints, and an expiry
approval.resolved_locallyTimeout, an answer given in the terminal, or shutdownallowed, denied, timed_out or canceled
attachment.consumedAfter a download attemptThe attachment id and whether it worked — never image bytes

Acks and error codes#

Every command gets exactly one ack, with one of five statuses:

Ack statuses
StatusMeaning
accepted / completedTaken on, and finished.
rejectedThe guard refused it before dispatch.
failedDispatch ran, and the adapter or a precondition failed.
duplicateAn idempotent retry returned the original result.

The error codes are a closed set too:

bad_signature   expired        replayedwrong_device    unknown_project unknown_sessioncapability_unsupported  provider_error  invalid_payload

Frames#

The WebSocket carries JSON objects discriminated by kind. From the bridge: auth.request, auth.response, event, pong. From the gateway: auth.challenge, auth.result, command, ping.

auth.result may carry serverKeys, which the bridge pins and uses to verify commands, and minBridgeVersion — if the running bridge is older it stops reconnecting and logs an update-required error.