# 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 ```json { "program_ref": "", "scheme_ref": "dag", "params_ref": "", "inputs": { "refs": [""], "inline_artifacts": [ { "content_type": "application/octet-stream", "type_tag": "0x00000000", "body_hex": "48656c6c6f" } ] }, "receipt": { "input_manifest_ref": "", "environment_ref": "", "evaluator_id": "local-amduatd", "executor_ref": "", "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 ```json { "run_ref": "", "trace_ref": "", "receipt_ref": "", "stored_input_refs": [""], "output_refs": [""], "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: ```json { "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`.