Ultimate Tool local service API #
Distribution copy. This is the sanitized public copy shipped in the
docs/directory of the release archives. It is derived from the repository documentdocs/HTTP_API.md, which is withheld from every archive in its original form. Transformations applied for distribution: this provenance notice; the physical-gate sentence for the source-linked capture was replaced with the same limits stated inline, because the acceptance record it linked to is withheld. No request, response, or schema content was changed, and every relative link below resolves inside both archives.
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:
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:
POST /api/v1/ultimate/probeswith the device host.- Review of the returned product, hostname, ID, firmware, and capabilities.
POST /api/v1/jobswith the expiringprobe_idand the exact confirmed reset count.- When the project declares
ultimate.reu_image, separately confirm the exact REU image write count. It must equalrepeat; 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/healthGET /api/v1/sessionGET /api/v1/projectGET /api/v1/targetsGET /api/v1/jobs/activeGET /api/v1/jobs/{job_id}GET /api/v1/jobs/{job_id}/eventsGET /api/v1/runs?limit=50GET /api/v1/runs/compare?baseline=RUN_UUID&candidate=RUN_UUID&context=residentGET /api/v1/runs/checkpoint-comparison?baseline=RUN_UUID&candidate=RUN_UUIDGET /api/v1/checkpoint-comparisons?limit=50GET /api/v1/checkpoint-comparisons/{comparison_id}GET /api/v1/runs/{run_id}GET /api/v1/runs/{run_id}/resolve?address=$B000&context=residentGET /api/v1/runs/{run_id}/timeline?cursor=40:120&limit=256&address=$D020-$D021&access=write&context=residentGET /api/v1/runs/{run_id}/crash-report?context=residentGET /api/v1/runs/{run_id}/diagnostic-reportGET /api/v1/runs/{run_id}/diagnostic-report/items?section=checkpoints&limit=50&cursor=CURSORGET /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 #
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 #
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 #
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:
{
"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 #
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:
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:
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:
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:
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 has
passed a physical gate on one reviewed, identity-pinned device through the
verified wired Ethernet/IGMPv2 receiver path, where it retained 11,704
datagrams with explicit capture-bound truncation while stable post-stop reads
verified terminal pass and QUT1. That is one reviewed device/artifact/network
result, not a guarantee for other configurations; the acceptance record itself
is withheld from the distribution. The operation requires a wired Ethernet
receiver on the device's LAN, the device's multicast debug stream, and three
separately confirmed counts (one reset with DMA load and launch, one stream
start, one unconditional stop). It performs no retry: an ambiguous failure
leaves the capture stopped and is never replayed.
Start a matrix job #
Fake or VICE:
{
"test": "smoke",
"targets": ["fake", "vice"],
"repeat": 1,
"baseline_run_id": "OPTIONAL_RUN_UUID"
}
With physical hardware:
{
"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.
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:
{
"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:
{"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:
{
"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:
{
"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:
{
"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.