Skip to content

feat: report sealed only when a Cloud Seal verifies against pinned keys - #68

Merged
abrichr merged 1 commit into
mainfrom
agent/verified-seal-proof
Oct 15, 2026
Merged

abrichr merged 1 commit into
mainfrom
agent/verified-seal-proof

Conversation

@abrichr

@abrichr abrichr commented Oct 15, 2026

Copy link
Copy Markdown
Member

Security-sensitive (proof semantics): left for Richard's review.

  • New src/openadapt_agent/seal.py verifies a Cloud Execute receipt's seal_signature. It's byte-compatible with Cloud sealSignature.ts (OpenAdaptAI/openadapt-cloud#399/#402) and openadapt_types.seal (feat(execute): optional Cloud issuer seal_signature on ExecuteEvidenceReceiptV1 openadapt-types#44). It's a self-contained copy until types#44 is released; then it can import from types.
  • sealed: true / proof: "sealed" requires all of the following:
    • status success and execution_outcome == VERIFIED
    • report.execute_receipt.execution_id == run_id with outcome verified or rolled_back_verified
    • the receipt hash matches and the signature's run_id is bound to the receipt
    • a known, non-revoked key from a pinned registry (OPENADAPT_SEAL_KEYS_PATH / OPENADAPT_SEAL_KEYS_JSON, saved from /api/public/seal-keys; never fetched at verify time)
    • a valid Ed25519 signature
  • Anything else gives sealed: false. Fail-closed semantics are unchanged: a verified local run is still done with proof: local.
  • Honest scope: nothing writes execute_receipt into a local Flow report today, so in practice this stays false until a Cloud-backed path supplies the receipt. This PR is the verification gate.

Tests: tests/test_seal.py (forged, tampered, revoked, wrong-run, unsigned and no-keys cases all refuse, plus the runner end-to-end). 346 pass; ruff clean.

RunOutcome.seal_verified() is true only for a success with execution
outcome VERIFIED whose report carries a Cloud Execute receipt for this run
(verified outcome) and whose seal_signature (openadapt.seal-signature/v1)
verifies against a pinned issuer registry (OPENADAPT_SEAL_KEYS_PATH or
OPENADAPT_SEAL_KEYS_JSON; never fetched at verify time). Then the result
says sealed: true and proof: sealed. Every other case keeps sealed: false;
a verified local run stays done with proof: local.

Signed-off-by: Richard Abrich <richard.abrich@mldsai.com>
@abrichr

abrichr commented Oct 15, 2026

Copy link
Copy Markdown
Member Author

Self-review.

  • sealed / proof: "sealed" is reported only when all of these hold: the run status is success, the execution outcome is VERIFIED, the report's execute_receipt is for this run with a sealable outcome, and its seal_signature verifies against a pinned registry (OPENADAPT_SEAL_KEYS_PATH or OPENADAPT_SEAL_KEYS_JSON, never fetched at verify time). Any exception or missing registry gives sealed: false.
  • cryptography is already a dependency, and an ImportError still refuses.
  • The verifier duplicates openadapt_types.seal (chore(deps): Bump the codeql-action group with 2 updates #44) on purpose, so the agent doesn't need a newer types release. Its test covers forged, tampered, revoked, wrong-run and unsigned cases.
  • No changes needed.

@abrichr
abrichr merged commit f959c04 into main Oct 15, 2026
8 checks passed
@abrichr
abrichr deleted the agent/verified-seal-proof branch October 15, 2026 02:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant