# Ultimate Tool local service API **API schema:** 13 The development service exposes one `c64dev.toml` project at `http://127.0.0.1:6464` by default. It serves the dashboard and JSON API from the same origin. It does not listen on the LAN. Start it with: ```sh cargo run -q -p ultimate-tool -- serve ``` Use `--manifest PATH` to select a project contract and `--port PORT` to change the loopback port. ## Safety boundary `GET /api/v1/session` returns a random token that lasts for the current server process. Every `POST` requires that value in `X-Ultimate-Tool-Token`, JSON content, and—when sent by a browser—the exact dashboard origin. The service does not enable CORS. The Ultimate password is read only from `C64U_PASSWORD` in the server environment. It is never accepted in an API body. A physical run requires: 1. `POST /api/v1/ultimate/probes` with the device host. 2. Review of the returned product, hostname, ID, firmware, and capabilities. 3. `POST /api/v1/jobs` with the expiring `probe_id` and the exact confirmed reset count. 4. When the project declares `ultimate.reu_image`, separately confirm the exact REU image write count. It must equal `repeat`; otherwise it must be zero. The probe authorization expires after five minutes and is consumed by one accepted job. The core probes again and pins the identity before mutation. ## Endpoints ### Inspection - `GET /api/v1/health` - `GET /api/v1/session` - `GET /api/v1/project` - `GET /api/v1/targets` - `GET /api/v1/jobs/active` - `GET /api/v1/jobs/{job_id}` - `GET /api/v1/jobs/{job_id}/events` - `GET /api/v1/runs?limit=50` - `GET /api/v1/runs/compare?baseline=RUN_UUID&candidate=RUN_UUID&context=resident` - `GET /api/v1/runs/checkpoint-comparison?baseline=RUN_UUID&candidate=RUN_UUID` - `GET /api/v1/checkpoint-comparisons?limit=50` - `GET /api/v1/checkpoint-comparisons/{comparison_id}` - `GET /api/v1/runs/{run_id}` - `GET /api/v1/runs/{run_id}/resolve?address=$B000&context=resident` - `GET /api/v1/runs/{run_id}/timeline?cursor=40:120&limit=256&address=$D020-$D021&access=write&context=resident` - `GET /api/v1/runs/{run_id}/crash-report?context=resident` - `GET /api/v1/runs/{run_id}/diagnostic-report` - `GET /api/v1/runs/{run_id}/diagnostic-report/items?section=checkpoints&limit=50&cursor=CURSOR` - `GET /api/v1/runs/{run_id}/evidence/{name}` - `GET /api/v1/riscv/intents/{intent_id}` Run history comes from immutable `.ultimate-tool/runs` records rather than a database. The evidence endpoint serves only registered files and `SHA256SUMS`; paths and unregistered names are rejected. The resolver accepts decimal, `$ABCD`, and `0xABCD` addresses. Its schema-2 result state is `resolved`, `ambiguous`, `unmapped`, or `unavailable`; all four are successful HTTP responses. The optional context is exact. Invalid addresses and failed analysis-integrity checks return `422 analysis_failed`. Each match labels its nullable location as `address_callsite`, `symbol_start`, or `null`. The flat HTTP response uses top-level `schema_version = 13` and `resolver_schema_version = 2`, avoiding duplicate JSON keys. Run comparison returns schema-1 `compared`, `unavailable`, or `incompatible` data with HTTP 200. Invalid comparison evidence returns `422 comparison_failed`. Crash reports return schema-2 `resolved`, `ambiguous`, `unmapped`, or `unavailable` data. Stable unavailable reasons are `no_crash_state`, `site_not_instrumented`, and `analysis_unavailable`. An optional context is exact and recomputes only from the run's checksummed crash-state and build-analysis evidence. Malformed or checksum-invalid registered inputs return `422 crash_analysis_failed`. The flat response uses `schema_version = 13` and `crash_analysis_schema_version = 2`. Comparison responses likewise retain API schema 13 and expose `comparison_schema_version = 1`. ### Run bus timeline ```http GET /api/v1/runs/{run_id}/timeline?limit=256 ``` Returns bounded pages over one immutable run's checksummed 6510 debug-stream capture. Parameters: `cursor` (`DATAGRAM_ORDINAL:ENTRY_INDEX`, default start), `limit` (default 256, hard maximum 4096), inclusive `address` range such as `$D020-$D021`, `access` (`read` or `write`), and an exact analysis `context`. The flat response carries top-level `schema_version = 13` plus `timeline_schema_version = 1`, `mode` (`bus_only` or `source_linked`), `capture_complete`, `capture_truncated`, `crosses_gap`, `next_cursor`, and `items`. Gap and uncertain-datagram rows are always included, even under active filters; source resolution is computed only from the run's own checksummed artifact and analysis. Instruction, call, and return labels are intentionally never produced. Invalid cursors, out-of-range limits, empty contexts, missing captures, and tampered evidence return `422 timeline_failed`. ### Run diagnostic report ```http GET /api/v1/runs/{run_id}/diagnostic-report GET /api/v1/runs/{run_id}/diagnostic-report/items?section=checkpoints&limit=50 ``` The report summarizes one immutable run: `status` (`finalized` or `provisional`), `outcome`, `target_kind`, `execution_plane`, `failure`, `first_error`, `expectations` (with the recorded test snapshot when present), `results` (expected-versus-observed terminal and screen verdicts plus a heartbeat and crash summary), `artifact`, `evidence` integrity counts, and nullable `budget` and `comparison` summaries. A recorded memory capture has a typed `checkpoints` summary: full eligible proof is `verified`, while invalid or narrowly recognized legacy captures are `unavailable`; absent captures are null. Corrupt evidence fails closed. Workflow checkpoints are independently pageable. `counts` carries the row count of every detail section so a client can size its paging without loading rows. Expectation availability distinguishes recorded values, genuine pre-schema-13 legacy absence, non-test operations, and unavailable modern contracts. Cursor tokens are bounded to 512 bytes. Resolved instrumented crashes retain up to eight verified same-run inline source frames, withheld on ambiguity. The report is assembled from the run record and its verified analysis, crash, budget, and comparison evidence; it never reads the current project manifest, so history stays inspectable after the manifest is changed or deleted. A finished run verifies every registered file first and fails closed with `422 diagnostic_report_failed`; an unfinished run returns a `provisional` summary, where `interrupted` reports whether the owning service still considers the run active. The items endpoint returns one bounded page of a section. `section` is required and is one of `checkpoints`, `budget`, `comparison_sections`, `comparison_objects`, `comparison_symbols`, or `assertions`. `limit` defaults to 50 and accepts 1 through 100. `cursor` is the opaque `next_cursor` from a previous page; it is bound to its run, section, and verified source digest, so cross-run, cross-section, stale, and malformed cursors are rejected. The `checkpoints` section reports the runner's workflow checkpoints (`name`/`passed`/`detail`/`timestamp`), not the frozen memory-checkpoint ledger. Both responses use the flat versioning convention: top-level `schema_version = 13` with the inner contract in `diagnostic_report_schema_version = 1`. An unknown run returns `404 core_error`; a corrupt record, tampered evidence, or invalid section, limit, or cursor returns `422 diagnostic_report_failed`. Both routes are read-only and need no mutation token. The dashboard script `GET /diagnostic-report.js` builds these requests and renders the returned data; it performs no diagnostics itself. ### Checkpoint comparison ```http GET /api/v1/runs/checkpoint-comparison?baseline=RUN_UUID&candidate=RUN_UUID GET /api/v1/checkpoint-comparisons?limit=50 GET /api/v1/checkpoint-comparisons/{comparison_id} ``` The first route is a provisional, read-only comparison of two finished runs' frozen memory-checkpoint captures. It takes `baseline` and `candidate` run UUIDs (the `baseline_id` and `candidate_id` aliases are also accepted). It runs the same core `compare_run_checkpoints` method the CLI uses and stores nothing, so it is repeatable and never creates a pair record. The flat response carries top-level `schema_version = 13` plus `checkpoint_comparison_schema_version = 1` and the schema-1 comparison: `state` (`matched`, `diverged`, `incompatible`, or `unavailable`), the project/test/semantics identity, both run and target identifiers, artifact and evidence digests, ordered per-row digests, the first recorded difference, and coded reasons. A run that records no capture is a successful `unavailable` result, not a 404. A checksum-invalid or inconsistent capture returns `422 checkpoint_comparison_failed`. The other two routes read the immutable pair records a checkpoint-matrix job stores under `.ultimate-tool/comparisons`. The listing returns bounded summaries (`comparison_id`, `created_at`, `comparison_sha256`, both run IDs, and `state`) newest first with any per-record warnings; `limit` defaults to 50 and is rejected with `422 invalid_limit` outside 1 through 200 rather than silently clamped. The single-record route returns the stored record flat with top-level `schema_version = 13` and `checkpoint_comparison_record_schema_version = 1`, embedding the full schema-1 comparison. A missing or corrupt pair (including one whose checksum manifest no longer matches) returns `422 checkpoint_comparison_failed`, never a swallowed 404. All three routes are read-only and need no mutation token. The specialized VICE-then-Ultimate checkpoint matrix runs through the ordinary matrix job route with an added flag: ```json { "test": "smoke", "targets": ["vice", "ultimate"], "repeat": 1, "compare_checkpoints": true, "ultimate_authorization": { "probe_id": "PROBE_UUID", "confirmed_reset_count": 1, "confirmed_reu_write_count": 0 } } ``` `compare_checkpoints` defaults to `false`. When true the service requires exactly the two targets `vice` then `ultimate` with `repeat` 1, validated before the probe authorization is consumed and before any build or network access, so an unsupported order (`422 invalid_checkpoint_targets`) or repeat (`422 invalid_checkpoint_repeat`) never reaches a target and never burns a single-use permit. The core also requires a test that declares a `memory_checkpoint_capture`. The matrix builds once, runs VICE first, and starts the Ultimate candidate only after the VICE baseline passed with a valid, verified capture; it always stores exactly one pair record. The job snapshot then carries that stored `checkpoint_comparison_id`, and a pair whose state is not `matched` fails the job even when both runs passed. The job still uses the same single-Ultimate-reset authorization contract as an ordinary physical run; this route adds no new credentials, and `C64U_PASSWORD` stays server-side. `GET /api/v1/project` labels each test with a `memory_checkpoint_capture` boolean so a client can tell which tests are eligible. ### Read-only hardware probe ```http POST /api/v1/ultimate/probes Content-Type: application/json X-Ultimate-Tool-Token: SESSION_TOKEN {"host":"DEVICE_HOST","strict_gate":false} ``` The response includes `probe_id`, `expires_at`, and a normalized target identity. `strict_gate` defaults to `false`. When true the probe issues each read once, with no retry, redirect, or swallowed optional-capability failure, and the stored probe is marked strict. It is a restriction on the read path and adds no authorization. ### Prepare and start a debug-stream capture Preparation consumes a fresh probe, binds and joins the configured multicast destination on the selected local interface, holds the target lease, and performs no hardware mutation: ```http POST /api/v1/debug-captures/preparations {"probe_id":"PROBE_UUID","interface":"RECEIVER_ETHERNET_IP"} ``` The returned preparation expires after five minutes. Cancel it without device access using `DELETE /api/v1/debug-captures/preparations/{preparation_id}`. After reviewing the identity, `6510 Only` mode, destination, interface, and exact mutation envelope, start the asynchronous job with: ```http POST /api/v1/debug-captures { "preparation_id": "PREPARATION_UUID", "confirmed_stream_start_count": 1, "confirmed_stream_stop_count": 1 } ``` The preparation is single-use. The core rechecks identity and configuration, never retries either mutation, preserves each complete datagram accepted within the hard bounds, and records an ordinary immutable run. One active preparation reserves the service job lane. ### Prepare and start a source-linked capture The source-linked flow adds a clean candidate build, artifact snapshot, normalized analysis validation, and a unique probe-symbol requirement on top of the raw receiver preparation, and requires three separate confirmations: one reset with DMA load and launch of the reviewed artifact, one stream start, and one unconditional stop. Preparation performs no hardware mutation: ```http POST /api/v1/source-linked-captures/preparations {"probe_id":"PROBE_UUID","interface":"RECEIVER_ETHERNET_IP","test":"timeline-probe"} ``` Start after reviewing the returned identity, stream configuration, and prerequisites: ```http POST /api/v1/source-linked-captures { "preparation_id": "PREPARATION_UUID", "confirmed_reset_count": 1, "confirmed_stream_start_count": 1, "confirmed_stream_stop_count": 1 } ``` All three counts must be exactly one; the counts are validated before the preparation is consumed. Preparations are kind-tagged: a raw start cannot consume a source-linked preparation and vice versa; a mismatch returns `422 capture_preparation_kind_mismatch` and leaves the preparation in place. `DELETE /api/v1/debug-captures/preparations/{preparation_id}` cancels either kind idempotently without device access. The produced run is source-linked and queryable through the timeline endpoint above. The combined operation passed its physical gate on 2026-10-05 on the pinned device through the verified wired Ethernet/IGMPv2 receiver path. The accepted run retained 11,704 datagrams and explicitly recorded capture-bound truncation; stable post-stop reads verified terminal pass and `QUT1`. This is one reviewed device/artifact/network result, not a guarantee for other configurations. See [the acceptance record](SOURCE_LINKED_TIMELINE.md#combined-physical-operation). ### Start a matrix job Fake or VICE: ```json { "test": "smoke", "targets": ["fake", "vice"], "repeat": 1, "baseline_run_id": "OPTIONAL_RUN_UUID" } ``` With physical hardware: ```json { "test": "smoke", "targets": ["vice", "ultimate"], "repeat": 1, "strict_gate": false, "ultimate_authorization": { "probe_id": "PROBE_UUID", "confirmed_reset_count": 1, "confirmed_reu_write_count": 0 } } ``` `strict_gate` defaults to `false` and is orthogonal to `compare_checkpoints`. It is a fail-stop restriction, never an added permission: it keeps the pinned identity, reset count, probe expiry, token, origin, and lease rules unchanged. When true the job fails closed on the first transport, status, body, or parse error and requires a probe issued with `strict_gate`; an ordinary probe is rejected with `422 probe_strictness_mismatch` before the permit is consumed and before any mutation. The bounded supported shapes, the mutation budget (3 + 6 = 9 across the two separately authorized operations), and the one-resume-per-pause-episode rule are documented in [CHECKPOINT_COMPARISON.md](CHECKPOINT_COMPARISON.md#strict-physical-gate). `GET /api/v1/project` exposes `ultimate_reu_image` as a project-relative path or `null`. If it is non-null, a hardware probe must advertise `reu_write_dma` before a job can consume that probe authorization. The service returns `202 Accepted` with a `job_id`. One matrix job may run at a time; another submission receives `409 job_busy`. Closing the browser does not cancel an active job. ### Trusted native preparation and standing authorization `POST /api/v1/riscv/preparations` builds the configured `ultimate_riscv` project and returns an immutable preparation. It performs no device access. After a fresh `POST /api/v1/ultimate/probes`, create an intent: ```json { "preparation_id": "PREPARATION_UUID", "probe_id": "PROBE_UUID", "authorization": "standing", "expires_at": null, "maximum_runs": null, "confirmed_standing_authorization": true } ``` Submit this to `POST /api/v1/riscv/intents`. One-shot mode does not require the standing confirmation. `GET /api/v1/riscv/intents/{intent_id}` exposes the exact UE2 hash, target snapshot, bounds, use count, and state. `POST /api/v1/riscv/intents/{intent_id}/revoke` with `{"reason":"..."}` is permanent and audit-recorded. `POST /api/v1/riscv/runs` accepts: ```json {"intent_id":"INTENT_UUID","device":"DEVICE_HOST"} ``` It returns an asynchronous job. The core freshly probes the device and requires equality with the intent's identity and firmware snapshot before any mutation. Each run uses a fresh display ID and UTR2 token. Exact stock recovery and a tool-owned post-recovery C64 health check are required; failure of either suspends the standing intent. The core also latches authorization immediately before a first FTP creation and the final **Run Update** write, so ambiguous acceptance leaves the intent non-active instead of permitting a later retry. The host validator and intent do not sandbox native code. If a native run already proved exact stock recovery but could not start its C64 health check, `POST /api/v1/riscv/health-checks` accepts: ```json { "intent_id": "INTENT_UUID", "after_run_id": "NATIVE_RUN_UUID", "device": "DEVICE_HOST" } ``` This is a separate asynchronous job. It performs no FTP creation or **Run Update**. The core requires an Ultimate RISC-V parent run from the same standing intent, exact stock recovery, no prior linked health-check run, and the exact intent-bound target/firmware snapshot. The intent is suspended while the C64 reset/run and coordinated startup write are pending and reactivated only after observable pass. ## Progress stream `GET /api/v1/jobs/{job_id}/events` is a server-sent event stream. Each `progress` event has an integer SSE ID and a JSON body: ```json { "schema_version": 13, "sequence": 7, "job_id": "JOB_UUID", "progress": { "schema_version": 5, "timestamp": "2026-08-03T00:00:00Z", "kind": "diagnostic", "run_id": "RUN_UUID", "target": "vice", "detail": "ready", "diagnostic": {} } } ``` The server replays its bounded per-job buffer after `Last-Event-ID`. When the requested sequence is older than the retained buffer, it sends `resync_required`; clients then reload the job and run resources. Progress is live coordination data and lasts only for the server process. Completed run evidence remains durable. Run summaries and details expose the versioned structured `failure` record and the run-level `heartbeat` summary. Failures include a stable code, layer, stage, detection time, last event, and heartbeat state. `first_error` remains present for compatibility with older consumers. Schema-9 run records may also contain a schema-2 `crash_state` with capture-entry registers, an optional exact site, and metadata for the registered raw context, bounded stack, zero-page, and selected-global evidence files. They also contain `supporting_artifacts`; schema-5 screen observation records use `screen_match` with empty events/mailbox observations and no supported heartbeat. Schema 7 added nullable `analysis`; schema 8 added `budget`, `comparison`, and `crash_analysis` metadata for checksummed normalized evidence. Older records load unavailable fields as `null`. Schema 9 analysis metadata adds `line_range_count` beside source/section/contribution/symbol counts and line availability. Budget metadata includes verdict, result counts, and minimum headroom. Comparison metadata includes state, baseline, delta counts, and warnings. Schema 8 adds nullable `crash_analysis` metadata for registered `crash-analysis.json`; schemas 1–7 load it as `null`. Project test descriptions expose `observation` and make `expected_terminal` nullable for screen tests. Run schema 11 adds native preparation, launch-intent, invocation-display, and post-recovery C64 health-check linkage while retaining compatibility with historical native smoke records. Run schema 12 adds the `ultimate_debug_stream` execution plane, nullable `debug_capture` metadata, and a nullable `build` so capture runs do not invent build commands. Historical object-valued build records load unchanged. Run schema 13 records the selected test's expectation snapshot that the diagnostic report compares against; historical records load with a null snapshot and report expectations as `not_recorded_legacy`. ## Errors API errors use one envelope: ```json { "schema_version": 13, "error": { "code": "hardware_authorization_required", "message": "Ultimate runs require a fresh probe and reset confirmation" } } ``` Configuration and authorization failures do not silently fall back to another target or weaker identity check.