Chio/Docs

BuildOperations

Receipt Dashboard

The operator dashboard indexes the kernel receipt log and is served by the trust control plane API.

What It Shows

The dashboard is a thin skin over the same HTTP API the chio CLI uses. Every panel is driven by a documented endpoint on the trust-control service, so the view in the browser and the view from the CLI stay in sync. No client-side reconstruction, no shadow scoring path.

  • Receipts by agent: filter on an agent subject (hex-encoded ed25519 public key) to see only receipts produced against that agent's capability tokens.
  • Receipts by tool server and tool name: exact-match filters for narrowing to one route or tool.
  • Receipts by outcome: the four Decision variants. allow, deny, cancelled, incomplete.
  • Budget pressure: per-grant utilization with near-limit and exhaustion counters.
  • Time range: unix-second since / until window applied to every query.

The browser and the CLI see the same thing

Every filter on the sidebar maps directly to a query parameter on GET /v1/receipts/query. A dashboard view and the equivalent chio receipt list invocation return the same rows in the same order.

Layout at a Glance

The dashboard is a three-pane layout: filters on the left, a composed operator summary across the top, and a paginated receipt table that opens a detail panel on click.

rendering…
Three-pane dashboard: filter sidebar on the left, composed operator summary across the top, paginated receipt table beneath.

Clicking any row opens a detail panel on the right. The panel renders the full receipt JSON, financial metadata (cost charged, budget remaining, total budget, delegation depth, settlement status, all formatted from minor units), a per-day cost sparkline, and the full delegation chain from root capability to leaf.


Operator Summary

Above the receipt table sits a composed operator summary backed by GET /v1/reports/operator. It is not a client-side reconstruction over paged receipts. The summary is the primary view for answering the question is this agent or tool slice healthy and exportable right now?

PanelSurfacesUse For
ActivityReceipt totals, allow / deny ratioIs this slice doing anything at all
SpendSpend totals plus top root budget holderWho is driving cost in the filtered window
Budget PressurematchingGrants, nearLimitCount, exhaustedCountGrants about to trip or already tripped
Compliance StatecheckpointCoverageRate, lineageCoverageRate, uncheckpointedReceiptsAudit readiness of the receipt log
Settlement ReadinesspendingSettlementReceipts, failedSettlementReceipts, proof completenessIs this slice exportable for invoicing
Shared EvidenceReference counts plus the latest referenced partner / shareFederated reputation context

Export scope

The settlement readiness card calls out export-scope caveats directly. If a slice has pending settlement receipts, failed settlement receipts, or incomplete proofs, the dashboard tells you before you attempt an invoice export instead of after.

Filtering Receipts

The left sidebar exposes six filters. All of them map directly to GET /v1/receipts/query. Changing any filter resets pagination to page 1. Results are paginated 50 per page, with cursor-based previous and next controls.

FilterTypeDescription
Agent SubjectHex ed25519 pubkeyOnly receipts produced against this agent's capability tokens
Tool ServerExact-match stringSingle tool server identifier
Tool NameExact-match stringSingle tool name within a server
OutcomeEnumallow, deny, cancelled, incomplete
SinceUnix seconds (u64)Lower bound on receipt timestamp
UntilUnix seconds (u64)Upper bound on receipt timestamp

Launching the Dashboard

The dashboard SPA is served as a static catch-all from dashboard/dist/ on the same axum router as the control API, mounted after all API routes. It is gated on dashboard/dist/index.html being present at startup: build the bundle into that path and chio trust serve hosts the UI and the API on one address. There is no separate dashboard bind.

Local development

bash
# Default listen address: 127.0.0.1:8940. The service token is required;
# supply it via CHIO_TRUST_SERVICE_TOKEN so it stays out of ps/proc.
$ export CHIO_TRUST_SERVICE_TOKEN=$(cat ./secrets/chio_service_token.txt)
$ chio trust serve \
    --listen 127.0.0.1:8940 \
    --receipt-db ./receipts.sqlite \
    --authority-db ./authority.sqlite \
    --revocation-db ./revocations.sqlite

# Then open http://127.0.0.1:8940/ in a browser

The server serves the dashboard bundle as a static catch-all after all API routes, so the same address hosts the API and the UI. There is no second port to open.

Docker compose

docker-compose.yml
services:
  chio-trust:
    # An image carrying the chio binary plus the compiled dashboard/dist
    # bundle. chio trust serve is a subcommand of the single chio binary,
    # not a separate trust-control product image.
    image: your-registry/chio:latest
    command:
      - chio
      - trust
      - serve
      - --listen
      - 0.0.0.0:8940
      - --receipt-db
      - /var/lib/chio/receipts.sqlite
      - --authority-db
      - /var/lib/chio/authority.sqlite
      - --revocation-db
      - /var/lib/chio/revocations.sqlite
    ports:
      - "8940:8940"
    volumes:
      - chio-state:/var/lib/chio
    environment:
      # Required bearer token. hide_env_values keeps it out of --help output;
      # source it from your secret manager or a .env file, never commit it.
      CHIO_TRUST_SERVICE_TOKEN: ${CHIO_TRUST_SERVICE_TOKEN}

