Skip to main content
Unlisted page
This page is unlisted. Search engines will not index it, and only users having a direct link can access it.

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

  • getAgentTrailEvents sends query names the trail list route does not accept and expects data rather than events.
  • lookupRequestEvent expects a digest_id that trail lookup does not return.
  • getEventDigest assumes an event can be resolved directly to a digest.
  • verifyEventIntegrity sends event_id, request_id, and digest_id to a route that requires start_time and end_time.
  • buildEvidenceBundle composes all of those incompatible assumptions.

Do not use these helpers for audit assertions in 0.1.0.

Next steps