Cryptographic evidence for every step of the vulnerability lifecycle.
When AI agents discover vulnerabilities (e.g., Claude Code Security / Mythos), the disclosure process needs more than "we found and fixed it." Regulated industries and multi-vendor ecosystems need independently verifiable proof that every step was followed per policy.
This example shows how to use Ed25519 receipts + Cedar policies to create a tamper-evident, auditable vulnerability disclosure chain.
Current vulnerability disclosure processes lack:
- Proof a scan was performed (audit evidence for compliance)
- Proof a finding was reviewed and approved (not just suggested)
- Tamper-evident record of the remediation lifecycle
- Policy governance of what the scanning agent is allowed to do
SHA-3 commitments prove "we had this file at this time." Receipt chains prove who, what, when, under which policy, and what came before.
DISCOVER (agent finds vulnerability)
| receipt: what was found, severity, affected software
v
DISCLOSE (responsible disclosure to vendor)
| receipt: who was notified, when, embargo terms
v
PATCH (fix developed and reviewed)
| receipt: patch hash, reviewer, test results
v
DEPLOY (fix deployed to production)
| receipt: deployment verification, rollback status
Each step: Ed25519-signed, chain-linked, policy-bound
Verify entire chain: npx @veritasacta/verify chain.jsonl
The Cedar policy (vulnerability-disclosure.cedar) defines what the scanning agent is allowed to do:
// Agent CAN scan code and report internally
permit(principal, action == Action::"scan_code", resource);
permit(principal, action == Action::"report_finding", resource)
when { context.report_destination == "internal" };
// Agent CANNOT disclose externally without human approval
forbid(principal, action == Action::"disclose_vulnerability", resource)
when { context.disclosure_target == "external" && context.human_approval != true };
// Agent CANNOT deploy patches to production
forbid(principal, action == Action::"deploy_patch", resource)
when { context.environment == "production" && context.human_approval != true };
// Agent MUST escalate critical findings
forbid(principal, action == Action::"report_finding", resource)
when { context.severity == "critical" && context.escalated_to_human != true };
Each step produces a signed receipt that links to the previous one:
{
"receipt_id": "rcpt-discover-001",
"action": "scan_code",
"actor": "sb:agent:security-scanner",
"timestamp": "2026-04-08T10:00:00Z",
"payload": {
"vulnerability_id": "CVE-2026-XXXX",
"severity": "high",
"affected_package": "protect-mcp@0.5.3",
"affected_file": "src/signing.ts",
"description": "Timing side-channel in signature verification"
},
"policy_digest": "sha256:9d0f...",
"previousReceiptHash": null,
"signature": "ed25519:..."
}The disclosure receipt links to the discovery:
{
"receipt_id": "rcpt-disclose-001",
"action": "disclose_vulnerability",
"actor": "sb:agent:pm",
"timestamp": "2026-04-08T10:30:00Z",
"payload": {
"vulnerability_id": "CVE-2026-XXXX",
"disclosure_target": "internal",
"notified_parties": ["security-team@scopeblind.com"],
"embargo_until": "2026-07-08T00:00:00Z",
"human_approval": true,
"approver": "sb:human:tom"
},
"policy_digest": "sha256:9d0f...",
"previousReceiptHash": "sha256:abc123...",
"signature": "ed25519:..."
}Verify the entire disclosure chain offline:
# Verify a single receipt
npx @veritasacta/verify receipt-discover.json --key <public-key-hex>
# Verify the complete chain
npx @veritasacta/verify chain.jsonl --key <public-key-hex>
# Exit codes:
# 0 = valid (cryptographically verified)
# 1 = invalid (tampered or broken chain)
# 2 = error (malformed input)import { signDecision } from 'protect-mcp';
// After Mythos discovers a vulnerability
const discoveryReceipt = await signDecision({
action: 'scan_code',
actor: 'sb:agent:mythos',
payload: {
vulnerability_id: finding.id,
severity: finding.severity,
affected_package: finding.package,
cwe: finding.cwe_id,
},
policyDigest: currentPolicyHash,
});The MCP gateway automatically signs every tool call. Configure the vulnerability disclosure policy:
{
"policyPath": "./policies/vulnerability-disclosure.cedar",
"signingKey": "./keys/signing.json",
"receiptLog": "./receipts/disclosures.jsonl"
}The receipt format follows draft-farley-acta-signed-receipts-01:
| Field | Purpose |
|---|---|
receipt_id |
Unique identifier |
action |
What was done |
actor |
WHO signed it (Ed25519 key ID) |
timestamp |
WHEN it happened |
payload |
WHAT was decided/found |
policy_digest |
WHICH policy governed the action |
previousReceiptHash |
Chain linking (tamper-evident ordering) |
signature |
Ed25519 signature over JCS-canonicalized payload |
For Glasswing's 90-day recommendations on disclosure processes:
- Multi-vendor: AWS, Apple, Google, Microsoft won't adopt a standard owned by a competitor. IETF Internet-Drafts are neutral ground.
- Offline verification: No network calls, no trust in the issuer. Anyone can verify.
- Interoperability: 4 independent implementations across TypeScript and Python produce interoperable receipts.
- Policy-bound: Cedar policies define boundaries. Receipts prove boundaries were respected.
- IETF Draft: Signed Receipts
- protect-mcp (MIT) - MCP gateway with receipt signing
- @veritasacta/verify (Apache-2.0) - Offline verification
- Cedar WASM PR - Policy engine bindings
- Vulnerability Disclosure Design Issue