volumes:
  chio-state:

Authentication

Every request the dashboard makes is bearer-authenticated. The token is resolved in this order:

  1. sessionStorage key chio_token (set from a previous visit).
  2. URL query parameter ?token=<value>.

When a token is found in the URL it is promoted to sessionStorage and removed from the URL bar via window.history.replaceState. That keeps the token out of browser history and out of the Referer header on outbound navigations.

bash
# Hand an operator a one-shot URL
$ echo "http://127.0.0.1:8940/?token=$(cat ./secrets/chio_service_token.txt)"
http://127.0.0.1:8940/?token=sv_live_8f2c...

No token means no data

If no token is present the dashboard renders an explicit operator guidance state. It does not fall back to issuing unauthenticated requests, so a misconfigured deployment fails loud instead of silently leaking receipt metadata.

Closing the tab clears sessionStorage. Reopening the dashboard later requires either a fresh ?token= URL or the previous tab still being alive.


Receipt Detail Panel

Clicking a row opens a right-hand detail panel that renders everything needed for a spot audit without leaving the browser.

  • Decision badge (Allow, Deny, Canceled, Incomplete).
  • Tool server and tool name, plus a formatted timestamp.
  • Full capability ID.
  • Financial metadata when present: cost charged, budget remaining, total budget, delegation depth, settlement status. All formatted from minor units using the receipt's own currency field.
  • Agent subject key when attribution metadata is present.
  • Cost-over-time sparkline, aggregated per day by the backend analytics API for the receipt subject.
  • Raw tool-call parameters.
  • Full receipt JSON for copy-paste into tickets.

Delegation chain view

The DelegationChain component fetches GET /v1/lineage/{capability_id}/chain and renders every CapabilitySnapshot in the chain from root to leaf. Each snapshot shows subject_key, issuer_key, issued_at, expires_at, delegation_depth, and the raw grants JSON. Operators can trace exactly how a capability was delegated and attenuated before it reached the agent.

Budget sparkline

When the selected receipt has financial metadata, the panel renders a BudgetSparkline backed by GET /v1/receipts/analytics?agentSubject=...&timeBucket=day. It uses the receipt's attribution subject key, falling back to capability lineage when attribution metadata is absent. The chart is inline SVG, which keeps the dashboard bundle small without pulling in a charting framework.


Portable Reputation Comparison

When the Agent Subject filter is set, an extra panel appears above the receipt table. Operators can upload a passport JSON document and the dashboard calls POST /v1/reputation/compare/{subject_key} on the trust-control service.

The panel renders:

  • Subject match vs mismatch between the live local subject key and the passport subject DID.
  • Current local effective score and resolved tier.
  • Optional relying-party acceptance state when the uploaded passport document contains a verifier-evaluation result.
  • Per-credential drift for composite score, reliability, delegation hygiene, and resource stewardship.
  • Shared-evidence provenance: partner and share IDs, remote capability IDs, local anchor capability IDs, and local receipt counts.

Receipt-store data

The browser uses the same comparison contract as chio reputation compare --control-url .... There is no second local scoring path embedded in the dashboard.

Shared Evidence API

Trust-control exposes GET /v1/federation/evidence-shares, which returns the same shared-evidence report shape used inside operator reports and portable reputation comparison. The CLI wrapper is:

bash
$ chio trust evidence-share list \
    --agent-subject <subject-hex> \
    --json

This surfaces imported remote evidence references directly without merging foreign receipts into native local receipt history. The dashboard uses the same endpoint for its shared-evidence summary card.


Configuration Reference

chio trust serve is flag-driven; there is no separate config file. The dashboard rides on the same address as the API, so configuration is a handful of flags:

FlagDefaultNotes
--listen127.0.0.1:8940Bind address for both the API and the dashboard SPA
--service-tokenrequiredBearer token; prefer CHIO_TRUST_SERVICE_TOKEN so it stays out of ps//proc
--receipt-dbGlobal flag; path to the receipt SQLite database
--authority-dbGlobal flag; authority store path
--revocation-dbGlobal flag; revocation store path
bash
# --service-token has no default, so the server cannot start without it.
$ export CHIO_TRUST_SERVICE_TOKEN=$(cat ./secrets/chio_service_token.txt)
$ chio trust serve \
    --listen 127.0.0.1:8940 \
    --receipt-db ./state/receipts.sqlite \
    --authority-db ./state/authority.sqlite \
    --revocation-db ./state/revocations.sqlite

For deeper queries on individual receipts, consult the receipt format reference. The dashboard renders the exact same struct, field for field.