amduat-api/docs/amduatd-api-v2-design.md
2026-02-07 19:46:59 +01:00

5.6 KiB

amduatd API v2 Design (PEL-only writes)

Status

Draft proposal for discussion. First server slice implemented: POST /v2/pel/execute with inputs.refs, inputs.inline_artifacts[{body_hex,type_tag?}], and required receipt.

Goals

  • Keep amduatd thin: transport, parsing, auth, and mapping to core calls.
  • Make PEL the only write primitive in HTTP v2.
  • Enforce artifact provenance for writes: adding new artifacts must go through:
    1. store inputs,
    2. run a PEL program,
    3. persist and return receipt.
  • Keep /v1/* behavior unchanged while v2 is introduced.

Non-goals

  • Re-implementing PEL semantics in amduat-api.
  • Creating new substrate semantics in daemon code.
  • Removing v1 immediately.

High-level model

v2 separates read and write concerns:

  • Read paths remain direct and deterministic (GET /v2/meta, GET /v2/artifacts/{ref}, federation/space read endpoints).
  • All state-changing operations are executed as PEL runs.

In v2, POST /v1/artifacts equivalent is replaced by a mutation endpoint that always drives a PEL run.

For simplified clients, v2 also exposes async operation endpoints:

  • POST /v2/ops/put
  • POST /v2/ops/concat
  • POST /v2/ops/slice
  • GET /v2/jobs/{id}

These enqueue work and execute PEL in the daemon background loop.

Graph-oriented endpoints can be added as thin wrappers over concepts/relations:

  • POST /v2/graph/nodes (maps to concept create/publish)
  • POST /v2/graph/edges (maps to relation edge append)
  • GET /v2/graph/nodes/{name} (node + versions + incoming/outgoing edges)
  • GET /v2/graph/history/{name} (version + edge timeline)

Versioning and contract id

  • Base path: /v2
  • Contract id: AMDUATD/API/2
  • Keep the same store-seeded contract model used in v1.

Endpoint shape

Read-only endpoints (v2)

  • GET /v2/meta
  • GET /v2/contract
  • GET /v2/artifacts/{ref}
  • HEAD /v2/artifacts/{ref}
  • GET /v2/artifacts/{ref}?format=info
  • Space/federation read endpoints can be mirrored under /v2/* unchanged where possible.

Write endpoints (v2)

  • POST /v2/pel/execute

No direct artifact write endpoint in v2 (POST /v2/artifacts is intentionally absent).

POST /v2/pel/execute

Single mutation primitive for v2.

Request

{
  "program_ref": "<hex-ref-or-name>",
  "scheme_ref": "dag",
  "params_ref": "<optional-ref-or-name>",
  "inputs": {
    "refs": ["<hex-ref-or-name>"],
    "inline_artifacts": [
      {
        "content_type": "application/octet-stream",
        "type_tag": "0x00000000",
        "body_hex": "48656c6c6f"
      }
    ]
  },
  "receipt": {
    "input_manifest_ref": "<ref-or-name>",
    "environment_ref": "<ref-or-name>",
    "evaluator_id": "local-amduatd",
    "executor_ref": "<ref-or-name>",
    "started_at": 1731000000,
    "completed_at": 1731000005
  },
  "effects": {
    "publish_outputs": true,
    "append_fed_log": true
  }
}

Processing contract

Server behavior is deterministic and ordered:

  1. Resolve program_ref, scheme_ref, params_ref, and inputs.refs to refs.
  2. Decode and store inputs.inline_artifacts into ASL store.
  3. Build effective input_refs = refs + stored_inline_refs.
  4. Execute PEL using core pel_surf_run_with_result.
  5. Build FER receipt from run result + receipt fields.
  6. Store receipt artifact and return receipt_ref.
  7. Apply requested side effects (publish_outputs, append_fed_log) after successful run.

If any step fails:

  • return non-2xx,
  • do not publish outputs,
  • do not append federation publish records.

Response

{
  "run_ref": "<pel1-result-ref>",
  "trace_ref": "<optional-trace-ref>",
  "receipt_ref": "<fer1-receipt-ref>",
  "stored_input_refs": ["<ref>"],
  "output_refs": ["<ref>"],
  "status": "OK"
}

Notes:

  • stored_input_refs are refs created from inline_artifacts.
  • receipt_ref is required for successful writes in v2.

Error model

  • 400 invalid request or schema violation.
  • 404 referenced input/program/params not found.
  • 409 conflict in post-run side effects (e.g. cursor/update conflicts).
  • 422 run completed but business policy failed (reserved for policy checks).
  • 500 internal failure.

Error payload:

{
  "error": {
    "code": "invalid_input_ref",
    "message": "input ref not found",
    "retryable": false
  }
}

Behavioral differences from v1

  • Removed write path: direct POST /artifacts.
  • New requirement: every write returns a receipt ref tied to a PEL run.
  • pel/run semantics become the write gateway rather than an optional compute endpoint.

Compatibility and rollout

  1. Add /v2/pel/execute while keeping /v1/* unchanged.
  2. Mirror core read endpoints under /v2/*.
  3. Mark POST /v1/artifacts as deprecated in docs.
  4. Migrate clients to v2 execute flow.
  5. Remove v1 write endpoints in a later major release.

Open questions

  • Should v2 require exactly one output for write operations, or allow multiple outputs with one receipt?
  • Should inline artifact body support both body_hex and body_base64, or move to multipart for large payloads?
  • Should publish_outputs default to true or be explicit-only?
  • Do we need async execution (202 + run status endpoint) for long-running programs in v2?

Minimal implementation plan

  1. Add v2 contract bytes (registry/amduatd-api-contract.v2.json).
  2. Add /v2/pel/execute handler by adapting existing POST /v1/pel/run + artifact ingest path.
  3. Factor shared receipt parsing/building from current v1 pel handler.
  4. Add tests:
    • inline artifact -> stored -> run -> receipt success,
    • failure before run,
    • failure during run,
    • side effect gating (no publish on failure).
  5. Document deprecation notice for POST /v1/artifacts.