318 lines
8.7 KiB
Markdown
318 lines
8.7 KiB
Markdown
# amduat-api
|
|
|
|
`amduat-api` builds `amduatd`, a minimal HTTP server over a Unix domain socket that exposes Amduat substrate operations for a **single ASL store root**.
|
|
|
|
## Build
|
|
|
|
```sh
|
|
cmake -S . -B build
|
|
cmake --build build -j
|
|
```
|
|
|
|
To build without the embedded UI:
|
|
|
|
```sh
|
|
cmake -S . -B build -DAMDUATD_ENABLE_UI=OFF
|
|
```
|
|
|
|
When the UI is enabled (default), `/v1/ui` serves the same embedded HTML as before.
|
|
|
|
## Core dependency
|
|
|
|
This repo vendors the core implementation as a git submodule at `vendor/amduat`.
|
|
|
|
```sh
|
|
git submodule update --init --recursive
|
|
```
|
|
|
|
## Quickstart (local)
|
|
|
|
Initialize a store:
|
|
|
|
```sh
|
|
./vendor/amduat/build/amduat-asl init --root .amduat-asl
|
|
```
|
|
|
|
Run the daemon (fs backend default):
|
|
|
|
```sh
|
|
./build/amduatd --root .amduat-asl --sock amduatd.sock
|
|
```
|
|
|
|
Run the daemon with the index-backed store:
|
|
|
|
```sh
|
|
./build/amduatd --root .amduat-asl --sock amduatd.sock --store-backend index
|
|
```
|
|
|
|
Note: `/v1/fed/records` requires the index backend.
|
|
|
|
## Federation (dev)
|
|
|
|
Federation is opt-in and disabled by default. Enabling federation requires the
|
|
index-backed store and explicit flags:
|
|
|
|
```sh
|
|
./build/amduatd --root .amduat-asl --sock amduatd.sock --store-backend index \
|
|
--fed-enable --fed-transport unix --fed-unix-sock peer.sock \
|
|
--fed-domain-id 1 --fed-registry-ref <registry_ref_hex>
|
|
```
|
|
|
|
Flags:
|
|
|
|
- `--fed-enable` turns on the coordinator tick loop.
|
|
- `--fed-transport stub|unix` selects transport (`stub` by default).
|
|
- `--fed-unix-sock PATH` configures the unix transport socket path.
|
|
- `--fed-domain-id ID` sets the local domain id.
|
|
- `--fed-registry-ref REF` seeds the registry reference (hex ref).
|
|
- `--fed-require-space` rejects `/v1/fed/*` requests that do not resolve a space.
|
|
|
|
`X-Amduat-Space` is honored for `/v1/fed/*` requests the same way as other
|
|
endpoints. If `--space` is configured, unix transport requests will include the
|
|
same `X-Amduat-Space` header when contacting peers.
|
|
|
|
Run the daemon with derivation indexing enabled:
|
|
|
|
```sh
|
|
./build/amduatd --root .amduat-asl --sock amduatd.sock --enable-derivation-index
|
|
```
|
|
|
|
To fail `/v1/pel/run` if the derivation index write fails:
|
|
|
|
```sh
|
|
./build/amduatd --root .amduat-asl --sock amduatd.sock --derivation-index-strict
|
|
```
|
|
|
|
Dev loop (build + restart):
|
|
|
|
```sh
|
|
./scripts/dev-restart.sh
|
|
```
|
|
|
|
Query store meta:
|
|
|
|
```sh
|
|
curl --unix-socket amduatd.sock http://localhost/v1/meta
|
|
```
|
|
|
|
Browser UI (use `socat` to forward the Unix socket):
|
|
|
|
```sh
|
|
socat TCP-LISTEN:8080,fork,reuseaddr UNIX-CONNECT:amduatd.sock
|
|
```
|
|
|
|
Then open `http://localhost:8080/v1/ui` (concept editor).
|
|
|
|
Discover the store-backed API contract ref:
|
|
|
|
```sh
|
|
curl --unix-socket amduatd.sock 'http://localhost/v1/contract?format=ref'
|
|
```
|
|
|
|
Fetch the contract bytes (JSON):
|
|
|
|
```sh
|
|
curl --unix-socket amduatd.sock http://localhost/v1/contract
|
|
```
|
|
|
|
Upload raw bytes:
|
|
|
|
```sh
|
|
curl --unix-socket amduatd.sock -X POST http://localhost/v1/artifacts \
|
|
-H 'Content-Type: application/octet-stream' \
|
|
--data-binary 'hello'
|
|
```
|
|
|
|
Download raw bytes:
|
|
|
|
```sh
|
|
curl --unix-socket amduatd.sock http://localhost/v1/artifacts/<ref>
|
|
```
|
|
|
|
Download artifact framing (`ENC/ASL1-CORE v1`):
|
|
|
|
```sh
|
|
curl --unix-socket amduatd.sock \
|
|
'http://localhost/v1/artifacts/<ref>?format=artifact' --output artifact.bin
|
|
```
|
|
|
|
Run a PEL program from store-backed refs (default `scheme_ref=dag`):
|
|
|
|
```sh
|
|
curl --unix-socket amduatd.sock -X POST http://localhost/v1/pel/run \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"program_ref":"<program_ref>","input_refs":["<input_ref_0>"],"params_ref":"<params_ref>"}'
|
|
```
|
|
|
|
When derivation indexing is enabled, successful PEL runs record derivations under
|
|
`<root>/index/derivations/by_artifact/` keyed by output refs (plus result/trace/receipt refs).
|
|
|
|
Define a PEL/PROGRAM-DAG/1 program (store-backed):
|
|
|
|
```sh
|
|
curl --unix-socket amduatd.sock -X POST http://localhost/v1/pel/programs \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"nodes":[{"id":1,"op":{"name":"pel.bytes.concat","version":1},"inputs":[{"external":{"input_index":0}},{"external":{"input_index":1}}],"params_hex":""}],"roots":[{"node_id":1,"output_index":0}]}'
|
|
```
|
|
|
|
Create a named concept and publish a ref (so you can use the name instead of hex refs):
|
|
|
|
```sh
|
|
curl --unix-socket amduatd.sock -X POST http://localhost/v1/concepts \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"name":"hello","ref":"<program_ref>"}'
|
|
```
|
|
|
|
Publish a new version:
|
|
|
|
```sh
|
|
curl --unix-socket amduatd.sock -X POST http://localhost/v1/concepts/hello/publish \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"ref":"<program_ref_v2>"}'
|
|
```
|
|
|
|
Resolve the latest ref:
|
|
|
|
```sh
|
|
curl --unix-socket amduatd.sock http://localhost/v1/resolve/hello
|
|
```
|
|
|
|
Inspect concepts:
|
|
|
|
```sh
|
|
curl --unix-socket amduatd.sock http://localhost/v1/concepts
|
|
curl --unix-socket amduatd.sock http://localhost/v1/concepts/hello
|
|
```
|
|
|
|
Artifact info (length + type tag):
|
|
|
|
```sh
|
|
curl --unix-socket amduatd.sock 'http://localhost/v1/artifacts/<ref>?format=info'
|
|
```
|
|
|
|
## Space selection
|
|
|
|
Requests can select a space via the `X-Amduat-Space` header:
|
|
|
|
```sh
|
|
curl --unix-socket amduatd.sock http://localhost/v1/concepts \
|
|
-H 'X-Amduat-Space: demo'
|
|
```
|
|
|
|
Precedence rules:
|
|
|
|
- `X-Amduat-Space` header (if present)
|
|
- daemon `--space` default (if configured)
|
|
- unscoped names (no space)
|
|
|
|
When capability tokens are used, the requested space must match the token's
|
|
space (or the token must be unscoped), otherwise the request is rejected.
|
|
|
|
## Current endpoints
|
|
|
|
- `GET /v1/meta` → `{store_id, encoding_profile_id, hash_id, api_contract_ref}`
|
|
- `GET /v1/contract` → contract bytes (JSON) (+ `X-Amduat-Contract-Ref` header)
|
|
- `GET /v1/contract?format=ref` → `{ref}`
|
|
- `GET /v1/ui` → browser UI for authoring/running programs
|
|
- `GET /v1/fed/records?domain_id=...&from_logseq=...&limit=...` → `{domain_id, snapshot_id, log_prefix, next_logseq, records[]}` (published artifacts + tombstones + PER + TGK edges)
|
|
- `GET /v1/fed/artifacts/{ref}` → raw bytes for federation resolve
|
|
- `GET /v1/fed/status` → `{status, domain_id, registry_ref, last_tick_ms}`
|
|
- `POST /v1/artifacts`
|
|
- raw bytes: `Content-Type: application/octet-stream` (+ optional `X-Amduat-Type-Tag: 0x...`)
|
|
- artifact framing: `Content-Type: application/vnd.amduat.asl.artifact+v1`
|
|
- `GET|HEAD /v1/artifacts/{ref}`
|
|
- raw bytes default
|
|
- artifact framing: `?format=artifact`
|
|
- `POST /v1/concepts`
|
|
- request: `{name, ref?}` (`name` is lowercase; `ref` publishes an initial version)
|
|
- response: `{name, concept_ref}`
|
|
- `POST /v1/concepts/{name}/publish` → publishes a new version `{ref}`
|
|
- `GET /v1/concepts` → `{concepts:[{name, concept_ref}]}`
|
|
- `GET /v1/concepts/{name}` → `{name, concept_ref, latest_ref, versions[]}`
|
|
- `GET /v1/resolve/{name}` → `{ref}` (latest published)
|
|
- `POST /v1/pel/run`
|
|
- request: `{program_ref, input_refs[], params_ref?, scheme_ref?}` (`program_ref`/`input_refs`/`params_ref` accept hex refs or concept names; omit `scheme_ref` to use `dag`)
|
|
- request receipt (optional): `{receipt:{input_manifest_ref, environment_ref, evaluator_id, executor_ref, started_at, completed_at, sbom_ref?, parity_digest_hex?, executor_fingerprint_ref?, run_id_hex?, limits?, logs?, determinism_level?, rng_seed_hex?, signature_hex?}}`
|
|
- response: `{result_ref, trace_ref?, receipt_ref?, output_refs[], status}`
|
|
- `POST /v1/pel/programs`
|
|
- request: authoring JSON for `PEL/PROGRAM-DAG/1` (kernel ops only; `params_hex` is raw hex bytes)
|
|
- response: `{program_ref}`
|
|
- `POST /v1/context_frames`
|
|
|
|
Receipt example (with v1.1 fields):
|
|
|
|
```json
|
|
{
|
|
"program_ref": "ab12...",
|
|
"input_refs": ["cd34..."],
|
|
"receipt": {
|
|
"input_manifest_ref": "ef56...",
|
|
"environment_ref": "7890...",
|
|
"evaluator_id": "local-amduatd",
|
|
"executor_ref": "1122...",
|
|
"started_at": 1712345678,
|
|
"completed_at": 1712345688,
|
|
"executor_fingerprint_ref": "3344...",
|
|
"run_id_hex": "deadbeef",
|
|
"limits": {
|
|
"cpu_ms": 12,
|
|
"wall_ms": 20,
|
|
"max_rss_kib": 1024,
|
|
"io_reads": 1,
|
|
"io_writes": 0
|
|
},
|
|
"logs": [
|
|
{"kind": 1, "log_ref": "5566...", "sha256_hex": "aabbcc"}
|
|
],
|
|
"determinism_level": 2,
|
|
"rng_seed_hex": "010203",
|
|
"signature_hex": "bead"
|
|
}
|
|
}
|
|
```
|
|
|
|
Federation records example:
|
|
|
|
```bash
|
|
curl --unix-socket amduatd.sock \
|
|
'http://localhost/v1/fed/records?domain_id=1&from_logseq=0&limit=256'
|
|
```
|
|
|
|
```json
|
|
{
|
|
"domain_id": 1,
|
|
"snapshot_id": 42,
|
|
"log_prefix": 1234,
|
|
"next_logseq": 120,
|
|
"records": [
|
|
{
|
|
"domain_id": 1,
|
|
"type": 0,
|
|
"ref": "ab12...",
|
|
"logseq": 100,
|
|
"snapshot_id": 42,
|
|
"log_prefix": 1234,
|
|
"visibility": 1,
|
|
"has_source": false,
|
|
"source_domain": 0
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
Response example:
|
|
|
|
```json
|
|
{
|
|
"result_ref": "aa11...",
|
|
"trace_ref": "bb22...",
|
|
"receipt_ref": "cc33...",
|
|
"output_refs": ["dd44..."],
|
|
"status": "OK"
|
|
}
|
|
```
|
|
|
|
## Notes
|
|
|
|
- This is intentionally a local-first surface (Unix socket, no TLS). HTTPS can be added later without changing the semantics.
|