Local IPC
The daemon's canonical wire protocol. Clients on the same host — the CLI, operator UIs, third-party tooling — communicate with the daemon over length-prefixed JSON on a Unix socket at $COVENANT_HOME/sock. The HTTP gateway is a thin adapter over the same surface.
Frame format
Each frame is a 4-byte big-endian unsigned integer length prefix followed by exactly that many bytes of UTF-8 JSON. Frames over 8 MiB are rejected at the read boundary.
+---------+---------+---------+---------+---------- … ----------+
| len[31..24] | len[23..16] | len[15..8] | len[7..0] | JSON payload |
+-------------+-------------+------------+-----------+--------------+
4-byte big-endian length up to 8 MiBThe framing applies in both directions: each request frame is followed by exactly one response frame, and a long-lived connection can carry many request/response pairs in sequence. Connections are not pooled by the daemon; clients may reuse a single connection or open one per request.
Request shapes
A request is a JSON object tagged with kind. The full set today:
{ "kind": "ping" }
{ "kind": "protocol_info" }
{ "kind": "authenticate",
"token_b58": "…" }
{ "kind": "submit_intent",
"text": "…" }
{ "kind": "recent_memory",
"tier": "working" | "episodic" | "longterm" | null,
"limit": 10 }
{ "kind": "search_memory",
"query": "…",
"tier": "working" | "episodic" | "longterm" | null,
"limit": 10 }
{ "kind": "purge_memory",
"tier": "working" | "episodic" | "longterm" | null,
"before_ms": 1714938000000 }
{ "kind": "recent_receipts",
"limit": 10 }
{ "kind": "recent_capabilities",
"limit": 10 }
{ "kind": "grant_capability",
"action": "tool.web_search",
"scope": null | { ... },
"expires_at": null | 1714938000000 }
{ "kind": "revoke_capability",
"signature_b58": "…" }
{ "kind": "verify",
"window": 100 }
{ "kind": "ignore_check",
"text": "…" }
{ "kind": "list_tools" }
{ "kind": "call_tool",
"name": "echo",
"arguments": { ... } }
{ "kind": "recent_audit",
"limit": 20 }
{ "kind": "verify_audit_integrity" }
{ "kind": "send_a2a_task", "task": { ... } }
{ "kind": "try_recv_a2a_task" }
{ "kind": "a2a_queue", "limit": 20 }
{ "kind": "post_a2a_result", "result": { ... } }
{ "kind": "try_recv_a2a_result" }Response shapes
Responses are also kind-tagged. One canonical response shape per request, plus a generic error for any handler-level failure.
{ "kind": "pong" }
{ "kind": "protocol_info",
"info": {
"protocol": "covenant.ipc",
"version": 1,
"min_supported": 1,
"max_supported": 1
} }
{ "kind": "authenticated",
"display": "operator@local" }
{ "kind": "authentication_failed",
"reason": "…" }
{ "kind": "intent_result",
"intent_id": "uuid",
"status": "ok" | "ignored",
"text": "…",
"sources": ["…"],
"settlement": null | { ... } }
{ "kind": "memories", "records": [ ... ] }
{ "kind": "memory_purged", "purged": 42 }
{ "kind": "receipts", "receipts": [ ... ] }
{ "kind": "capabilities", "capabilities": [ ... ] }
{ "kind": "capability_granted",
"signature_b58": "…",
"subject_display": "user@local",
"action": "tool.web_search" }
{ "kind": "capability_revoked",
"signature_b58": "…",
"removed": true }
{ "kind": "verify_report",
"window": 100,
"checks": [ { "name": "…", "passed": true, "message": "…" } ],
"drift": [ { "kind": "…", "id": "…", "message": "…", "repair": "…" } ],
"orphans_total": 0 }
{ "kind": "ignore_report",
"ignored": false,
"matched_pattern": null,
"rules_loaded": 3 }
{ "kind": "tool_list", "tools": [ ... ] }
{ "kind": "tool_result", "content": [ ... ], "is_error": false }
{ "kind": "audit_events", "events": [ ... ] }
{ "kind": "audit_integrity", "report": {
"events": 42,
"anchors": 42,
"valid": true,
"root_hash_hex": "…",
"failures": []
} }
{ "kind": "a2a_task_queued", "task_id": "uuid" }
{ "kind": "a2a_task_opt", "task": null | { ... } }
{ "kind": "a2a_result_posted", "task_id": "uuid" }
{ "kind": "a2a_result_opt", "result": null | { ... } }
{ "kind": "a2a_queue", "tasks": [ ... ], "results": [ ... ] }
{ "kind": "error", "message": "…" }Implementation notes
- Backpressure. The daemon reads one frame at a time per connection; long-running operations hold the connection open until completion, so a slow handler delays the next request on that connection.
- Frame size. The 8 MiB cap applies in both directions. A memory record set exceeding the cap is rare under normal operation, but a verification window over millions of records can reach it. Use the available
limitarguments to bound response sizes. - Timeouts. The daemon does not impose a per-request timeout. Clients are responsible for their own timeouts.
- Authentication. The first frame must be
authenticate, except for an optionalprotocol_infoprobe. The daemon answers protocol probes before authentication and then continues waiting forauthenticate. After authentication, the resolved identity is bound to the connection. - Compatibility.
protocol_infois intentionally minimal and treated as stable for protocol version 1. Clients should ignore unknown fields; adding new required fields implies a protocol version bump.
Reference implementation
The covenant-ipc Rust crate provides read_frame and write_frame helpers alongside the Request and Response enums. Refer to the CLI for an end-to-end example.
Related
- HTTP API — the same surface for clients that prefer JSON over HTTP.
- Security model — what the socket-as-credential design costs you.