amduat-api/docs/v2-app-developer-guide.md
2026-02-07 20:11:15 +01:00

6.9 KiB

Amduat v2 App Developer Guide

This is the compact handoff guide for building an application against amduatd v2.

For machine-readable contracts, see registry/amduatd-api-contract.v2.json.

1) Runtime Model

  • One daemon instance serves one ASL store root.
  • Transport is local Unix socket HTTP.
  • Auth is currently filesystem/socket permission based.
  • All graph APIs are under /v2/graph/*.

Minimal local run:

./vendor/amduat/build/amduat-asl init --root .amduat-asl
./build/amduatd --root .amduat-asl --sock amduatd.sock --store-backend index

2) Request Conventions

  • Use X-Amduat-Space: <space_id> for app isolation.
  • Treat graph cursors as opaque tokens (g1_*), do not parse.
  • Use as_of for snapshot-consistent reads.
  • Use include_tombstoned=true only when you explicitly want retracted facts.

3) Core App Flows

A. High-throughput ingest

Use POST /v2/graph/batch with:

  • idempotency_key for deterministic retries
  • mode=continue_on_error for partial-apply behavior
  • per-item metadata_ref or provenance for trust/debug

Expect response:

  • ok (overall)
  • applied aggregate counts
  • results[] with {kind,index,status,code,error}

B. Multi-hop retrieval for agents

Primary endpoints:

  • GET /v2/graph/subgraph
  • POST /v2/graph/retrieve
  • POST /v2/graph/query for declarative filtering

Use:

  • as_of for stable reasoning snapshots
  • max_depth, max_fanout, limit_nodes, limit_edges, max_result_bytes
  • provenance filters where needed (provenance_ref / provenance_min_confidence)

C. Incremental sync loop

Use GET /v2/graph/changes:

  • Start with since_cursor (or bootstrap with since_as_of)
  • Persist returned next_cursor after successful processing
  • Handle 410 as replay-window expiry (full resync required)
  • Optional long poll: wait_ms

D. Fact correction

  • Edge retraction: POST /v2/graph/edges/tombstone
  • Node-version retraction: POST /v2/graph/nodes/{name}/versions/tombstone

Reads default to exclude tombstoned facts on retrieval surfaces unless include_tombstoned=true.

4) Endpoint Map (what to use when)

  • Write node: POST /v2/graph/nodes
  • Write version: POST /v2/graph/nodes/{name}/versions
  • Write edge: POST /v2/graph/edges
  • Batch write: POST /v2/graph/batch
  • Point-ish read: GET /v2/graph/nodes/{name}
  • Edge scan: GET /v2/graph/edges
  • Neighbor scan: GET /v2/graph/nodes/{name}/neighbors
  • Path lookup: GET /v2/graph/paths
  • Subgraph: GET /v2/graph/subgraph
  • Declarative query: POST /v2/graph/query
  • Agent retrieval: POST /v2/graph/retrieve
  • Changes feed: GET /v2/graph/changes
  • Export: POST /v2/graph/export
  • Import: POST /v2/graph/import
  • Predicate policy: GET/POST /v2/graph/schema/predicates
  • Health/readiness/metrics: GET /v2/healthz, GET /v2/readyz, GET /v2/metrics
  • Graph runtime/capability: GET /v2/graph/stats, GET /v2/graph/capabilities

5) Provenance and Policy

Provenance object fields for writes:

  • required: source_uri, extractor, observed_at, ingested_at, trace_id
  • optional: confidence, license

Policy endpoint:

  • POST /v2/graph/schema/predicates

Key modes:

  • predicate validation: strict|warn|off
  • provenance enforcement: optional|required

6) Error Handling and Retry Rules

  • Retry-safe writes: only retries with same idempotency_key and identical payload.
  • Validation failures: 400 or 422 (do not blind-retry).
  • Not found for references/nodes: 404.
  • Cursor window expired: 410 on /changes (rebootstrap sync state).
  • Result guard triggered: 422 (max_result_bytes or traversal/search limits).
  • Internal errors: 500 (retry with backoff).

7) Performance and Safety Defaults

Recommended client defaults:

  • Set explicit limit on scans.
  • Always pass max_result_bytes on large retrieval requests.
  • Keep max_depth conservative (start with 2-4).
  • Enable include_stats=true in development to monitor scanned/returned counts and selected plan.
  • Call /v2/graph/capabilities once at startup for feature/limit negotiation.

8) Minimal Startup Checklist (for external app)

  1. Probe GET /v2/readyz.
  2. Read GET /v2/graph/capabilities.
  3. Configure schema/provenance policy (POST /v2/graph/schema/predicates) if your app owns policy.
  4. Start ingest path (/v2/graph/batch idempotent).
  5. Start change-consumer loop (/v2/graph/changes).
  6. Serve retrieval via /v2/graph/retrieve and /v2/graph/subgraph.
  7. Monitor /v2/metrics and /v2/graph/stats.

9) Useful Local Helpers

  • scripts/graph_client_helpers.sh contains practical shell helpers for:
    • idempotent batch ingest
    • one-step changes sync
    • subgraph retrieval

For integration tests/examples:

  • scripts/test_graph_queries.sh
  • scripts/test_graph_contract.sh

10) Copy/Paste Integration Skeleton

Set local defaults:

SOCK="amduatd.sock"
SPACE="app1"
BASE="http://localhost"

Startup probes:

curl --unix-socket "${SOCK}" -sS "${BASE}/v2/readyz"
curl --unix-socket "${SOCK}" -sS "${BASE}/v2/graph/capabilities"

Idempotent batch ingest:

curl --unix-socket "${SOCK}" -sS -X POST "${BASE}/v2/graph/batch" \
  -H "Content-Type: application/json" \
  -H "X-Amduat-Space: ${SPACE}" \
  -d '{
    "idempotency_key":"app1-batch-0001",
    "mode":"continue_on_error",
    "nodes":[{"name":"doc:1"}],
    "edges":[
      {"subject":"doc:1","predicate":"ms.within_domain","object":"topic:alpha",
       "provenance":{"source_uri":"urn:app:seed","extractor":"app-loader","observed_at":1,"ingested_at":2,"trace_id":"trace-1"}}
    ]
  }'

Incremental changes loop (bash skeleton):

cursor=""
while true; do
  if [ -n "${cursor}" ]; then
    path="/v2/graph/changes?since_cursor=${cursor}&limit=200&wait_ms=15000"
  else
    path="/v2/graph/changes?limit=200&wait_ms=15000"
  fi

  resp="$(curl --unix-socket "${SOCK}" -sS "${BASE}${path}" -H "X-Amduat-Space: ${SPACE}")" || break

  # TODO: parse and process resp.events[] in your app.
  next="$(printf '%s\n' "${resp}" | sed -n 's/.*"next_cursor":"\([^"]*\)".*/\1/p')"
  [ -n "${next}" ] && cursor="${next}"
done

Agent retrieval call:

curl --unix-socket "${SOCK}" -sS -X POST "${BASE}/v2/graph/retrieve" \
  -H "Content-Type: application/json" \
  -H "X-Amduat-Space: ${SPACE}" \
  -d '{
    "roots":["doc:1"],
    "goal_predicates":["ms.within_domain"],
    "max_depth":2,
    "max_fanout":1024,
    "limit_nodes":200,
    "limit_edges":400,
    "max_result_bytes":1048576
  }'

Subgraph snapshot read:

curl --unix-socket "${SOCK}" -sS \
  "${BASE}/v2/graph/subgraph?roots[]=doc:1&max_depth=2&dir=outgoing&limit_nodes=200&limit_edges=400&include_stats=true&max_result_bytes=1048576" \
  -H "X-Amduat-Space: ${SPACE}"

Edge correction (tombstone):

EDGE_REF="<edge_ref_to_retract>"
curl --unix-socket "${SOCK}" -sS -X POST "${BASE}/v2/graph/edges/tombstone" \
  -H "Content-Type: application/json" \
  -H "X-Amduat-Space: ${SPACE}" \
  -d "{\"edge_ref\":\"${EDGE_REF}\"}"