ergo · myzona

HTTP API reference

The engine's only public surface — 10 endpoints over a stdlib HTTP server.

The engine exposes a small, single-threaded HTTP API (stdlib server, no web framework) wrapping the claim store. All examples assume the engine on http://127.0.0.1:8788 with the token in $TOK.

Auth

If ERGO_API_TOKEN is set, every route except GET /health requires:

Authorization: Bearer <token>

If the var is unset, the server logs a warning at boot and runs open (dev/test only).

Write endpoints (contradiction-guarded)

These run the contradiction guard. A hard conflict returns 409 with the prior claim.

POST /remember

Guarded write for decisions / constraints / rejections / conventions.

fieldrequireddescription
org_idyestenant id
projectyesproject namespace within the tenant
whoyesactor (audit row)
statementyesthe claim text
reasonnowhy (free text; surfaced via /why)
force_exceptionnoif set, stores even on conflict, with this string as the exception reason
curl -s -H "$TOK" -X POST http://127.0.0.1:8788/remember -d '{
  "org_id":"acme","project":"platform","who":"alice",
  "statement":"Deploy on Tuesdays","reason":"team is on-call Mon/Wed"
}'
# 200 → {"result":"stored","claim_id":"...","warnings":[...]}
# 409 → {"result":"conflict","conflicts":[{"claim":{...},"tier":"block","score":0.97,...}]}
codemeaning
200stored (MED-tier warnings included if any)
409blocked — conflict set returned for the client to resolve
400missing required field
500normalizer / judge / store failure

POST /learn

The bridge between ingest (raw chunks, no gate) and remember (decisions, full gate). Use it when an agent read something and formed a belief. Same pipeline as remember; folds source into the stored reason for provenance. The contradiction check runs on the statement only — the source never pollutes the judge.

fieldrequireddescription
org_id, project, whoyesas /remember
statementyesthe belief to check + store
sourceyesprovenance (file:line, URL, doc id). Blank source is a 400
reasonnoextra rationale
curl -s -H "$TOK" -X POST http://127.0.0.1:8788/learn -d '{
  "org_id":"acme","project":"platform","who":"agent",
  "statement":"Sessions are never reused across restarts",
  "source":"sessions.py:847","reason":"observed in the restart path"
}'
# 200 → {"result":"learned","claim_id":"...","source":"sessions.py:847"}

POST /supersede

Change of mind. Replaces an active claim with a new one, and runs the guard against all other active claims first — if the new claim would itself contradict another, the supersede is refused.

curl -s -H "$TOK" -X POST http://127.0.0.1:8788/supersede -d '{
  "org_id":"acme","project":"platform","who":"alice","existing_id":"<id>",
  "statement":"Deploy on Wednesdays","reason":"Tuesdays now collide with standup"
}'
# 200 → {"result":"superseded","claim_id":"<new id>"}

Side effects: old row → status='superseded'; new row inserted with supersedes pointing at the old id (this is what /why walks).

POST /retract

Wrong-at-birth removal (typo, test junk, mis-scoped write). Flips an active claim to retracted. Retract ≠ supersede — if a decision genuinely evolved, use supersede to keep the why-chain. No gate runs (removal can't create a contradiction). The row is not deleted — it stays in /history with retract_reason; /active, /recall, /why, and the judge all exclude it.

curl -s -H "$TOK" -X POST http://127.0.0.1:8788/retract -d '{
  "org_id":"acme","project":"platform","who":"alice","claim_id":"<id>",
  "reason":"test junk written against the prod scope"
}'
# 200 → {"result":"retracted","claim_id":"<id>","retracted":true}

All five fields (org_id, project, who, claim_id, reason) are required; a blank reason is a 400.

Fast document path

POST /ingest

Bulk reference content. Skips the normalizer + judge (fact = upsert freely). Embeds each chunk and stores it. Requires the embedder to be enabled.

fieldrequireddescription
org_id, project, whoyesas /remember
textyesthe document body (any size)
sourcenoprovenance string, appended to each chunk for citations
curl -s -H "$TOK" -X POST http://127.0.0.1:8788/ingest -d '{
  "org_id":"acme","project":"kb","who":"alice","text":"...markdown...","source":"NOTES.md"
}'
# 200 → {"result":"ingested","chunks":49,"stored":49,"source":"NOTES.md"}

Chunking is paragraph-aware and word-safe (≈1200 chars, ≈150 overlap). Ingested facts are also skipped on the candidate side — a bulk chunk can never 409 a decision; only prior decisions can.

Read endpoints

GET /recall

Hybrid semantic + keyword search over active claims.

paramrequireddefaultdescription
org_id, projectyesscope
q (alias query)yessearch text
limitno10clamped to [1, 50]
curl -s -H "$TOK" "http://127.0.0.1:8788/recall?org_id=acme&project=kb&q=when%20to%20deploy&limit=3"

Score = 0.7 × vec_sim + 0.3 × keyword_overlap. Superseded / retracted claims are excluded.

GET /why

Like /recall but reasoned-only (a reason-less fact can never win a "why" query), returns the top hit, and walks the supersede chain.

curl -s -H "$TOK" "http://127.0.0.1:8788/why?org_id=acme&project=kb&q=when%20to%20deploy"
# → {"why":{"claim":"...","reason":"...","history":[ {newest}, {superseded} ]}}
# no reasoned match → {"why": null}

GET /active & GET /history

/active lists active claims with a typed state (project_missing / empty / has_active_claims). /history returns everything — active + superseded + retracted — ordered by sequence.

GET /diagnose

The ops view — the running binary answers for itself. Live counts, conflict telemetry, version-drift detection, and a warnings[] array flagging things the operator must act on (no backup configured, version drift, etc.). Bearer-authed.

GET /health

Liveness + an allowlisted config snapshot. No auth — so health checks don't need the secret. Ops metadata (owner, backup path) is deliberately kept off /health and lives on the authed /diagnose.

End-to-end

TOK="Authorization: Bearer $ERGO_API_TOKEN"

curl -s -H "$TOK" -X POST .../remember  -d '{"org_id":"acme","project":"platform","who":"alice","statement":"Deploy on Tuesdays","reason":"on-call Mon/Wed"}'
curl -s -H "$TOK" -X POST .../remember  -d '{"org_id":"acme","project":"platform","who":"bob","statement":"Never deploy on Tuesdays","reason":"incident risk"}'
# → 409 conflict against alice's claim
curl -s -H "$TOK" -X POST .../supersede -d '{"org_id":"acme","project":"platform","who":"alice","existing_id":"<id>","statement":"Never deploy on Tuesdays","reason":"incident risk outweighs on-call"}'
curl -s -H "$TOK" ".../why?org_id=acme&project=platform&q=when%20to%20deploy"
# → history with both entries