Chio/Docs

BuildIntegrated Examples

Agent Commerce Network

Run a budgeted security-review purchase between buyer and provider organizations with signed receipts at both boundaries.

Where the code lives

The example sits in examples/agent-commerce-network/. Run it with ./smoke.sh. Set OPENAI_API_KEY or ANTHROPIC_API_KEY to use an external-model agent loop. With neither variable set, the smoke uses its deterministic CI fallback.

What it shows

  • A buyer agent in Org A (lattice-platform-security) procures a service from a provider agent in Org B (vanguard-security).
  • A capability token carries a free quote-read grant and a budgeted job-write grant (maxInvocations, maxCostPerInvocation, maxTotalCost).
  • The buyer sidecar (chio api protect) signs a receipt for each API call.
  • The provider edge (chio mcp serve-http) verifies the capability, enforces the policy, and signs its own receipt.
  • Trust-control tracks budget consumption, revocations, and financial reports (budget usage, exposure ledger, settlement).
  • Both boundaries emit signed receipts: the buyer sidecar for each API call, the provider edge for each tool call. The run bundle captures the resulting receipts.

Architecture

rendering…
The orchestrator mints a capability through trust-control, sends it with buyer API calls, and reaches the review tool through the buyer sidecar and provider MCP edge.

Service inventory

smoke.sh picks free ports for its processes. The shape is fixed; the numbers vary per run. Port discovery uses the shared pick_free_port helper that opens a socket on port 0, reads back the OS-assigned port, and prints it. Each call returns a fresh free port:

examples/_shared/hello-http-common.sh
pick_free_port() {
  python3 - <<'PY'
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    sock.bind(("127.0.0.1", 0))
    print(sock.getsockname()[1])
PY
}

# smoke.sh then assigns four ports up front:
TRUST_PORT="$(pick_free_port)"
PROVIDER_PORT="$(pick_free_port)"     # provider edge, default 8931
BUYER_API_PORT="$(pick_free_port)"
BUYER_SIDECAR_PORT="$(pick_free_port)" # buyer sidecar (chio api protect), default 9090
ProcessCommandListens on
trust-controlchio trust serve127.0.0.1:$TRUST_PORT
provider edgechio mcp serve-http wrapping provider/review_server.py127.0.0.1:$PROVIDER_PORT
buyer FastAPIuvicorn buyer.app:app127.0.0.1:$BUYER_API_PORT
buyer sidecarchio api protect over the buyer FastAPI127.0.0.1:$BUYER_SIDECAR_PORT
orchestratorpython orchestrate.pyno listen; CLI client

Each long-running process writes logs under artifacts/live/<timestamp>/logs/ and its sqlite state under the matching state/ directory. The cleanup trap kills the background processes on exit.


Run It

bash
# From the chio workspace root
cargo build --bin chio
cd examples/agent-commerce-network
./smoke.sh

# Optional: live agent loop instead of the deterministic fallback
export OPENAI_API_KEY=...        # OpenAI Agents SDK path
# or
export ANTHROPIC_API_KEY=...     # Anthropic SDK path
./smoke.sh

On a successful run the script prints two lines:

bash
agent-commerce-network smoke passed
artifacts: /path/to/examples/agent-commerce-network/artifacts/live/<timestamp>

Phase 1: Quote

The orchestrator first asks trust-control for a capability with two grants. Read calls (procurement_quote_read) are free; write calls (procurement_job_write) carry the budget envelope.

The buyer agent posts a quote-request to the provider edge. Both sides are JSON. Quote-request body:

contracts/quote-request.json
{
  "quote_id": "quote_req_lattice_001",
  "buyer_id": "lattice-platform-security",
  "provider_id": "vanguard-security",
  "service_family": "security-review",
  "requested_scope": "release-review",
  "target": "git://lattice.example/payments-api",
  "release_window": "2026-05-01T16:00:00Z"
}

Quote-response body:

contracts/quote-response.json
{
  "quote_id": "quote_vanguard_001",
  "request_id": "quote_req_lattice_001",
  "service_family": "security-review",
  "offer_id": "release-review",
  "price_minor": 125000,
  "currency": "USD",
  "approval_required": true,
  "estimated_delivery_hours": 48,
  "pricing_basis": "bounded release review for one release window"
}
examples/agent-commerce-network/orchestrate.py
cap = trust.issue_capability(
    subject_pk="00" * 32,
    scope={
        "grants": [
            {"server_id": "http-sidecar-client",
             "tool_name": "procurement_quote_read",
             "operations": ["invoke"], "constraints": []},
            {"server_id": "http-sidecar-client",
             "tool_name": "procurement_job_write",
             "operations": ["invoke"], "constraints": [],
             "maxInvocations": 3,
             "maxCostPerInvocation": _usd(args.budget_minor),
             "maxTotalCost": _usd(args.budget_minor)},
        ],
        "resource_grants": [], "prompt_grants": [],
    },
    ttl=3600,
)

