From 85012cc71d053bfdbfd7d6feea4084fafdbc439a Mon Sep 17 00:00:00 2001 From: Imran Siddique Date: Mon, 27 Jul 2026 13:31:47 -0700 Subject: [PATCH] feat(demo-06): weight custody talk-track demo Add a sixth demo depending on the published weight-custody-manifest package (>=0.19.0). A pure in-process WCM flow with a software (mock) attestation provider: a manifest binds the exact weight hash and is jointly signed, the attestation gate releases the key only for the certified serving stack, a tampered checkpoint is refused before load, and a fine-tune's lineage verifies back to the signed base. Runs with no hardware and no server. Wire it into demo.py (choice 6 + narration) and requirements.txt, and broaden the README (six demos across cMCP, TRACE, and WCM). Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 39 +++++-- demo-06-weight-custody/run.py | 187 ++++++++++++++++++++++++++++++++++ demo.py | 13 ++- requirements.txt | 1 + 4 files changed, 228 insertions(+), 12 deletions(-) create mode 100644 demo-06-weight-custody/run.py diff --git a/README.md b/README.md index 988a3ef..e736158 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,21 @@ # agentrust-io demos -Runnable demos for [cMCP](https://github.com/agentrust-io/cmcp) and [TRACE](https://github.com/agentrust-io/trace-spec). Five demos, ~6 minutes total. +Runnable demos for [cMCP](https://github.com/agentrust-io/cmcp), [TRACE](https://github.com/agentrust-io/trace-spec), and [WCM](https://pypi.org/project/weight-custody-manifest/). Six demos, ~7 minutes total. --- ## Prerequisites ``` -pip install cmcp-runtime +pip install cmcp-runtime weight-custody-manifest ``` -`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/`. +`cmcp-runtime` includes all dependencies (`starlette`, `uvicorn`, `cmcp-verify`) and drives demos 1 through 5; `weight-custody-manifest` (from PyPI) drives demo 6. 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 # run all demos, pausing before each (good for live talks) python demo.py --no-pause # run straight through python demo.py 2 # run only demo 2 ``` @@ -141,6 +141,23 @@ What you see: --- +## Demo 6 -- Weight custody (~60 seconds) + +Demos 1-5 govern what an agent *does*. Demo 6 is the layer beneath: the model *weights* themselves. A Weight Custody Manifest binds the exact weight hash, gates the decryption key behind attestation, and carries the fine-tune's lineage. It needs no hardware and no server, and depends only on the `weight-custody-manifest` package from PyPI. + +``` +python demo-06-weight-custody/run.py +``` + +What you see: +- a manifest jointly signed (builder + custodian) binds the checkpoint's exact `weights_hash`, and the signature verifies +- the attestation gate releases the key only for the certified serving stack (genuine nonce, approved platform, signed image measurement) +- a tampered checkpoint's hash does not match the manifest, so it is **refused before it ever loads** +- a fine-tune is a derivative whose lineage verifies back to the signed base (the derivative is the real IP) +- honest scope: this is accountability-grade against an operator who physically owns the silicon (see TEE.fail), not silicon-proof custody + +--- + ## Structure ``` @@ -179,12 +196,14 @@ demos/ | +-- run.py # Cross-platform launcher (use this) | +-- run.sh # bash-only launcher +-- demo-05-compliance-domain/ - +-- cmcp-config.yaml - +-- catalog.json # Tags tools with compliance_domain + BAA status - +-- policies/ # Cedar: forbid any tool when context.baa_covered == false - +-- call.py # Two BAA-covered tools allowed, one non-covered tool denied - +-- run.py # Cross-platform launcher (use this) - +-- run.sh # bash-only launcher +| +-- cmcp-config.yaml +| +-- catalog.json # Tags tools with compliance_domain + BAA status +| +-- policies/ # Cedar: forbid any tool when context.baa_covered == false +| +-- call.py # Two BAA-covered tools allowed, one non-covered tool denied +| +-- run.py # Cross-platform launcher (use this) +| +-- run.sh # bash-only launcher ++-- demo-06-weight-custody/ + +-- run.py # WCM flow: sign, attestation gate, tamper refusal, lineage ``` --- diff --git a/demo-06-weight-custody/run.py b/demo-06-weight-custody/run.py new file mode 100644 index 0000000..86c8423 --- /dev/null +++ b/demo-06-weight-custody/run.py @@ -0,0 +1,187 @@ +"""Demo 6: Weight custody - prove the model is the one the builder shipped. + +Usage: + python demo-06-weight-custody/run.py # from repo root + python run.py # from the demo-06 directory + +A pure in-process Weight Custody Manifest flow with a software (mock) +attestation provider, so it runs anywhere with no hardware. It shows the four +things a manifest gives you over a bare checkpoint download: provenance, an +attestation gate, tamper refusal, and derivative lineage. Possession is not +provenance. +""" +from __future__ import annotations + +import hashlib +import sys + +from wcm import ( + EnclaveSession, + Ed25519Signer, + KeyBrokerService, + SoftwareProvider, + VerificationContext, + WeightCustodyManifest, + generate_ed25519, + is_root, + verify_lineage, + verify_manifest, +) + +sys.stdout.reconfigure(line_buffering=True) + + +def rule(title: str) -> None: + print(f"\n{'-' * 66}\n{title}\n{'-' * 66}") + + +def sha256(data: bytes) -> str: + return "sha256:" + hashlib.sha256(data).hexdigest() + + +def build_manifest( + *, + weights_hash: str, + license_text: str, + serving_measurement: str, + org: str, + derivatives: str, + derived_from: str | None = None, + rights_holder: dict | None = None, +) -> dict: + m: dict = { + "manifest_version": "0.1", + "weights_hash": weights_hash, + "builder": {"identity": org, "signing_key": "ed25519:demo"}, + "release_terms": { + "license": license_text, + "permitted_derivatives": "fine-tune-only", + "derivatives": derivatives, + "permitted_environments": ["enterprise-governed-enclave"], + }, + "release_policy": { + "required_assurance_tier": "hardware-attested", + "trusted_time_source": "secure-tsc", + "required_hw_platform": ["amd-sev-snp", "nvidia-cc-gpu"], + "required_gpu_measurement": {"rim_pin": "nvidia-rim:demo-golden"}, + "required_serving_image": { + "signer": "ed25519:demo", + "release_rule": "prefer-current", + "accepted_measurements": [ + {"measurement": serving_measurement, "status": "current"} + ], + }, + "attestation_revocation_check": "live-per-release, max-cache-age: short-window", + "revocation_authority": "builder-and-opaque-joint", + }, + "custody": { + "custodian": org, + "custodian_type": "customer-self-custody", + "kbs_image": {"measurement": sha256(b"reference-kbs-image"), "signer": "ed25519:demo"}, + "enclave_id": "did:example:enterprise-enclave-01", + "attestation_cadence": "1h", + }, + "base_confidentiality": "gated-open", + "deployment_model": "builder-to-customer", + } + if derived_from is not None: + m["derived_from"] = derived_from + if rights_holder is not None: + m["rights_holder"] = rights_holder + return m + + +def sign(manifest: WeightCustodyManifest, keypair, role: str, signer: str) -> dict: + return Ed25519Signer(keypair).sign(manifest.unsigned_dict(), role=role, signer=signer) + + +def _release(kbs: KeyBrokerService, manifest: WeightCustodyManifest, serving: str): + """Issue a fresh single-use challenge, produce evidence, run the gate.""" + challenge = kbs.issue_challenge() + evidence = SoftwareProvider().produce( + challenge, + serving_image_measurement=serving, + gpu_measurement="nvidia-rim:demo-golden", + ) + return kbs.verify_and_release(manifest, evidence) + + +def main() -> None: + print("Weight Custody Manifest: possession is not provenance.") + print("Real WCM code with a software (mock) attestation provider, no hardware.") + + builder = generate_ed25519() # the model builder + custodian = generate_ed25519() # the deploying customer / governance function + + # The builder ships a checkpoint and a serving stack it certifies. + checkpoint = b"" + base_hash = sha256(checkpoint) + serving = sha256(b"vllm-0.6.3 + policy-bundle-v2 (the certified serving stack)") + cadence = "1h" + + rule("1. The builder signs a manifest binding the exact weight hash") + base_doc = build_manifest( + weights_hash=base_hash, + license_text="Frontier-Model-License (usage + field-of-use)", + serving_measurement=serving, + org="frontier-labs", + derivatives="fine-tune-only", + ) + base = WeightCustodyManifest.model_validate(base_doc) + base = base.with_signatures([ + sign(base, builder, "builder", "frontier-labs"), + sign(base, custodian, "custodian", "enterprise-governance"), + ]) + ctx = VerificationContext() + ctx.add_key(builder.public_bytes) + ctx.add_key(custodian.public_bytes) + print("weights_hash bound :", base_hash) + print("manifest signature :", verify_manifest(base, ctx).ok, "(jointly signed builder + custodian)") + + rule("2. Attestation gate: the key releases only into the certified stack") + kbs = KeyBrokerService({base.weights_hash: b"the-model-decryption-key"}) + print("gate released key :", _release(kbs, base, serving).released) + print("enforced: genuine attestation nonce, approved platform, and a serving") + print("image measurement matching what the builder signed.") + + rule("3. A tampered checkpoint fails before it ever loads") + tampered = checkpoint.replace(b"certified", b"backdoored") + matches = sha256(tampered) == base.weights_hash + print("certified hash :", base.weights_hash) + print("downloaded hash :", sha256(tampered)) + print("matches manifest :", matches, "-> load proceeds" if matches else "-> REFUSE to load") + print("no human reads 2.8T parameters; the hash does the reading.") + + rule("4. The fine-tune is the real IP: lineage back to the signed base") + derivative = checkpoint + b"<+ proprietary fine-tune on private data>" + deriv_hash = sha256(derivative) + deriv_doc = build_manifest( + weights_hash=deriv_hash, + license_text="Frontier-Model-License + enterprise-proprietary-derivative", + serving_measurement=serving, + org="enterprise-governance", + derivatives="none", + derived_from=base.weights_hash, + rights_holder={"base": "frontier-labs", "derivative": "enterprise"}, + ) + deriv = WeightCustodyManifest.model_validate(deriv_doc) + deriv = deriv.with_signatures([ + sign(deriv, builder, "builder", "enterprise-governance"), + sign(deriv, custodian, "custodian", "enterprise-governance"), + ]) + lineage = verify_lineage({base.weights_hash: base, deriv.weights_hash: deriv}, deriv.weights_hash) + print("derivative :", deriv_hash) + print("lineage verified :", lineage.ok, " depth", lineage.depth, " root is a base:", is_root(base)) + + rule("What this is, and is not") + session = EnclaveSession.from_release(base, _release(kbs, base, serving)) + session.use_key() + print("custody active : key held under a", cadence, "cadence, wiped on lapse", + "(time_floor " + session.time_floor.value + ")") + print("honest scope : accountability-grade against an operator who physically") + print(" owns the silicon (see TEE.fail), not silicon-proof") + print(" custody. It IS the provenance the download never gave you.") + + +if __name__ == "__main__": + main() diff --git a/demo.py b/demo.py index cac716a..31f6126 100644 --- a/demo.py +++ b/demo.py @@ -3,7 +3,7 @@ python demo.py # run all, 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 through 5) + python demo.py 2 # run only demo 2 (1 through 6) The trust chain, end to end: Demo 1 cMCP enforces Cedar on every tool call and signs a TRACE claim. @@ -14,6 +14,10 @@ Demo 4 by call context -- the same tool, allowed in one workflow, denied in another. Demo 5 by tool attribute -- a non-BAA-covered tool refused by one guardrail rule. +And the layer beneath it all, the weights themselves: + Demo 6 Weight custody -- a signed manifest binds the exact weight hash; a tampered + checkpoint is refused before load, and a fine-tune's lineage verifies to the base. + 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 @@ -48,6 +52,11 @@ "demo-05-compliance-domain/run.py", "A tool that is not BAA-covered is refused by one guardrail rule, whatever it is\n" " named. The decision is on the tool's compliance attribute, not its identity."), + ("6", "Weight custody", + "demo-06-weight-custody/run.py", + "A checkpoint signed to its exact weight hash. Attestation gates the key, a\n" + " tampered fork is refused before load, and a fine-tune's lineage verifies back to\n" + " the signed base. Possession is not provenance."), ] GREEN = "\033[92m"; BLUE = "\033[96m"; DIM = "\033[90m"; BOLD = "\033[1m"; RST = "\033[0m" @@ -147,7 +156,7 @@ def run(idx, title, script, blurb, pause): def main(): ap = argparse.ArgumentParser(description="Run the agentrust-io trust-chain demos.") - ap.add_argument("only", nargs="?", choices=["1", "2", "3", "4", "5"], help="run only this demo") + ap.add_argument("only", nargs="?", choices=["1", "2", "3", "4", "5", "6"], help="run only this demo") ap.add_argument("--no-pause", action="store_true", help="run straight through, no prompts") args = ap.parse_args() diff --git a/requirements.txt b/requirements.txt index 445a869..e1aa782 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ cmcp-runtime +weight-custody-manifest>=0.19.0