Chio/Docs

Trust Control Plane

The smallest example that exercises the trust-control HTTP surface end to end. No app, no MCP edge, no agents. Start chio trust serve, issue a capability, query it, revoke it, mint one receipt with chio check, export the evidence, and verify it offline.

Where the code lives

examples/hello-trust-control/. Run ./smoke.sh for the full flow or ./run-trust.sh to start trust-control alone for manual experimentation.

What It Shows

  • Capability issuance through the shared trust-control HTTP API.
  • Capability status and revocation through the chio CLI.
  • Receipt creation without any HTTP app or framework, using chio check.
  • Offline evidence export and verification with chio evidence export and chio evidence verify.
  • Bounded staleness: a revocation through trust-control is observed by every subsequent kernel call.

Files

text
examples/hello-trust-control/
  README.md
  policy.yaml      minimal HushSpec: allow read_file only
  run-trust.sh     start trust-control alone (port 8051 by default)
  smoke.sh         full issue / revoke / receipt / export / verify flow

Run It

bash
# From the chio workspace root
cargo build --bin chio
cd examples/hello-trust-control

# Full smoke: issue, revoke, mint receipt, export, verify
./smoke.sh

# Or: start trust-control alone for manual exploration
./run-trust.sh
# trust-control listens on 127.0.0.1:8051
# state under .artifacts/manual-state/

On a successful run the smoke prints:

text
hello-trust-control smoke passed
artifacts: .../examples/hello-trust-control/.artifacts/<timestamp>
capability id: <id>
receipt id: <id>
evidence dir: .../evidence

Phase 1: Start Trust-Control

The smoke launches chio trust serve on a free port with all four sqlite stores rooted in the run's artifact directory:

examples/hello-trust-control/smoke.sh
"${CHIO_BIN}" trust serve \
  --listen "127.0.0.1:${TRUST_PORT}" \
  --service-token "${SERVICE_TOKEN}" \
  --receipt-db "${TRUST_RECEIPT_DB}" \
  --revocation-db "${REVOCATION_DB}" \
  --authority-db "${AUTHORITY_DB}" \
  --budget-db "${BUDGET_DB}"

Once /health returns 200, trust-control is ready to accept capability operations.


Phase 2: Issue a Capability

The shared helper issue_demo_capability posts to /v1/capabilities/issue and writes the capability JSON to disk. The example then materializes a wire-format token (the JSON the kernel passes through the X-Chio-Capability header) under capability.token.

The request body the helper posts:

POST /v1/capabilities/issue
{
  "subjectPublicKey": "0000000000000000000000000000000000000000000000000000000000000000",
  "scope": {
    "grants": [
      {
        "server_id": "http-sidecar-client",
        "tool_name": "hello_trust_control_invoke",
        "operations": ["invoke"],
        "constraints": []
      }
    ],
    "resource_grants": [],
    "prompt_grants": []
  },
  "ttlSeconds": 3600
}

The capability ID is parsed out of the response for the next steps:

smoke.sh
CAPABILITY_ID="$(python3 - "${ARTIFACT_ROOT}/capability.json" <<'PY'
import json, sys
from pathlib import Path
payload = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
print(payload["capability"]["id"])
PY
)"

Phase 3: Query Status

chio trust status hits GET /v1/capabilities/<id> through the CLI. The smoke asserts that the freshly issued capability is not yet revoked:

smoke.sh
"${CHIO_BIN}" \
  --control-url "${CONTROL_URL}" \
  --control-token "${SERVICE_TOKEN}" \
  trust status \
  --capability-id "${CAPABILITY_ID}" \
  --json \
  > "${ARTIFACT_ROOT}/status-before.json"

# Asserts: payload["revoked"] is False

Phase 4: Revoke

chio trust revoke writes an idempotent revocation record. The first call returns {"revoked": true, "newly_revoked": true}; subsequent calls return "newly_revoked": false. The smoke asserts the first-call shape and re-queries status to confirm the revocation is now visible:

smoke.sh
"${CHIO_BIN}" \
  --control-url "${CONTROL_URL}" \
  --control-token "${SERVICE_TOKEN}" \
  trust revoke \
  --capability-id "${CAPABILITY_ID}" \
  --json \
  > "${ARTIFACT_ROOT}/revoke.json"

# Asserts: payload["revoked"] is True
# Asserts: payload["newly_revoked"] is True

"${CHIO_BIN}" \
  --control-url "${CONTROL_URL}" \
  --control-token "${SERVICE_TOKEN}" \
  trust status \
  --capability-id "${CAPABILITY_ID}" \
  --json \
  > "${ARTIFACT_ROOT}/status-after.json"

# Asserts: payload["revoked"] is True

Bounded staleness, not eventual consistency

Edge kernels re-check revocation at the start of every tool call against trust-control or the local revocation store. There is no stale-decision window on the critical path. See Trust Control Plane for the cluster repair-sync interval that bounds cross-node propagation.

