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 exportandchio evidence verify. - Bounded staleness: a revocation through trust-control is observed by every subsequent kernel call.
Files
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 flowRun It
# 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:
hello-trust-control smoke passed
artifacts: .../examples/hello-trust-control/.artifacts/<timestamp>
capability id: <id>
receipt id: <id>
evidence dir: .../evidencePhase 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:
"${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:
{
"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:
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:
"${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 FalsePhase 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:
"${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 TrueBounded staleness, not eventual consistency
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.
"${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.
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_fileThe 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:
"${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"] >= 1Inspect the Artifacts
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 checkManual 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:
# 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 | jqBounded staleness on the kernel side
Smoke Assertions
The smoke checks every phase produced the right shape. The load-bearing python 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, payloadDecision rule
Where to read more