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 MiB

The 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 limit arguments 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 optional protocol_info probe. The daemon answers protocol probes before authentication and then continues waiting forauthenticate. After authentication, the resolved identity is bound to the connection.
  • Compatibility. protocol_info is 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.