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 - Brain —
POST /inspect - Policy —
POST /eval - HIL —
POST /pending+/decide - 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, 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 /eval |
mTLS service caller | allow, deny, or review |
400 malformed input, 503 store unavailable |
policy.evaluated |
| HIL Operator | POST /pending, POST /decide |
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 | 409 chain break, 503 store unavailable |
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.
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' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "call_tool",
"params": {
"name": "refund.issue",
"arguments": { "amount": 8500, "currency": "USD" }
}
}'
curl https://localhost:8787/mcp \
-H 'Authorization: Bearer '"$CLAVENAR_LITE_TOKEN" \
-H 'Content-Type: application/json' \
-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 and denial payload fields expected by POST /decide.
hil-decision.schema.json
Proof
Ledger verify
Successful and broken-chain responses for GET /verify.
ledger-verify.schema.json
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}
POST /mcp
X-Clavenar-Correlation-Id: auto
Content-Type: application/json
{
"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
}
Source: clavenar‑proxy README · 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 }
Full schema (cache layers, mock‑mode heuristics, model brokering) in the clavenar‑brain README and accuracy benchmark in BENCHMARK.md.
Governance · POST /eval
Pure‑Rust Rego evaluator (regorus). Input is the merged {request, brain_signals, attestation, agent_envelope} object; output is one of allow / deny / review + a forensic event payload.
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
Starter policies under policies/templates/: pii_egress, prod_db_writes, money_moves, agent_impersonation, prompt_injection, off_hours_actions, rate_limit_review.
Human approval · POST /pending + POST /decide
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; returns{correlation_id, ttl_secs}POST /decide— operator approves / denies; bearer‑token gatedGET /pending— list active (operator UI / agent poll)GET /pending/{correlation_id}— single‑row state
POST /pending
{
"correlation_id": "3ab2...",
"method": "refund.issue",
"risk_summary": "money movement over $5,000",
"ttl_secs": 300
}
POST /decide
{
"correlation_id": "3ab2...",
"decision": "approve",
"decider_note": "Customer support ticket verified"
}
Decision payload includes decided_by (NameID from console session), decider_note, decided_at. All four fields land in the ledger row.
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— walk the chain end‑to‑end;200with{height, ok: true}or409with the first broken rowGET /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": 630000,
"head_hash": "a5d3..."
}
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 captures the operator NameID and note.
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.
Use the correlation id as the idempotency key for client retries. Treat policy denials and HIL pendings as terminal from the caller's perspective until a decision or callback arrives.
| Surface | Retry behavior | Idempotency rule | Client action |
|---|---|---|---|
POST /mcp |
Retry 503 and network timeouts with jitter; obey 429 retry_after_secs. |
Reuse X-Clavenar-Correlation-Id when retrying the same tool call. |
Stop on 403; park on 202 until callback or HIL poll resolves. |
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 |
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 409 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— chain integrity broken (/verifyonly)429— rate limit;{error, scope, key, retry_after_secs, correlation_id}503— downstream not ready (Brain / Policy / Ledger unreachable)