Chio/Docs

Agent Commerce Network

A buyer org procures a security review from a provider org. Every boundary is mediated by chio: a capability with a budget, a buyer API sidecar, a provider MCP edge, signed receipts on both sides bound to the same governed intent, and a settlement reconciliation record. This page is the runnable companion to Worked Example: Agent Commerce Network, which narrates the same flow.

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 a live agent loop, or leave both unset for the 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 every 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 kernels emit receipts that bind to the same governed transaction intent. The pair is the bilateral evidence record.

Architecture

rendering…
Three chio control surfaces frame the procurement flow. The orchestrator talks to trust-control to mint a capability, presents it on every buyer API call, and the buyer sidecar forwards through the provider MCP edge into the review tool.

Service Inventory

smoke.sh picks free ports for every process. 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, default 8940
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

Every long-running process writes its log under artifacts/live/<timestamp>/logs/ and its sqlite state under the matching state/ directory. The cleanup trap kills every background pid 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: Governed Intent

The buyer wraps the quote in a GovernedTransactionIntent with a metered billing context. The intent hash anchors both the buyer-side and the provider-side receipt. Three settlement modes are available; the example uses AllowThenSettle through the trust-control split-budget endpoints.

Before dispatch, the buyer kernel calls /v1/budgets/authorize-exposure with the worst-case price. If that would breach maxTotalCost or maxInvocations, the call denies and the buyer service surfaces denied_budget:

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: Receipt Pair

Each kernel emits its own signed receipt. Both share the same intent_hash, which is what makes the pair a bilateral artifact. The buyer sidecar writes to state/buyer-receipts.sqlite3; trust-control accumulates trust receipts in state/trust-receipts.sqlite3.

The governed intent is the input to both signatures. It carries the capability id, the metered billing context, and the worst-case price the buyer kernel pre-charges before dispatch:

GovernedTransactionIntent (in-memory)
{
  "intent_id": "intent_lattice_vanguard_001",
  "capability_id": "cap-019d921b-...-3e9-...",
  "buyer_org": "lattice-platform-security",
  "provider_org": "vanguard-security",
  "tool_server": "provider-security-review",
  "tool_name": "execute_review",
  "settlement_mode": "AllowThenSettle",
  "billing_context": {
    "scheme": "metered",
    "quote_id": "quote_vanguard_001",
    "max_price_minor": 125000,
    "currency": "USD"
  },
  "intent_hash": "sha256:7bf4...c0a1"
}

Both sides emit a receipt that commits to intent_hash. The buyer kernel signs its receipt with the buyer kernel public key; the provider kernel signs with its own. The pair is the bilateral evidence record:

state/buyer-receipts.sqlite3 (one row, decoded)
{
  "id": "rcpt-019d921b-buyer-...",
  "timestamp": 1776272775,
  "capability_id": "cap-019d921b-...-3e9-...",
  "intent_hash": "sha256:7bf4...c0a1",
  "tool_server": "http-sidecar-client",
  "tool_name": "procurement_job_write",
  "decision": {"verdict": "allow"},
  "kernel_key": "f257309e...d6ed0",
  "signature": "3d86c9f9...3e107",
  "settlement_status": "Charged"
}
state/trust-receipts.sqlite3 (provider-side row, decoded)
{
  "id": "rcpt-019d921b-provider-...",
  "timestamp": 1776272776,
  "capability_id": "cap-019d921b-...-3e9-...",
  "intent_hash": "sha256:7bf4...c0a1",
  "tool_server": "provider-security-review",
  "tool_name": "execute_review",
  "decision": {"verdict": "allow"},
  "kernel_key": "8c1a2bf3...07e29",
  "signature": "9e0a4d61...88e44"
}

Both rows commit to the same intent_hash. The verifier loads both rows, recomputes the hash from the buyer intent, and confirms the provider receipt commits to the same hash under a different kernel key. Different signers, same fact.

Two receipts, one binding

See Bilateral Receipts for the verification rule: load both receipts, recompute the intent hash from one side, and confirm the other side commits to the same hash under its own kernel signature.

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 receipt's settlement_status is set to Failed and the overrun is recorded.

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 a real EVM, Solana, CCIP, or x402 rail.


Inspect the Artifacts

Every run drops 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 artifact directory, recomputes hashes, and confirms every required artifact is present. 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 any required artifact is missing or any hash recompute 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 every contract under contracts/ exists and parses, that the quote-response's price_minor is consistent with the settlement-reconciliation's quoted_amount_minor, and that the receipt id chain is unbroken from the buyer side through to the provider side.


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 SETTLEMENT_DB="$(pwd)/state/trust-budgets.sqlite3"
export BUYER_DB="$(pwd)/state/buyer-receipts.sqlite3"

# Five most recent receipts trust-control ingested
sqlite3 "$TRUST_RECEIPT_DB" \
  'select id, tool_server, tool_name, verdict, created_at \
   from receipts order by created_at desc limit 5;'

# Settlement rows in the budget store
sqlite3 "$SETTLEMENT_DB" \
  'select capability_id, status, amount_charged, amount_settled \
   from settlements;'

# Buyer-side receipts
sqlite3 "$BUYER_DB" \
  'select id, tool_name, verdict, intent_hash from receipts;'

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

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

Expected output (with one happy-path run):

text
# trust-receipts: two rows, one per kernel
rcpt-...buyer|http-sidecar-client|procurement_job_write|allow|1776272775
rcpt-...provider|provider-security-review|execute_review|allow|1776272776

# settlements: one reconciled row at the quoted amount
cap-019d921b-...|reconciled|125000|125000

# buyer-receipts: same intent_hash on the buyer row
rcpt-...buyer|procurement_job_write|allow|sha256:7bf4...c0a1

# 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

Three branches the agent can fall down. Each is intentional and each leaves an artifact:

  • Budget denial: requested work exceeds the envelope. The buyer service stamps the job as denied_budget; the pre-charge never lands.
  • Pending approval: the quote tripped approval_required. The agent must call approve_job with a reason. Trust-control records the approval in the receipt metadata before the dispatch proceeds.
  • 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 bilateral receipt pair. 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 is the long-form narrative for this same flow. Manifests covers the signed manifest the provider publishes. Pricing explains how quotes compose into MeteredBillingQuote. Bilateral Receipts covers receipt-pair verification.
Agent Commerce Network · Chio Docs