00Documentation
Three ways in. One record out.
An HTTP call from anything that can make one, a proxy in the MCP transport path, or a hook inside Claude Code. Everything below is the current implementation, including the exact responses.
01Quickstart
Quickstart
Create a key
In Ovelin, go to Settings → API keys. You need your organisation id and an API key. Put both in your shell profile rather than in a file that ends up in git.
export OVELIN_URL="https://your-ovelin-deployment"
export OVELIN_ORG_ID="your-org-uuid"
export OVELIN_API_KEY="…" # from Settings → API keys
# Older REEVE_* and AGENTLEDGER_* names still work; OVELIN_* wins if both are set.Send one action
The organisation id goes in the path; the key goes in the Authorization header.
curl -X POST $OVELIN_URL/api/ingest/$OVELIN_ORG_ID \
-H "Authorization: Bearer $OVELIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": "openai_api",
"agent_name": "invoice-agent",
"timestamp": "2026-08-14T14:22:07.412Z",
"data_accessed": ["customer_pii"],
"outcome": "success"
}'Read the response
The returned hash is the seal. It is computed over the normalised record and chained to the entry before it, before the response is sent.
HTTP/1.1 201 Created
{
"ok": true,
"id": "9f14c0a2-6b3e-4a71-b0d2-5c8e1f7a4d30",
"hash": "a41f9c02b7e34d61…",
"matched_agent": "invoice-agent",
"actor": "ops@example.com",
"redacted": [],
"alerts_raised": 0
}The entry is now in your Audit Log. If the payload contained anything matching a credential pattern, it was stripped before the record was hashed, and redacted lists what was removed.
02Ingest API
Ingest API
- Endpoint
- POST /api/ingest/:org_id
- Auth
- Authorization: Bearer <api key>. Keys are compared as hashes in constant time.
- source
- One of make_com, openai_api, zapier, n8n, custom. Each has its own payload shape, normalised into one event.
- Idempotency
- A replayed delivery produces the same hash as the original and returns duplicate: true rather than a second entry — webhook retries from Make.com, Zapier and n8n are safe.
- Errors
- 400 invalid organisation or JSON · 401 invalid key · 413 payload too large · 422 payload failed validation.
An alternative path form, /api/ingest/:org_id/:api_key, exists for platforms that cannot set a custom header. Prefer the header: a key in a URL ends up in proxy logs and browser history.
03MCP gateway
MCP gateway
A logging proxy for the Model Context Protocol. Your host connects to the gateway; the gateway connects to your real MCP servers. Every tool call and resource read passes through it.
An MCP server could expose a log_action tool instead — but a tool is something the model chooses to call, and “the AI recorded what it felt like recording” is not evidence. The gateway sits in the transport path, so the model cannot route around it.
Move your servers into a gateway config
Paste in the mcpServers entries your host uses today.
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/work"]
}
}
}Point the host at the gateway
Replace those entries with one. Tools reappear namespaced, as filesystem__read_file.
{
"mcpServers": {
"ovelin": {
"command": "node",
"args": ["/absolute/path/to/agentledger-gateway.mjs"],
"env": {
"OVELIN_GATEWAY_CONFIG": "/absolute/path/to/gateway.config.json"
}
}
}
}Modes
- monitor
- Default. Logs in the background, best effort. A failed audit write never blocks a tool call, so there is no added latency.
- enforce
- The intent is written and acknowledged before the call executes. If that write fails, the call is refused. Costs one ingest round trip per call and writes two events (intent, then outcome) correlated by call_id. Refuses to start without credentials.
Policy
Because the gateway is in the path, it can refuse calls rather than only record them. Rules are fetched and cached, so enforcement keeps working if Ovelin is briefly unreachable — losing contact never silently disarms a deny.
- allow · deny · require_approval
- Rules match a tool pattern and optionally an argument substring. A deny refuses the call; require_approval pauses the agent until a person decides.
- OVELIN_BLOCK_SECRETS=1
- Refuse any call whose arguments contain a live-looking credential, so the model cannot hand your API key to a third-party server.
- OVELIN_DENY_TOOLS
- A machine-local deny list, e.g. github__delete_repo. Local rules run first and are absolute.
- Org-wide halt
- A kill switch that stops all agent activity, delivered on the same feed as the rules.
Blocked calls are still audited, with metadata.blocked: true. An attempt is exactly the thing you want on the record.
04Claude Code hook
Claude Code hook
Records every tool call Claude Code makes — file reads and writes, shell commands, web fetches, subagent launches. A PostToolUse hook runs in the host, outside the model’s control, so capture is not voluntary.
Set the same three environment variables as above, then register the hook in .claude/settings.json for one project or ~/.claude/settings.json for all of them.
{
"hooks": {
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "node /absolute/path/to/agentledger-hook.mjs",
"timeout": 5
}
]
}
]
}
}Metadata only: file paths, commands and outcomes. Never file contents and never command output. The hook and the gateway share one redaction definition, so a credential is stripped identically by both.
05Verification
Verification
Each record is sealed with a keyed hash computed over the whole entry and chained to the digest before it. Verification recomputes that hash for a stored record and compares it to the value written at ingest; a mismatch means the record was altered.
- POST /api/verify-log
- Takes a log id. Session-authenticated and scoped by row-level security to your organisation, so it verifies your records and no one else’s.
- Keyed, not public
- The seal is an HMAC. The key is what stops someone who can write to the database from re-sealing the chain after altering it — which is why the browser demo on the home page, which has no key, is a demonstration of the mechanism rather than of the guarantee.
- Append-only
- Database triggers refuse updates and deletes on the ledger for every role, including Ovelin's own service role.
- Signed checkpoints
- Exports carry an Ed25519 signature over a digest of the records. node ovelin-verify.mjs export.json --public-key ovelin.pem checks it offline, with no dependencies — the HMAC needs Ovelin's secret; the checkpoint does not.
06What is stored
What is stored
Recorded
- Upstream server and tool name
- Arguments, redacted
- Success or failure, and duration
- Result size and part count
- Sensitivity classification
- Actor identity and session correlation id
Never recorded
- Tool results — file contents, query rows, API responses
- Resource bodies
- Anything matching a credential pattern
- Upstream environment values
Result size is recorded deliberately. Volume is what makes exfiltration detectable — “this agent read 400 MB through the database tool in an hour” — without retaining the data itself.