Trail evidence APIs and @keeptrusts/agent 0.1.0 limit
:::danger Do not use the 0.1.0 evidence helpers
This unlisted page is retained to document their contract mismatch. Use the authenticated trail API workflow below for evidence assertions.
:::
The trail API supports request lookup, digest listing and download, public-key
retrieval, and server-side chain verification. It does not expose the
per-request evidence-bundle model implemented by @keeptrusts/agent 0.1.0.
Use a control-plane bearer token with org:trail:read for reads. Server-side
verification requires org:trail:admin.
Look up trail events by request ID
The lookup route requires a UUID request ID and returns an events wrapper.
It parses both the standard hyphenated notation and the 32-character simple
notation used on gateway decision events:
curl -sS \
--get "$KEEPTRUSTS_API_URL/v1/trail/events/lookup" \
--header "Authorization: Bearer $KEEPTRUSTS_CONTROL_PLANE_TOKEN" \
--data-urlencode "request_id=$KEEPTRUSTS_REQUEST_ID"
Each trail event uses fields such as event_id, event_time, event_source,
event_name, request_id, sequence_number, previous_hash, and
record_hash. Trail events do not include a digest_id field.
List and download digests
Digest records are queried independently from request lookup:
curl -sS \
--get "$KEEPTRUSTS_API_URL/v1/trail/digests" \
--header "Authorization: Bearer $KEEPTRUSTS_CONTROL_PLANE_TOKEN" \
--data-urlencode "limit=50"
The response contains digests, next_cursor, and result_count. Choose a
digest_id from that list, then download it:
curl -sS \
--header "Authorization: Bearer $KEEPTRUSTS_CONTROL_PLANE_TOKEN" \
"$KEEPTRUSTS_API_URL/v1/trail/digests/$KEEPTRUSTS_DIGEST_ID/download"
The download response contains storage_key and dsse_envelope. It does not
repeat digest_id and does not return the SDK's body_base64 or encoding
fields.
The current public API does not provide a direct event-to-digest association. Do not infer one from array position or timestamps.
Retrieve the verification key
GET /v1/trail/public-key returns kid, algorithm, public_key, and
created_at. The public key is base64-encoded, the algorithm is Ed25519, and
the creation time is RFC3339.
getPublicKey(client) is the one 0.1.0 evidence helper whose path and response
fields align with this route, subject to the package entrypoint limitation.
Verify a trail window
POST /v1/trail/verify verifies a bounded time window. Replace both
placeholder values with RFC3339 timestamps. start_time must be earlier than
end_time, and the selected mode must find at least one verifiable digest or
event; reversed, equal, and empty windows return an error:
{
"start_time": "replace-with-rfc3339-start",
"end_time": "replace-with-rfc3339-end",
"deep_verify": true
}
The response contains status, digests_verified, events_verified,
chain_integrity, first_sequence, last_sequence, gaps, and
verification_time_ms. Treat status and chain_integrity as the verification
result; do not infer success from a successful HTTP status alone.
With deep_verify: false, the server checks the stored digest links between
digest rows returned for the window. digests_verified is that row count and
events_verified is the sum of their recorded event counts.
With deep_verify: true, the server recomputes every returned event's record
hash, requires consecutive sequence numbers, and checks each returned event's
previous_hash against the preceding returned event. events_verified is the
event row count; digests_verified reports digest records found in the same
window. Deep verification does not perform cryptographic DSSE-envelope
verification and cannot span more than 90 days.
Both modes are bounded: the first returned record has no predecessor in the
response, so its inbound link is not checked. A status of pass means the
checks above passed inside that nonempty returned window; it is not an
assertion about a specific event ID or continuity with records outside the
window.
Why the evidence helpers are incompatible
getAgentTrailEventssends query names the trail list route does not accept and expectsdatarather thanevents.lookupRequestEventexpects adigest_idthat trail lookup does not return.getEventDigestassumes an event can be resolved directly to a digest.verifyEventIntegritysendsevent_id,request_id, anddigest_idto a route that requiresstart_timeandend_time.buildEvidenceBundlecomposes all of those incompatible assumptions.
Do not use these helpers for audit assertions in 0.1.0.