Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,25 @@ jobs:

- name: Run people directory tests
run: python -m unittest discover -s tests -v

ca2a-delegation:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ca2a-delegation
steps:
- uses: actions/checkout@v7

- name: Set up Python 3.11
uses: actions/setup-python@v6
with:
python-version: "3.11"

- name: Install cA2A runtime (pre-release)
run: python -m pip install --pre ca2a-runtime

- name: Run delegation tests
run: python -m unittest discover -s tests -v

- name: Run the delegation demo
run: python delegation_agent.py
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
[![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
[![cMCP](https://img.shields.io/badge/Uses-cMCP_Runtime-7c3aed)](https://github.com/agentrust-io/cmcp)
[![Agent Manifest](https://img.shields.io/badge/Uses-Agent_Manifest-0ea5e9)](https://github.com/agentrust-io/agent-manifest)
[![cA2A](https://img.shields.io/badge/Uses-cA2A-7c3aed)](https://github.com/agentrust-io/ca2a)
[![Discord](https://dcbadge.limes.pink/api/server/9JWNpH7E?style=flat)](https://discord.gg/9JWNpH7E)

# agentrust-io Examples

End-to-end integration examples showing cMCP, Agent Manifest, and TRACE working together across deployment scenarios. Each example is self-contained and runnable on a fresh cloud VM. Running them shows how the three projects compose: cMCP enforces policy at the tool call boundary, Agent Manifest carries the identity and capability declaration, and TRACE emits a signed Trust Record for every tool invocation so you can see what the full audit trail looks like in practice.
End-to-end integration examples showing cMCP, Agent Manifest, and TRACE working together across deployment scenarios. Each example is self-contained and runnable on a fresh cloud VM. Running them shows how the projects compose: cMCP enforces policy at the tool call boundary, Agent Manifest carries the identity and capability declaration, cA2A attenuates authority at the agent-to-agent boundary, and TRACE emits a signed Trust Record for every tool invocation so you can see what the full audit trail looks like in practice.

## Examples

| Example | What it shows | Platform | Compliance |
|---|---|---|---|
| `ca2a-delegation/` | Agent-to-agent delegation with cA2A: attenuated credit-workflow chain, offline verification, and a rejected scope escalation | Software-only (offline) | Separation of duties, least authority |
| `embodied-action-receipts/` | Fixture-style offline verification for embodied action receipts: accepted chain, missing receipt, signature mismatch and valid controller rejection | Software-only fixtures | TRACE action-receipt evidence boundary |
| `financial-services/` | Corporate credit risk agent: six-step assessment with CDD, exposure and IFRS 9 guardrails on the write | SEV-SNP / TDX | EU AI Act Art. 9/12, CRR Art. 395, EBA/GL/2020/06, EU AML, DORA Art. 9 |
| `healthcare/` | Clinical agent on a coherent ICD-10 patient: drug-interaction check feeds EU AI Act Art. 14 HITL and contraindication denies | SEV-SNP / TDX | EU AI Act Art. 14, HIPAA |
| `industrial-embodied-ai/` | Material-movement agent with cMCP authorization, an independent safety-controller boundary and offline-verifiable closed-session evidence | TEE / software-only development mode | OT security and industrial robot safety references |
| `multi-tenant-saas/` | HR SaaS with an EU tenant (enforcing GDPR residency/Art. 9) and a US tenant (advisory) on one catalog | TDX | GDPR Art. 6/9/44, customer DPA |
| `startup-tpm/` | 15-minute quickstart on any cloud VM with Trusted Launch | TPM 2.0 | Development / staging |

Each example is fully runnable with no external dependencies: it ships a mock upstream MCP server, an agent script, an attested tool catalog, and a Cedar policy bundle, and ends by printing the signed TRACE Trust Record for the session. The `trace-output/` files in each example are captured from real runs.
Most examples are fully runnable with no external dependencies: they ship a mock upstream MCP server, an agent script, an attested tool catalog, and a Cedar policy bundle, and end by printing the signed TRACE Trust Record for the session. The `trace-output/` files are captured from real runs. The `ca2a-delegation/` and `embodied-action-receipts/` examples are offline verifiers and ship their captured chains instead.

## Quickstart

Expand Down
114 changes: 114 additions & 0 deletions ca2a-delegation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# ca2a-delegation: The Agent-to-Agent Boundary

The other examples in this repo govern the **agent-to-tool** boundary: cMCP decides what a *single* agent may call. This example governs the **agent-to-agent** boundary with [cA2A](https://github.com/agentrust-io/ca2a): when a lead agent hands part of a task to a sub-agent, each hop carries a signed delegation credential whose scope is a provable subset of its parent, so authority can only ever narrow as it flows outward.

It uses the credit-risk workflow from [`financial-services/`](../financial-services/README.md) as the worked case, and maps the same pattern onto the other examples at the bottom.

> **Scope of what runs here.** cA2A is in alpha. This example exercises the part that is built today: **attenuated delegation credentials and offline chain verification** (`ca2a_runtime.delegation`). The live peer path (attesting an inbound peer, sealing the payload to its measurement) and the per-hop TRACE provenance record are cA2A roadmap. See the [cA2A ROADMAP](https://github.com/agentrust-io/ca2a/blob/main/ROADMAP.md) and [LIMITATIONS](https://github.com/agentrust-io/ca2a/blob/main/LIMITATIONS.md).

---

## The scenario

A credit assessment is decomposed across three agents. The authority to **write the risk report** is the sensitive one: it is granted to the lead agent but is deliberately never delegated onward, so no sub-agent can regain it.

```
credit-platform
│ grants: read:documents, screen:sanctions, read:bureau, run:risk-model, write:risk-report
lead-credit-agent
│ delegates (evidence-gathering only): read:documents, screen:sanctions, read:bureau
│ WITHHELD: run:risk-model, write:risk-report
screening-sub-agent
│ delegates (narrowest): read:bureau
bureau-connector
```

Each hop's scope is a subset of its parent's; each hop's issuer is the previous hop's subject; each links to its parent by `credential_id`.

---

## Run it

```bash
git clone https://github.com/agentrust-io/examples.git
cd examples/ca2a-delegation
pip install --pre ca2a-runtime # cA2A is alpha; --pre is required
```

```bash
python delegation_agent.py
```

```
Building the credit delegation chain:
[0] credit-platform -> lead-credit-agent
scope: ['read:bureau', 'read:documents', 'run:risk-model', 'screen:sanctions', 'write:risk-report']
[1] lead-credit-agent -> screening-sub-agent
scope: ['read:bureau', 'read:documents', 'screen:sanctions']
[2] screening-sub-agent -> bureau-connector
scope: ['read:bureau']

verify_chain: verified=True hops=3 leaf_scope=['read:bureau']
authority withheld at the first delegation (never reaches a sub-agent): ['run:risk-model', 'write:risk-report']

Now the bureau connector tries to grant itself write:risk-report ...
verify_chain: verified=False code=SCOPE_ESCALATION
reason: hop 2 scope exceeds parent grant
```

The demo writes both chains to `chain-output/`. Verify either from the CLI, which checks the same four invariants:

```bash
ca2a verify-chain --chain chain-output/credit-delegation-chain.json
# {"verified": true, "hops": 3, "leaf_scope": ["read:bureau"]}

ca2a verify-chain --chain chain-output/escalation-attempt.json
# {"verified": false, "code": "SCOPE_ESCALATION", "error": "hop 2 scope exceeds parent grant"}
```

---

## What verification checks

`verify_chain` fails on the first violation:

1. **Signature** on every hop against the issuer's Ed25519 public key.
2. **Continuity**: each hop's issuer is the previous hop's subject.
3. **Attenuation**: each hop's scope is a subset of its parent's scope (`SCOPE_ESCALATION` otherwise).
4. **Anti-replay / structure**: unique `credential_id`s, `parent_id` links to the previous hop, depth increments by one and stays within `max_depth`.

Because the write authority is withheld at the first delegation, attenuation alone guarantees that no descendant, however many hops down, can write the risk report. That is separation of duties enforced by the credential, not by convention.

---

## How this maps onto the other examples

The same agent-to-agent boundary applies wherever one agent hands work to another. cA2A is the layer above the cMCP tool boundary each of these already demonstrates.

| Example | Delegation hop | Attenuation the chain proves | Escalation it blocks |
|---|---|---|---|
| [`financial-services`](../financial-services/) | lead credit agent → screening sub-agent → bureau connector | evidence-gathering scope is a subset; `write:risk-report` is withheld | a sub-agent trying to write the risk report |
| [`healthcare`](../healthcare/) | clinician agent → pharmacy/medication-safety sub-agent | pharmacist gets only `check:interaction`; no `read:record` or `write:plan` | a consult agent trying to write the treatment plan |
| [`multi-tenant-saas`](../multi-tenant-saas/) | tenant orchestrator → export processor | EU parent holds `export:eea-only`; it cannot mint a child with `export:us` | a cross-border delegation the tenant never had authority to grant |
| [`industrial-embodied-ai`](../industrial-embodied-ai/) | cell orchestrator → robot-cell agent (its `delegation_chain` field is the hook) | the cell agent gets only `move:buffer-zone-1` | a motion request outside the granted zone |

In each, the sensitive capability is granted high and withheld from the delegated scope, so cA2A's attenuation check is what stops a sub-agent from doing more than it was handed. Where a sub-agent is in a different trust domain (the SaaS export processor, a cross-hospital consult), the cA2A **sealed channel** (roadmap) is what would bind the task payload to that peer's attested measurement.

---

## The tests

`tests/test_delegation_scenario.py` checks that the chain verifies, that each hop is a subset of its parent, that the write authority is never delegated, and that the escalation attempt raises `ScopeEscalation`.

```bash
python -m unittest discover -s tests -v
```

---

## License

Apache 2.0. See [LICENSE](../LICENSE) in the repo root.
43 changes: 43 additions & 0 deletions ca2a-delegation/chain-output/credit-delegation-chain.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"chain": [
{
"credential_id": "credit-cred-0",
"issuer": "7f55fd29bc6632cc46c68910e44d8d0efdb46d6c0599e316ef693ffe07f1cc5b",
"subject": "cb3a37b9f41f452008e0077222a6850f5f8a6bada53b383a85e86c0dee1b7739",
"scope": [
"read:bureau",
"read:documents",
"run:risk-model",
"screen:sanctions",
"write:risk-report"
],
"depth": 0,
"parent_id": null,
"signature": "1c67f820a1acabb7821d8310cd85883da567eff2ceaecf678c8bb8dcd8fe06858157a8f40d35f11fd7e4b91ba2a29476bf495fbc2198bcaf9227746e51528500"
},
{
"credential_id": "credit-cred-1",
"issuer": "cb3a37b9f41f452008e0077222a6850f5f8a6bada53b383a85e86c0dee1b7739",
"subject": "a399d0d49232152829593199ca2c74ff2b66ab669e6e02c1fdeaeb910f0ab1b5",
"scope": [
"read:bureau",
"read:documents",
"screen:sanctions"
],
"depth": 1,
"parent_id": "credit-cred-0",
"signature": "62fae79108808b023a95ed68c4ae328de6b4dcd30e11f9ed87cb61fb03fe46f350f2377b9aa2deac55d87da1ec975273b01b8961dc86a48a3b3f7674a2948a0f"
},
{
"credential_id": "credit-cred-2",
"issuer": "a399d0d49232152829593199ca2c74ff2b66ab669e6e02c1fdeaeb910f0ab1b5",
"subject": "b93a9ce5014c6484d9861d9dc22cab731b0d6d21594c91edea8d11c1be65bfbf",
"scope": [
"read:bureau"
],
"depth": 2,
"parent_id": "credit-cred-1",
"signature": "4e8b5ac961f1d174297c726046069f87dac7a6894002ff385fdda5729912783615ae1e5857815377b6289dadde2897ab2f6a56dc8e548e968bc3f923b20dd501"
}
]
}
44 changes: 44 additions & 0 deletions ca2a-delegation/chain-output/escalation-attempt.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"chain": [
{
"credential_id": "credit-cred-0",
"issuer": "a8ea035f0d574cb3ace5c8e988fa10b972935887f266af109b725b909e91d243",
"subject": "e5c7125ecce92f4de683e32e4fa28b9d8ab87fe5a10ceb7de35077a8ab95c435",
"scope": [
"read:bureau",
"read:documents",
"run:risk-model",
"screen:sanctions",
"write:risk-report"
],
"depth": 0,
"parent_id": null,
"signature": "6a7bdb7d20800d7e415173449b6a93fddedacbe1e103a6add8e92e52ebd017ae7590aa498721002aea168cd1354f692e38249edb1654ce216f8302295b3ea000"
},
{
"credential_id": "credit-cred-1",
"issuer": "e5c7125ecce92f4de683e32e4fa28b9d8ab87fe5a10ceb7de35077a8ab95c435",
"subject": "915f9a0266a99eb6c4d92dea2c8a476f53b8ff8ef77fc749dd935a353fc1721b",
"scope": [
"read:bureau",
"read:documents",
"screen:sanctions"
],
"depth": 1,
"parent_id": "credit-cred-0",
"signature": "34ee23de76780d88168a7f80abde7b5437aaa029073cd9c37978d7ff205d45c8c7fb12c3409dfeaff601fbbeb1f2ef7da93c087bc6c76cfc36b4aef0a9a88f07"
},
{
"credential_id": "credit-cred-2",
"issuer": "915f9a0266a99eb6c4d92dea2c8a476f53b8ff8ef77fc749dd935a353fc1721b",
"subject": "a90c204fa79fc6a2f222ad5a9a5581d7cd1f7d47f0e65dba9a269627b275bc9f",
"scope": [
"read:bureau",
"write:risk-report"
],
"depth": 2,
"parent_id": "credit-cred-1",
"signature": "7bd69aa6ef7b6ce975b26aa65b15fdcd6c6df7c96a4fefb6ac6e12eea4a9a8d6020586de2e9e706c058906c1be50b9aaae3fb36f616b2ba4509f20664cc76708"
}
]
}
84 changes: 84 additions & 0 deletions ca2a-delegation/delegation_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env python3
"""
cA2A delegation demo for the credit-risk workflow.

Builds a three-hop delegation chain (credit platform -> lead credit agent ->
screening sub-agent -> bureau connector), verifies it offline, then shows a
sub-agent trying to widen its scope and being rejected. No network, no TEE.

Usage:
python delegation_agent.py
"""

from __future__ import annotations

import json
import sys
from pathlib import Path

if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8", errors="replace")

sys.path.insert(0, str(Path(__file__).resolve().parent))

import delegation_scenario as scenario # noqa: E402
from ca2a_runtime.errors import CA2AError # noqa: E402

OUT_DIR = Path(__file__).resolve().parent / "chain-output"


def _write(document: dict, name: str) -> Path:
OUT_DIR.mkdir(exist_ok=True)
path = OUT_DIR / name
path.write_text(json.dumps(document, indent=2) + "\n", encoding="utf-8")
return path


def show_valid_chain() -> None:
chain = scenario.build_credit_chain()
print("Building the credit delegation chain:")
for label, cred in zip(scenario.HOP_LABELS, chain):
print(f" [{cred.depth}] {label}")
print(f" scope: {sorted(cred.scope)}")
print()

scenario.verify(chain)
leaf = sorted(chain[-1].scope)
withheld = sorted(scenario.CREDIT_CHAIN_SCOPES[0] - chain[1].scope)
print(f"verify_chain: verified=True hops={len(chain)} leaf_scope={leaf}")
print(f" authority withheld at the first delegation (never reaches a sub-agent): {withheld}")
path = _write(scenario.as_chain_document(chain), "credit-delegation-chain.json")
print(f" wrote {path.name}")
print()


def show_escalation_attempt() -> None:
chain = scenario.build_escalation_attempt()
print("Now the bureau connector tries to grant itself write:risk-report ...")
path = _write(scenario.as_chain_document(chain), "escalation-attempt.json")
try:
scenario.verify(chain)
print(" UNEXPECTED: the chain verified. This should not happen.")
sys.exit(1)
except CA2AError as exc:
code = getattr(exc, "code", "CA2A_ERROR")
print(f" verify_chain: verified=False code={code}")
print(f" reason: {exc}")
print(f" wrote {path.name}")
print()


def main() -> None:
print("=== cA2A: agent-to-agent delegation for the credit-risk workflow ===")
print()
show_valid_chain()
show_escalation_attempt()
print("The write:risk-report authority stays with the lead agent by construction:")
print("it is not in any delegated child's scope, so no descendant can regain it.")
print()
print("Verify either chain from the CLI (same four invariants):")
print(" ca2a verify-chain --chain chain-output/credit-delegation-chain.json")


if __name__ == "__main__":
main()
Loading