The agent then calls POST /procurement/quote-requests on the buyer sidecar, which forwards to the provider edge. The provider responds with a per-call quote and the approval_required flag flips when the quote exceeds 100,000 cents.

contracts/quote-response.json (template)
{
  "quote_id": "quote_vanguard_001",
  "request_id": "quote_req_lattice_001",
  "service_family": "security-review",
  "offer_id": "release-review",
  "price_minor": 125000,
  "currency": "USD",
  "approval_required": true,
  "estimated_delivery_hours": 48,
  "pricing_basis": "bounded release review for one release window"
}

Expected log lines (provider edge):

text
chio.mcp.serve-http: tools/call request_quote -> allow
chio.mcp.serve-http: receipt receipt_<id> sealed (verdict=allow)

Phase 2: Job Creation and Budget Check

The buyer service turns the quote into a job and checks it against the budget envelope client-side, before anything dispatches. If the job budget is below the quoted price it stamps denied_budget; if the quote tripped the approval threshold it stamps pending_approval; otherwise it executes. The grant's maxInvocations / maxCostPerInvocation / maxTotalCost caps are enforced separately by trust-control as the buyer sidecar signs each call.

examples/agent-commerce-network/buyer/app.py
if budget_minor < quote["price_minor"]:
    job["status"] = "denied_budget"
    job["denial_reason"] = "requested work exceeds the buyer budget envelope"
elif quote["approval_required"]:
    job["status"] = "pending_approval"
else:
    self._execute_job(job)

Phase 3: Dispatch

The buyer issues an MCP tools/call for execute_review. The capability rides on the request as the X-Chio-Capability header, and the auth bearer rides on Authorization: Bearer ...:

examples/agent-commerce-network/commerce_network/agents.py
headers: dict[str, str] = {"Authorization": f"Bearer {auth_token}"}
if cap_header:
    headers["X-Chio-Capability"] = cap_header
r = http.post(f"{buyer_url}{path}", headers=headers, json=body)

The provider edge runs its policy pipeline and forwards into the review tool. The tool returns a fulfillment package:

contracts/fulfillment-package.json (template)
{
  "fulfillment_id": "fulfillment_vanguard_001",
  "job_id": "job_lattice_001",
  "service_family": "security-review",
  "deliverables": [
    "executive-summary.md",
    "findings.json",
    "remediation-checklist.md"
  ],
  "status": "completed_with_findings",
  "severity_summary": {"critical": 0, "high": 2, "medium": 5, "low": 7}
}

Phase 4: Receipts

Each boundary signs its own receipt. The buyer sidecar ( chio api protect) writes an HTTP receipt for each call it mediates into state/buyer-receipts.sqlite3 (one row per receipt in the http_receipts table). The provider MCP edge signs a receipt for each tools/call it admits. Trust-control ingests receipts into state/trust-receipts.sqlite3 (the chio_tool_receipts table). The run bundle captures all of them: the evidence is the set of independently signed receipts across both orgs, each under its own kernel signature.

Peek at the two stores directly. Route, method, and verdict live inside the receipt blob rather than in dedicated columns:

bash
# Trust-control ingested receipts: one row per admitted call
sqlite3 state/trust-receipts.sqlite3 \
  'select receipt_id, tool_server, tool_name, decision_kind from chio_tool_receipts order by seq;'

# Buyer sidecar HTTP receipts: id plus the signed receipt blob
sqlite3 state/buyer-receipts.sqlite3 \
  'select id, receipt_json from http_receipts order by rowid;'

Bilateral intent binding is a protocol feature, not this example

The protocol's GovernedTransactionIntent binding, where a buyer and provider receipt each commit to one shared intent hash, is documented under Bilateral Receipts. This runnable example keeps the flow simpler: a capability grant with a budget envelope, a client-side budget check, and independently signed receipts on each side.

Phase 5: Reconcile

After dispatch, the buyer kernel calls /v1/budgets/reconcile-spend with the actual cost. Three outcomes:

  • Actual < charged: the difference is credited back to the grant.
  • Actual = charged: no adjustment.
  • Actual > charged: the overrun is recorded against the grant.

Phase 6: Settle

In the example the settlement rail is stubbed and returns a synthetic settlement_id:

contracts/settlement-reconciliation.json (template)
{
  "settlement_id": "settlement_lattice_vanguard_001",
  "job_id": "job_lattice_001",
  "quoted_amount_minor": 125000,
  "approved_amount_minor": 125000,
  "settled_amount_minor": 125000,
  "currency": "USD",
  "status": "reconciled",
  "buyer_position": "accepted",
  "provider_position": "accepted"
}

See On-chain Settlement and Pricing for how the payment_reference field on the receipt connects to an EVM, Solana, CCIP, or x402 settlement rail.


Inspect Run Files

Each run creates a directory under artifacts/live/<timestamp>/:

bash
cd artifacts/live/<timestamp>

# Capability that drove the run
cat capability.json

# What the agent decided and which tools it called
cat agent-output.json
cat summary.json

# Contracts captured from the agent's tool calls
ls contracts/
cat contracts/quote-response.json
cat contracts/fulfillment-package.json
cat contracts/settlement-reconciliation.json

# Trust-control financial reports
cat financial/budget-usage.json
cat financial/exposure-ledger.json
cat financial/settlement-report.json

# Persistent state per service
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       grant usage records
# buyer-receipts.sqlite3      receipts written by chio api protect
# provider-sessions.sqlite3   provider edge session state

# Per-process logs
ls logs/
# trust.log              chio trust serve
# provider-edge.log      chio mcp serve-http
# buyer-api.log          uvicorn buyer.app
# buyer-sidecar.log      chio api protect

The smoke also runs the in-tree verifier: commerce_network.verify.verify_bundle reads the run directory, confirms the required files is present, that each contract parses, and that the agent and summary final_status agree. Its output lands in review-result.json; the smoke aborts if ok is false.


Smoke Assertions

The smoke runs the in-tree verifier as its last step. The post-orchestrator block in smoke.sh aborts the run if a required file is missing or a check fails:

examples/agent-commerce-network/smoke.sh
uv run --project "${EXAMPLE_ROOT}" python -c "
import sys; sys.path.insert(0, '${EXAMPLE_ROOT}')
from commerce_network.verify import verify_bundle
import json
r = verify_bundle('${ARTIFACT_ROOT}')
json.dump(r, open('${ARTIFACT_ROOT}/review-result.json', 'w'), indent=2)
assert r['ok'], r['errors']
"

printf 'agent-commerce-network smoke passed\n'
printf 'artifacts: %s\n' "${ARTIFACT_ROOT}"

verify_bundle checks that agent-output.json, the three contracts, and summary.json all exist; that the agent's final_status matches summary.json; that each contract under contracts/ parses as JSON; and that no entry in financial/budget-usage.json records a negative totalCostCharged. There is no price-consistency check and no receipt-chain check.


Inspect After

Set the env vars to the run's sqlite paths and walk the state with the standard CLI tooling:

bash
cd artifacts/live/<timestamp>
export TRUST_RECEIPT_DB="$(pwd)/state/trust-receipts.sqlite3"
export BUYER_DB="$(pwd)/state/buyer-receipts.sqlite3"

# Five most recent receipts trust-control ingested
sqlite3 "$TRUST_RECEIPT_DB" \
  'select receipt_id, tool_server, tool_name, decision_kind from chio_tool_receipts order by seq desc limit 5;'

# Buyer-side HTTP receipts: id plus the signed receipt blob
sqlite3 "$BUYER_DB" \
  'select id, receipt_json from http_receipts order by rowid;'

# Budget consumption, from the trust-control financial report
jq '.' financial/budget-usage.json

# Pricing slice of the quote contract
jq '{price_minor, currency, approval_required}' contracts/quote-response.json

# Settlement reconciliation, bilateral position
jq '{status, buyer_position, provider_position, settled_amount_minor}' \
  contracts/settlement-reconciliation.json

Expected output (with one happy-path run):

text
# trust-receipts (receipt_id|tool_server|tool_name|decision_kind)
rcpt-...|http-sidecar-client|procurement_quote_read|allow
rcpt-...|provider-security-review|execute_review|allow

# buyer-receipts: each row is (id, receipt_json); route, method, and
# verdict are nested inside receipt_json (verdict at .verdict.verdict)

# pricing slice
{
  "price_minor": 125000,
  "currency": "USD",
  "approval_required": true
}

# settlement bilateral
{
  "status": "reconciled",
  "buyer_position": "accepted",
  "provider_position": "accepted",
  "settled_amount_minor": 125000
}

Failure Paths

The example has three intentional failure branches. Each writes a run file:

  • Budget denial: requested work exceeds the envelope. The buyer service stamps the job as denied_budget; it does not dispatch work to the provider.
  • Pending approval: the quote tripped approval_required. The agent must call approve_job with a reason; the dispatch proceeds only after the approval is recorded on the job.
  • Dispute: after fulfillment, the agent calls dispute_job. The settlement record flips to reversal_pending.

Use this example when...

Two organizations are exchanging value and you need a budgeted capability, an MCP edge on the provider, an API sidecar on the buyer, and a signed receipt on each side. For a single-org flow with budgets and a tool sidecar, see hello-tool / http-node. For settlement that lands on chain (Base Sepolia), see Web3 Network.

Where to read more

Worked Example describes the same flow in more detail. Manifests covers the signed manifest the provider publishes. Pricing explains how quotes compose into MeteredBillingQuote. Bilateral Receipts covers receipt-pair verification.