Skip to content

Protocol and observation

Developer docs

The wire contract lives in platonic-protocol and its exact-byte tests. This page groups that contract for navigation; it is not an API reference.

Protocol v2 is a closed, versioned, newline-delimited JSON protocol over local IPC. Each request and response is a typed Envelope; unknown methods, fields, and incompatible versions fail at the envelope boundary. A v2 daemon rejects a v1 envelope before method dispatch, and the v1 fixture rejects v2 the same way; there is no agent.* alias surface. The source owns serialization and validation but performs no I/O or policy. See the crate README, Envelope, and every_current_method_keeps_exact_v2_request_and_response_bytes.

On a host connection, workspace and profile registry calls plus daemon.shutdown_if_idle can be dispatched without selecting a workspace. Other calls must begin with hello. The server resolves the registered workspace and returns its stable identity, ledger path, daemon scope, build identity, and advertised capabilities. The server-side gate is handle_host_line; the transport call is DaemonClient::hello.

Capabilities report what this peer implements. They do not grant workspace, thread, tool, or approval authority; the server still enforces those checks in its handlers.

At this source head, ProtocolMethod contains 29 callable methods:

  • Handshake: hello
  • Run and event flow: run.start, message.append, issue-prep.start, events.stream, run.cancel
  • Voice facts: voice.events.commit, voice.events.read
  • Approval and session readback: approval.decide, sessions.list, transcript.read, session.approval_profile.set
  • Host control: daemon.status, daemon.shutdown_if_idle
  • Threads: thread.spawn, thread.list, thread.status, thread.authority, thread.send, thread.events, thread.stop
  • Workspace registry: workspace.create, workspace.list, workspace.status
  • Profiles: profile.create, profile.list, profile.status, profile.update, profile.open

The hello result advertises 31 capabilities. Twenty-nine correspond to callable methods. The two additional flags, transcript.read.typed and transcript.read.pending_approval, describe additive fields returned by the callable transcript.read; they are not methods. The exhaustive inventory is the typed Capability, CAPABILITIES, and ProtocolMethod source, locked by capability_names_and_error_codes_keep_exact_v2_literals.

Internal modules such as handlers/runs.rs or runtime/thread.rs are implementations behind those methods, not extra protocol surface. Add or change wire behavior in the typed protocol first, then route it through the server handler that owns the semantics.

profile.open resolves, starts, or decides the one home reservation for a profile. thread.spawn requires a parent and preserves that parent’s profile_id; a child can narrow authority but cannot cross profiles. Thread list and status project profile_id, thread_kind, the home relation, return availability, and live epoch. The model-facing profile.read, thread.tree, thread.events, and thread.transcript tools expose bounded same-profile readback. thread.return and thread.answer expose the already-owned immediate parent/child channel rather than a generic messaging system.

thread.send establishes one controller for an active turn. That controller may queue a bounded steer only with the matching turn id. A different controller receives controller_owned; after the turn becomes idle, another controller may start the next turn. The rule and rejection shapes live in runtime/thread.rs and are proven end to end by thread_send_and_three_observers_are_semantically_conformant_on_host_daemon.

thread.events is independently observable by multiple clients. Each observer advances its own offset through the same ordered, bounded live buffer and pairs it with live_epoch_id. Omitting an offset tails from the current position; an evicted offset returns lagged, while a cursor from an old server epoch must reset from current status or durable readback. Durable transcript readback, not a private observer cache, is the recovery path after lag or reconnect. See LiveThread::events and TranscriptReadResult.

A gateway is a server-owned connector module, but it reaches run authority through the same client and protocol boundary as any other peer. The Discord bridge connects, completes hello, checks required capabilities, calls thread.status, thread.send, thread.events, approval.decide, and transcript.read, and falls back to durable readback after lag or reconnect. It does not acquire sessions, policy, approvals, fallback, or run semantics. Read gateway/discord/daemon_bridge.rs and the gateway boundary.

The daemon endpoint is a user-owned Unix socket, with private parent directories and a 0600 socket. It is not a network listener. The implementation is in server/socket.rs, while the accepted no-network-listener boundary is decision P017.

The authenticated HTTP/SSE gateway is a translating /v2 adapter over this native boundary, not the native protocol exposed on a network socket. Its bearer principal supplies fixed workspace scope and may further narrow that ceiling with profile ids; route ids never widen either scope. The generated contract is gateway-v2.yaml. The merged /v1 gateway was never released, so v2 replaced it without a compatibility adapter. JSON-RPC, ACP, cookies, proxy-header identity, in-process TLS, and direct remote daemon access remain outside this boundary. Operational socket and gateway setup belongs to the User operations guide.