Phase 5: Mint a Receipt

Receipts are minted by running an evaluation. The example uses chio check, which runs the same kernel pipeline against a CLI-supplied tool name and params, signs the verdict, and persists it to a receipt sqlite store of your choosing.

smoke.sh
"${CHIO_BIN}" check \
  --policy "${EXAMPLE_ROOT}/policy.yaml" \
  --tool read_file \
  --params '{"path":"README.md"}' \
  --receipt-db "${CHECK_RECEIPT_DB}" \
  --json \
  > "${ARTIFACT_ROOT}/check.json"

The policy is intentionally narrow: deny by default, allow only the read_file tool.

examples/hello-trust-control/policy.yaml
hushspec: "0.1.0"
name: hello-trust-control
description: Allow one narrow read-style tool so a receipt can be minted without an app surface.

rules:
  tool_access:
    enabled: true
    default: block
    allow:
      - read_file

The smoke asserts every required field on the receipt: tool name, verdict (allow), receipt ID, policy hash.


Phase 6: Export + Verify Offline

chio evidence export bundles the receipt store into an offline package: receipts as NDJSON, capability lineage, child receipts, inclusion proofs, a manifest with SHA-256 hashes, and a retention record. The example then runs chio evidence verify against the package without contacting trust-control:

smoke.sh
"${CHIO_BIN}" evidence export \
  --receipt-db "${CHECK_RECEIPT_DB}" \
  --output "${EVIDENCE_DIR}"

"${CHIO_BIN}" evidence verify \
  --input "${EVIDENCE_DIR}" \
  --json \
  > "${ARTIFACT_ROOT}/verify.json"

# Asserts: verify["toolReceipts"] == 1
# Asserts: verify["verifiedFiles"] >= 1

Inspect the Artifacts

bash
cd .artifacts/<timestamp>

# Capability JSON and the wire-format token
cat capability.json
cat capability.token

# Status before and after revocation
cat status-before.json   # revoked: false
cat revoke.json          # revoked: true, newly_revoked: true
cat status-after.json    # revoked: true

# Minted receipt and evidence
cat check.json
cat receipts.ndjson
ls evidence/
cat evidence/manifest.json
cat verify.json
cat summary.json

# Persistent state
ls state/
# trust-receipts.sqlite3      receipts ingested by trust-control
# trust-revocations.sqlite3   revocation table
# trust-authority.sqlite3     authority signing seed + history
# trust-budgets.sqlite3       (unused in this example, present for parity)
# check-receipts.sqlite3      receipts written by chio check

Manual Experimentation

run-trust.sh starts trust-control alone on 127.0.0.1:8051 with state under .artifacts/manual-state/. Once running you can drive the same flow by hand:

bash
# Terminal 1: start trust-control
./run-trust.sh

# Terminal 2: drive the flow
export CHIO_CONTROL_URL=http://127.0.0.1:8051
export CHIO_SERVICE_TOKEN=demo-token

# Issue
curl -sS -H "Authorization: Bearer $CHIO_SERVICE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"subject_pk":"0000...0000","scope":{"grants":[],"resource_grants":[],"prompt_grants":[]},"ttl":3600}' \
  $CHIO_CONTROL_URL/v1/capabilities/issue | jq

# Status
chio --control-url $CHIO_CONTROL_URL --control-token $CHIO_SERVICE_TOKEN \
  trust status --capability-id <id> --json | jq

# Revoke
chio --control-url $CHIO_CONTROL_URL --control-token $CHIO_SERVICE_TOKEN \
  trust revoke --capability-id <id> --json | jq

Bounded staleness on the kernel side

Edge kernels honor a revocation as soon as the next call hits the revocation store. If you run an edge kernel against this same trust-control, revoking through this example denies subsequent calls within the kernel's revocation re-check window. The deployment doc covers the cluster sync interval for multi-node setups.

Smoke Assertions

The smoke checks every phase produced the right shape. The load-bearing python assertions:

smoke.sh (assertions)
# After issue + status query
assert payload["capability_id"], payload
assert payload["revoked"] is False, payload

# After revoke
assert payload["revoked"] is True, payload
assert payload["newly_revoked"] is True, payload

# After chio check
assert payload["tool"] == "read_file", payload
assert payload["verdict"].lower() == "allow", payload
assert payload["receipt_id"], payload
assert payload["policy_hash"], payload

# After evidence verify
assert payload["toolReceipts"] == 1, payload
assert payload["verifiedFiles"] >= 1, payload

Decision rule

Use this example when you want to drive trust-control directly: issue, revoke, mint a receipt, and verify offline evidence without an HTTP app. Pick hello-tool instead when you want trust-control plus a real tool surface with capability binding on every call. Pick federation-bootstrap when you need authority signing across two kernels with a signed bilateral policy.

Where to read more

Trust Control Plane for HA topology, leader rule, replication, and key rotation. Rotate Keys and Revoke for the operator playbook.
Trust Control Example · Chio Docs