Sends JSON‑RPC plus optional callback, grant, and attestation headers.
Wire contracts at a glance.
Each layer exposes a short HTTP+JSON surface. Start with the path that matches your job, then drop into runnable calls, schema files, retry rules, and the proof row each request leaves behind. Where the byte‑exact shape matters, this page links into TECH_SPEC.md — the spec is authoritative.
Run Lite or the SDK first, then switch from observe to enforce once the first audit row verifies.
/docs/quickstart
Data plane
Call the proxy
Send JSON‑RPC over POST /mcp with mTLS SVID or lite bearer auth.
POST /mcp
Operators
Resolve review
Create and decide human approval rows without guessing the pending/decision payload shape.
/pending + /decide
Audit
Replay proof
Join proxy, Brain, Policy, HIL, and upstream outcome rows by one correlation id.
/audit/correlation/{id}
- Trust boundaries
- Endpoint matrix
- JSON‑RPC envelope + headers
- Runnable curl examples
- Machine‑readable schemas
- Proxy —
POST /mcp - SDK migration — explicit execution owner
- Brain —
POST /inspect - Policy —
POST /evaluate - HIL —
POST /pending+/decide/{id} - Ledger —
/log+/verify+/audit/* - End‑to‑end trace
- Identity —
/svid+/grant - Health · readyz · metrics
- Retries + idempotency
- Error shapes + status codes
Public ingress is intentionally tiny.
The proxy is the only data‑plane endpoint an agent should call directly. Brain, Policy, HIL, Ledger, and Identity stay private or operator‑gated, so auth failures fail closed before side effects reach an upstream tool.
POST /mcp
Requires mTLS SVID or lite bearer auth; stamps the correlation id.
Service-to-service calls only; produces semantic and Rego verdict signals.
Approvals and credential minting stay behind operator/session controls.
Verification and correlation replay expose audit evidence, not write paths.
Endpoint, auth, success, failure, proof row.
Use this table as the integration index before jumping into a per-service contract. The proof column tells you which ledger event should exist when the request finishes.
Filter by boundary or operational use. Rows keep the same wire contracts; filters only narrow the table.
| Service | Endpoint | Auth | Success | Failure | Proof row |
|---|---|---|---|---|---|
| Proxy Data plane | POST /mcp |
mTLS SVID or lite bearer | 200 upstream response, 202 pending |
403 deny, 426 legacy client, 429 velocity, 503 dependency |
proxy.verdict, upstream.outcome |
| Brain Private | POST /inspect |
Private service network | InspectionResponse |
503 model/provider unavailable |
brain.inspection |
| Policy Private | POST /evaluate |
mTLS service caller | allow, deny, or review |
400 malformed input, 503 store unavailable |
policy.evaluated |
| HIL Operator | POST /pending, POST /decide/{id} |
Proxy service token, operator session | Pending created or decision accepted | 401, 404, expiry closed |
hil.created, hil.approved, hil.denied |
| Ledger Proof | POST /log, GET /verify |
mTLS service caller, read auth on console | Row appended or chain valid | 503 store unavailable; invalid chains are reported in the 200 body |
Canonical hash-chain row |
| Identity Operator | POST /svid, POST /grant |
Admin token, OIDC token, SVID | Certificate or delegation grant | 401, attestation or envelope mismatch |
identity.svid_issued, identity.grant_issued |
No endpoints match that filter.
JSON‑RPC 2.0 + correlation header.
Every MCP request is JSON‑RPC 2.0. The proxy stamps a UUID‑v4 correlation_id on ingress and threads it through every downstream call so a single ledger row joins the proxy / brain / policy / HIL events.
{
"jsonrpc": "2.0",
"id": 1,
"method": "call_tool",
"params": { "name": "send_email", "arguments": { /* … */ } }
}
Headers (proxy ingress):
X‑Clavenar‑Correlation‑Id— proxy‑stamped, propagates downstream + into the ledgerX‑Clavenar‑Demo‑Prefix— 8‑hex from the demo‑session JWT (console‑demo only)X‑Clavenar‑Callback‑URL— async HIL: where to POST the operator decisionX‑Clavenar‑Grant— delegation grant from/grant(multi‑tenant)X‑Clavenar‑Attestation— per‑request override of attestation claims
Copy, set the host, and keep the correlation id.
These examples use placeholder hosts and credentials so they can be adapted to Lite, dev, or a private deployment. Copy buttons are added automatically by the docs runtime.
IDEMPOTENCY_ID="$(uuidgen)"
curl --http1.1 https://proxy.internal.example/mcp \
--cert ./agent-svid.pem \
--key ./agent-svid-key.pem \
--cacert ./bundle.pem \
-H 'Content-Type: application/json' \
-H 'X-Clavenar-Server-Execution-Contract: clavenar.server-execution/v1' \
-H "X-Clavenar-Idempotency-Id: $IDEMPOTENCY_ID" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "call_tool",
"params": {
"name": "refund.issue",
"arguments": { "amount": 8500, "currency": "USD" }
}
}'
IDEMPOTENCY_ID="$(uuidgen)"
curl https://localhost:8787/mcp \
-H 'Authorization: Bearer '"$CLAVENAR_LITE_TOKEN" \
-H 'Content-Type: application/json' \
-H 'X-Clavenar-Server-Execution-Contract: clavenar.server-execution/v1' \
-H "X-Clavenar-Idempotency-Id: $IDEMPOTENCY_ID" \
-H 'X-Clavenar-Callback-URL: https://agent.example/hil/callback' \
-d '{
"jsonrpc": "2.0",
"id": "demo-1",
"method": "call_tool",
"params": {
"name": "crm.update_customer",
"arguments": { "customer_id": "cus_123", "tier": "enterprise" }
}
}'
CORRELATION_ID="3ab2e8c2-0000-4000-9000-8fb77a123456"
curl "https://console.internal.example/api/audit/correlation/${CORRELATION_ID}" \
-H 'Authorization: Bearer '"$OPERATOR_TOKEN"
curl "https://ledger.internal.example/verify" \
--cert ./service-svid.pem \
--key ./service-svid-key.pem \
--cacert ./bundle.pem
Machine‑readable contracts for validators and tests.
The spec remains authoritative, but these JSON Schema files give SDKs, CI smoke tests, and API clients stable fixtures for the most common integration payloads.
JSON‑RPC 2.0 request body accepted by POST /mcp.
proxy-mcp-request.schema.json
Verdict
Proxy response
Allow, deny, pending, and rate-limit response envelopes with correlation ids.
proxy-verdict.schema.json
Operator
HIL decision
Approval, denial, and typed modification fields for POST /decide/{id}.
hil-decision.schema.json
Proof
Ledger verify
Successful and broken-chain responses for GET /verify.
ledger-verify.schema.json
Release inventory
Routes, types, and examples
All 139 generated-enforced routes plus twelve source-bound schemas and examples.
clavenar.route-schema-release/v1
Data plane · POST /mcp
The agent's MCP transport target. mTLS client cert (SVID) or bearer token (lite) authenticates the agent. The proxy fans out to Brain → Policy → HIL → Ledger and returns a verdict that proxies the upstream response (allow) or fails closed (deny / pending).
- Auth: mTLS (proxy) or
Authorization: Bearer(lite) - Verdict on success: upstream MCP response, plus
X‑Clavenar‑Correlation‑Id - Verdict on deny:
403with JSON body{error, intent_category, reasoning, correlation_id} - Verdict on park:
202with JSON body{status: "pending", correlation_id, ttl_secs} - Rate limit:
429with{error, scope, key, retry_after_secs, correlation_id} - Legacy client:
426with{contract: "clavenar.client-migration/v1", error: "client_contract_required", executable: false}
POST /mcp
Content-Type: application/json
X-Clavenar-Server-Execution-Contract: clavenar.server-execution/v1
X-Clavenar-Idempotency-Id: 8f1d3a40-7c0e-4d2b-9c9a-3b1d0e6a2c5a
{
"jsonrpc": "2.0",
"id": 1,
"method": "call_tool",
"params": {
"name": "refund.issue",
"arguments": { "amount": 8500 }
}
}
HTTP/1.1 200 OK
X-Clavenar-Correlation-Id: 3ab2...
{ "jsonrpc": "2.0", "id": 1, "result": { /* upstream */ } }
HTTP/1.1 403 Forbidden
{
"error": "policy_denied",
"intent_category": "Privileged Action",
"reasoning": "refund.issue over threshold",
"correlation_id": "3ab2..."
}
HTTP/1.1 202 Accepted
{
"status": "pending",
"correlation_id": "3ab2...",
"ttl_secs": 300
}
References: service architecture · clavenar‑lite README for the OSS variant.
Semantic eval · POST /inspect
Returns InspectionResponse: classifier verdict + persona drift score + injection signal. The proxy folds these into the Policy input.
# request { "agent_id": "agent-001", "jsonrpc": "2.0", "method": "call_tool", "params": { /* … */ }, "id": 1, "correlation_id": "e3b0c44…" } # response { "authorized": true, "reason": "Standard tool call", "intent_category": "Direct Execution", "persona_drift_score": 0.12, "injection_detected": false, "injection_confidence": 0.0 }
The supported public boundary is documented here; accuracy, latency, caveats, and reproduction guidance live in the reviewed benchmark.
Governance · POST /evaluate
Pure‑Rust Rego evaluator (regorus). Input is PolicyInput; output is PolicyDecision { allow, reasons, review_reasons, retention_class, approval_tier, policy_bundle }.
clavenar.authz.allow— must betruefor the request to passclavenar.authz.denyset with reasons — fails closedclavenar.authz.reviewset — yellow tier (HIL pending)clavenar.forensic.event— written to ledger regardless of verdict
The reviewed policy catalog lists the released starter policies and exact category totals.
Human approval · POST /pending + POST /decide/{id}
Yellow‑tier verdicts park here. Sandbox annotates predicted blast radius; the operator decides via the console, Slack deep‑link, or — async — via the SDK resolve() helper backed by the X‑Clavenar‑Callback‑URL contract.
POST /pending— proxy enqueues and returns the completePendingRequestPOST /decide/{id}— operator approves, denies, or submits a typed modification; authenticated principal and tenant are server‑derivedGET /pending— list active (operator UI / agent poll)GET /pending/{id}— single‑row state; use/pending/by-correlation/{correlation_id}for the join key
POST /pending
{
"agent_id": "agent-finance-01",
"correlation_id": "3ab2f91d-9e8d-4a8a-920f-67f587229e0d",
"method": "refund.issue",
"request_payload": {"params": {"arguments": {"amount": 7500}}},
"risk_summary": "money movement over the approval threshold",
"ttl_seconds": 600,
"state_namespace": "operator"
}
POST /decide/8bf35b7f-e50c-4cc3-a612-6e1a0e8aba19
{
"decision": "approve",
"reason": "Customer support ticket verified",
"decided_via": "console"
}
The response is the updated PendingRequest. In authenticated modes HIL derives decided_by, provenance, credential, and tenant from the verified session or exact Console workload rather than trusting the JSON body.
Forensic store · /log · /verify · /audit/*
Append‑only. Each row contains the full request, verdict, policy version, operator NameID (if HIL), and a chain pointer SHA‑256(prev_hash · canonical_row_bytes).
POST /log— write a forensic event (proxy / policy / HIL caller)GET /verify— always returnsVerifyResult; inspectvalid,first_invalid_seq, optionalunsupported_chain_version, and the complete-chaincommitmentGET /audit/correlation/{uuid}— pull all rows joined to a single proxy requestGET /audit/agents/{agent_id}/narrative?window=7d— the "story view"GET /export/iceberg?from=&to=— Parquet cold tier (S3)egress.streams.{splunk,datadog,generic}— push targets configured per‑sink
GET /verify
{
"valid": true,
"entries_checked": 85200,
"first_invalid_seq": null,
"commitment": {
"contract": "clavenar.verified-chain/v1",
"head_hash": "a5d3e8ef0a5d65d47cd3e787f0760f2217daa04151401539dbd58a5d4439b926",
"length": 85200,
"tail_chain_version": 6
},
"anchors": [],
"anchor_mismatch": null
}
GET /audit/correlation/3ab2...
[
{ "method": "proxy.verdict", "authorized": false },
{ "method": "hil.approved", "decided_by": "saml:finance-oncall" },
{ "method": "upstream.outcome", "status": 200 }
]
Backend is pluggable: SqliteLedgerStore (default) or PostgresLedgerStore behind the postgres Cargo feature; the chain bytes are byte‑identical across both.
One call through every contract.
A single high‑risk tool call should be explainable from ingress to upstream outcome. Use the correlation id as the join key and expect these ledger rows in order.
POST /mcp accepts JSON‑RPC and returns 202 when Policy asks for review.
proxy.verdict
Brain scores intent/drift/injection, then Policy evaluates the merged signal object.
brain.inspection + policy.evaluated
POST /pending parks the call; POST /decide/{id} applies the authenticated operator decision.
hil.created + hil.approved
GET /audit/correlation/{uuid} returns the joined rows for incident review.
upstream.outcome
GET /verify walks the hash chain and proves those rows were not rewritten later.
{ "valid": true }
Trust roots · POST /svid + POST /grant + POST /actor-token
The only component that mints credentials. Every other service consumes; nothing else issues. Identity is a single failure domain on purpose — rotating the trust root is one place to reason about.
POST /svid— agent enrolment; mints an SVID (mTLS client cert) keyed on(tenant, agent_name)POST /grant— exchange OIDCid_token+ agent SVID → delegation grant; intersects with the agent's registered envelopePOST /sign— sign a JWT for a registered agent (delegation, attestation, etc.)POST /actor-token+/actor-token/redeem— A2A federation across tenantsGET /.well-known/spiffe-bundle— SPIFFE bundle for federation peersPOST /agents+ lifecycle — the WAO (agent onboarding) registry
Uniform /health · /readyz · /metrics shape.
Every service hits the same trio with the same wire shape so Helm probes, dashboards, and Prometheus configs are one set of YAML. Metric naming is clavenar_<service>_<event>_<unit>.
GET /health— process up;200alwaysGET /readyz— dependencies reachable;503when not readyGET /metrics— Prometheus text exposition format
Replay only when the contract says it is safe.
The caller allocates X-Clavenar-Idempotency-Id before network I/O. Decision requests are side-effect free and may retry with that original UUID; server executions never enter an automatic transport retry loop.
| Surface | Retry behavior | Idempotency rule | Client action |
|---|---|---|---|
decision/v1 |
Bounded transport retries are allowed for network errors and retryable dependency responses. | Reuse the original canonical X-Clavenar-Idempotency-Id. |
Stop on 403; park on 202 until callback or HIL poll resolves. |
server-execution/v1 |
Do not automatically retry an effect-capable request after a transport or upstream failure. | The original UUID binds the durable intent and retained result. | An exact caller-driven replay returns a completed result; an uncertain outcome stays non-executable. |
POST /pending |
Retry only when the proxy did not receive a response. | One pending row per correlation id; duplicate creates should return the existing state. | Keep the original TTL and callback URL; do not mint a new correlation id. |
POST /decide/{id} |
Retry 503; do not retry 401, 403, or expired rows. |
The first accepted decision wins; later writes must fail closed. | After success, replay the correlation id to confirm hil.approved or hil.denied. |
GET /verify |
Safe to repeat; use normal dashboard polling cadence. | Read-only. The response describes the current ledger head. | Treat valid: false, an unsupported chain version, or an incomplete commitment as an incident and stop promotion until investigated. |
Status codes you'll actually see.
Status code carries the type; the JSON body carries the reason and a stable error code for programmatic handling.
200— verdictallow(orwould_denyin observe mode)202— verdictpark— pending operator decision; correlation id returned400— malformed envelope / unsupported method401 / 403— auth failure (no SVID, expired grant, scope outside envelope, attestation fail)403 deny— policy verdict;{error, intent_category, reasoning, correlation_id}409— HIL row already decided or another state transition conflict426— unselected legacy tool call; upgrade using the SDK migration guide429— rate limit;{error, scope, key, retry_after_secs, correlation_id}503— downstream not ready (Brain / Policy / Ledger unreachable)