Coven local socket API
The Coven local HTTP API served over a Unix socket: health, capabilities, actions, sessions, events, and input forwarding under /api/v1.
Coven Local API
Last updated: 2026-05-09
Coven exposes a small HTTP API over the local Unix socket at <covenHome>/coven.sock. The Rust daemon is the authority boundary: clients may validate for UX, but the daemon still validates project roots, cwd, harness ids, session ids, input, and live-session state before acting.
Every route returns either a documented success shape or the structured error envelope. Unknown routes, unknown action ids, and unknown API versions all fail closed with invalid_request or not_found.
See Authentication and local access for the current auth posture. In short: the daemon API does not use OAuth, JWTs, bearer tokens, API keys, or cookies today. Access is local Unix-socket based, provider credentials stay with the harness CLIs, and any remote, browser, or TCP exposure needs a separate auth design.
Versioning
The current public API contract is the named coven.daemon.v1 contract served under the /api/v1 route prefix.
Versioned clients should use the /api/v1 prefix:
| Method | Path | Surface | Purpose |
|---|---|---|---|
| GET | /api/v1/api-version | Meta | Read the active API version and supported versions. |
| GET | /api/v1/health | Meta | Check daemon health and metadata. |
| GET | /api/v1/capabilities | Meta | Discover daemon / control-plane capabilities and policy hints. |
| POST | /api/v1/actions | Control | Route a policy-shaped control-plane action. |
| GET | /api/v1/sessions | Sessions | List active sessions. |
| POST | /api/v1/sessions | Sessions | Launch a session. |
| GET | /api/v1/sessions/:id | Sessions | Fetch one session. |
| GET | /api/v1/events?sessionId=... | Events | Read session events. |
| POST | /api/v1/sessions/:id/input | Sessions | Forward input to a live session. |
| POST | /api/v1/sessions/:id/kill | Sessions | Kill a live session. |
Unversioned routes currently remain as legacy aliases during the early MVP window, but new clients should not rely on them.
Unknown /api/<version>/... prefixes fail closed with an unsupported API version JSON response.
Health Response
GET /api/v1/health is the required compatibility handshake. It returns the named contract version, the installed Coven binary version, machine-readable capabilities, and optional daemon metadata:
{
"ok": true,
"apiVersion": "coven.daemon.v1",
"covenVersion": "0.0.0",
"capabilities": {
"sessions": true,
"events": true,
"eventCursor": "sequence",
"structuredErrors": true
},
"daemon": {
"pid": 12345,
"startedAt": "2026-05-09T12:00:00Z",
"socket": "/Users/example/.coven/coven.sock"
}
}When no daemon metadata is available, daemon is null.
Clients should treat apiVersion: "coven.daemon.v1" as the stable contract guard. covenVersion identifies the binary build and can be shown in diagnostics, but clients should not branch compatibility on a package version alone.
GET /api/v1/api-version is lower-level route metadata. It currently returns the route prefix version, not the named contract:
{
"apiVersion": "v1",
"supportedApiVersions": ["v1"]
}Use /api/v1/health for normal client handshakes. Use /api/v1/api-version only when debugging route-prefix support.
Control-plane Capabilities
GET /api/v1/capabilities is the discovery point for first-party clients. It returns capability ids, adapter ownership, availability, policy hints, and action ids. This keeps clients from hard-coding what the daemon can do.
{
"capabilities": [
{
"id": "coven.control.actions",
"label": "Coven control-plane action router",
"adapter": "coven-daemon",
"status": "available",
"policy": "allow",
"actions": ["coven.capabilities.refresh"]
},
{
"id": "desktop.automation",
"label": "Desktop automation adapters",
"adapter": "desktop-use",
"status": "planned",
"policy": "requiresApproval",
"actions": []
}
]
}Control-plane Actions
POST /api/v1/actions accepts a structured intent envelope. The daemon routes only known actions; unknown actions fail closed before any adapter can run.
{
"action": "coven.capabilities.refresh",
"origin": "coven-client",
"args": {}
}Immediately completed safe actions return 200 with an event-shaped payload that clients can render optimistically or fold into later event streams:
{
"ok": true,
"accepted": true,
"action": "coven.capabilities.refresh",
"status": "completed",
"event": {
"kind": "capabilities.refreshed",
"action": "coven.capabilities.refresh",
"origin": "coven-client",
"payload": { "capabilities": 3 }
}
}Session Records
Session endpoints return the Rust daemon's SessionRecord shape with snake_case field names:
{
"id": "session-1",
"project_root": "/Users/example/project",
"harness": "codex",
"title": "Fix failing tests",
"status": "running",
"exit_code": null,
"archived_at": null,
"created_at": "2026-05-09T06:43:00Z",
"updated_at": "2026-05-09T06:43:05Z"
}| Method | Path | Response | Notes |
|---|---|---|---|
| GET | /api/v1/sessions | SessionRecord[] | Lists active, non-archived sessions. |
| POST | /api/v1/sessions | SessionRecord | Launches a project-scoped harness session. |
| GET | /api/v1/sessions/:id | SessionRecord | Returns 404 session_not_found for unknown ids. |
POST /api/v1/sessions accepts the launch fields the daemon validates before spawning a harness:
{
"projectRoot": "/Users/example/project",
"cwd": "/Users/example/project",
"harness": "codex",
"prompt": "Fix the failing tests",
"title": "Fix failing tests"
}cwd must resolve inside projectRoot. harness must be a supported harness id. Provider credentials are not part of this request; Codex, Claude Code, and future harnesses keep their own local auth flows.
Events
GET /api/v1/events returns an event page for one session.
| Parameter | Required | Meaning |
|---|---|---|
sessionId | Yes | Session id whose events should be returned. |
afterSeq | No | Preferred cursor. Returns events with seq > afterSeq. |
afterEventId | No | Compatibility cursor resolved to a sequence position. |
limit | No | Maximum records to return. The daemon enforces a max of 1000. |
{
"events": [
{
"seq": 42,
"id": "event-1",
"session_id": "session-1",
"kind": "output",
"payload_json": "{\"data\":\"hello\"}",
"created_at": "2026-05-09T06:43:10Z"
}
],
"nextCursor": {
"afterSeq": 42
},
"hasMore": false
}nextCursor is null when there are no events. Persisting nextCursor.afterSeq lets clients resume incremental reads after a daemon restart.
Live Input
POST /api/v1/sessions/:id/input forwards input only to a daemon-verified live session.
Request:
{
"data": "continue with the next failing test\n"
}Success returns 202:
{
"ok": true,
"accepted": true
}The daemon records accepted input as a session event. Completed, failed, killed, archived, or orphaned sessions are replay/log surfaces, not live input targets.
Live Kill
POST /api/v1/sessions/:id/kill terminates only a daemon-verified live process. The request body is empty.
Success returns 202:
{
"ok": true,
"accepted": true
}After a successful kill, the daemon updates the session status to killed and writes a kill event. Killing a session is not the same as deleting its history; use archive/summon/sacrifice lifecycle commands for record management.
Error Envelope
All API failures use this structured envelope. Clients should branch on error.code, not message text:
{
"error": {
"code": "session_not_found",
"message": "Session was not found.",
"details": {
"sessionId": "session-1"
}
}
}| Code | HTTP status | Meaning |
|---|---|---|
not_found | 404 | Route not found. |
invalid_request | 400 or 404 | Malformed request, unsupported API version, or unknown control action. |
session_not_found | 404 | Session id does not exist. |
session_not_live | 409 | Session exists but cannot accept input or kill because it is not running in this daemon. |
project_root_violation | 400 | Requested cwd resolves outside the declared project root. |
pty_spawn_failed | 500 | Harness PTY could not be launched. |
runtime_unavailable | 503 | The session runtime is unavailable. |
internal_error | 500 | Unexpected internal daemon error. |
Unknown session:
{
"error": {
"code": "session_not_found",
"message": "Session was not found.",
"details": { "sessionId": "session-1" }
}
}Non-live input or kill:
{
"error": {
"code": "session_not_live",
"message": "Session is not live.",
"details": { "sessionId": "session-1" }
}
}Compatibility Rules
| Rule | Risk area | Client or maintainer action |
|---|---|---|
Additive JSON fields are allowed in v1 responses. | Forward compatibility | Clients should ignore unknown optional fields when safe. |
Existing required fields should not be removed or renamed inside v1. | Breaking changes | Keep required response shapes stable for versioned clients. |
| Breaking response-shape or behavior changes require a new API version prefix. | Versioning | Move incompatible behavior behind a new route prefix. |
External clients should call /api/v1/health before assuming compatibility. | Handshake | Check daemon availability and API version first. |
Daemon changes that affect /api/v1/health, /api/v1/sessions, /api/v1/events, input, or kill behavior should update client compatibility tests in the same repo. | Contract drift | Update tests with the API-affecting change. |
Last updated on