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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ pip install cmcp-runtime

`cmcp-runtime` includes all dependencies (`starlette`, `uvicorn`, `cmcp-verify`). All demos use `CMCP_DEV_MODE=1` (software-only TEE, no hardware required). The local MCP server performs real filesystem operations on `./workspace/`.

## Quick start: one command

```
python demo.py # run all three demos, pausing before each (good for live talks)
python demo.py --no-pause # run straight through
python demo.py 2 # run only demo 2
```

`demo.py` sets the token and dev mode for you and prints the detected `cmcp-runtime` version. If your active Python cannot find the `cmcp` command, it will use a local `.venv` if one exists (`python -m venv .venv` then install `cmcp-runtime` into it). To run the demos individually instead, use the per-demo commands below.

Set a bearer token (the cMCP Runtime requires one):

```bash
Expand Down
166 changes: 166 additions & 0 deletions demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#!/usr/bin/env python3
"""One command, all three agentrust-io demos.

python demo.py # run all three, pausing before each (for live talks)
python demo.py --no-pause # run straight through, no prompts
python demo.py 2 # run only demo 2 (1, 2, or 3)

The trust chain, end to end:
Demo 1 cMCP enforces Cedar on every tool call and signs a TRACE claim.
Demo 2 Swap the policy bundle and the claim's hash changes; a pinned verifier rejects it.
Demo 3 Verify that signed claim offline: no server, no gateway, no network.

All demos run in software-only mode (CMCP_DEV_MODE=1). That is deliberate: software
proves the whole chain except the hardware root, so verification reads
'partially_verified'. On real TDX / SEV-SNP the hardware field verifies too and it
becomes 'verified'. That last gap is exactly what the hardware path closes.
"""
import argparse
import os
import pathlib
import shutil
import subprocess
import sys

ROOT = pathlib.Path(__file__).parent.resolve()

DEMOS = [
("1", "cMCP in action",
"demo-01-cmcp-in-action/run.py",
"Three tool calls through cMCP. write_file and read_file are allowed; list_dir is\n"
" denied by Cedar. The session closes into a signed TRACE claim."),
("2", "Policy swap = attestation failure",
"demo-02-policy-swap/run.py",
"Load a different Cedar bundle. The policy hash changes. Watch the\n"
" policy_bundle.hash line flip FAIL -> PASS when the pinned hash matches."),
("3", "Offline TRACE verification",
"demo-03-offline-trace/run.py",
"Verify the demo-1 claim with nothing but the claim and a public key. No network."),
]

GREEN = "\033[92m"; BLUE = "\033[96m"; DIM = "\033[90m"; BOLD = "\033[1m"; RST = "\033[0m"


def _c(s, color):
# colour only when attached to a real terminal
return f"{color}{s}{RST}" if sys.stdout.isatty() else s


def banner(idx, title, blurb):
line = "=" * 70
print()
print(_c(line, DIM))
print(_c(f" DEMO {idx}: {title}", BOLD + BLUE))
print(f" {blurb}")
print(_c(line, DIM))
print()


def _venv_python():
p = ROOT / ".venv" / ("Scripts/python.exe" if os.name == "nt" else "bin/python")
return p if p.exists() else None


def _reexec_into_venv_if_needed():
"""If this interpreter can't resolve cmcp but the demo .venv can, re-run there.
Makes `python demo.py` work regardless of which Python is on PATH."""
if os.environ.get("_DEMO_REEXEC"):
return
if shutil.which("cmcp") or _cmcp_in_scripts():
return
vp = _venv_python()
if not vp:
return
os.environ["_DEMO_REEXEC"] = "1"
rc = subprocess.run([str(vp), str(pathlib.Path(__file__).resolve()), *sys.argv[1:]]).returncode
sys.exit(rc)


def preflight():
if not (shutil.which("cmcp") or _cmcp_in_scripts()):
print(_c("cmcp not found.", BOLD))
print("Set up the demo environment once:")
print(" python -m venv .venv")
print(" .venv\\Scripts\\python -m pip install cmcp-runtime # (Scripts/ -> bin/ on macOS/Linux)")
print("Then just run: python demo.py")
sys.exit(1)
ver = _cmcp_version()
print(_c(f"cmcp-runtime {ver or '(unknown)'} detected.", DIM))
if ver and _older_than(ver, (0, 3, 0)):
print(_c(
" WARNING: the demo narration assumes 0.3.0+. On this older version, demo 2\n"
" step 6 and demo 3 will read 'verified' instead of 'partially_verified'.\n"
" For the talk, upgrade: pip install -U cmcp-runtime", BOLD))


def _cmcp_version():
try:
import importlib.metadata as m
return m.version("cmcp-runtime")
except Exception:
return None


def _older_than(ver, target):
try:
parts = tuple(int(x) for x in ver.split(".")[:3])
return parts < target
except Exception:
return False


def _cmcp_in_scripts():
import sysconfig
for base in (pathlib.Path(sys.executable).parent,
pathlib.Path(sysconfig.get_path("scripts")),
pathlib.Path(sysconfig.get_path("scripts", "nt_user"))):
for name in ("cmcp.exe", "cmcp"):
if (base / name).exists():
return True
return False


def run(idx, title, script, blurb, pause):
if pause:
try:
input(_c(f">>> Press Enter to run Demo {idx}: {title} ", GREEN))
except (EOFError, KeyboardInterrupt):
print("\nStopped."); sys.exit(0)
banner(idx, title, blurb)
rc = subprocess.run([sys.executable, str(ROOT / script)]).returncode
if rc != 0:
print(_c(f"\n[!] Demo {idx} exited with code {rc}. See the *.log files in the demo folder.", BOLD))
return rc


def main():
ap = argparse.ArgumentParser(description="Run the agentrust-io trust-chain demos.")
ap.add_argument("only", nargs="?", choices=["1", "2", "3"], help="run only this demo")
ap.add_argument("--no-pause", action="store_true", help="run straight through, no prompts")
args = ap.parse_args()

os.environ.setdefault("CMCP_BEARER_TOKEN", "demo-token")
os.environ.setdefault("CMCP_DEV_MODE", "1")
_reexec_into_venv_if_needed()
preflight()

print(_c("\nagentrust-io 路 the trust chain, live", BOLD))
print(_c("Agent Manifest (what the agent is) 路 cMCP (what it does) 路 TRACE (the proof)", DIM))

selected = [d for d in DEMOS if (args.only is None or d[0] == args.only)]
pause = not args.no_pause and sys.stdin.isatty()

failures = 0
for idx, title, script, blurb in selected:
if run(idx, title, script, blurb, pause) != 0:
failures += 1

print()
if failures:
print(_c(f"Done with {failures} failure(s).", BOLD))
sys.exit(1)
print(_c("Done. The proof outlives the runtime that made it.", BOLD + GREEN))


if __name__ == "__main__":
main()