Skip to content

feat(seal): CROSS-3 bounded-autonomy seal — offline-verifiable receipt#6

Closed
gesh75 wants to merge 1 commit into
feat/llm-ceilingfrom
feat/llm-seal
Closed

feat(seal): CROSS-3 bounded-autonomy seal — offline-verifiable receipt#6
gesh75 wants to merge 1 commit into
feat/llm-ceilingfrom
feat/llm-seal

Conversation

@gesh75

@gesh75 gesh75 commented Jun 1, 2026

Copy link
Copy Markdown
Owner

CROSS-3 — the bounded-autonomy SEAL (the capstone, stacked on #5-ceiling)

Fuses the #4 model-identity wedge + the #5 authority ceiling into one detached, offline-checkable receipt: a proof that a named + hashed model produced THIS exact change AND that it was bounded by the ceiling — signed with a pinned key, checkable on an appliance that holds no secrets. That intersection (model wedge ∧ signed ceiling ∧ offline-keyless check) is the moat none of Forward / Cisco / Itential hand an auditor offline.

Base = feat/llm-ceiling (#5-ceiling). Merge the stack in order.

What's here (additive core/seal/ — the bundle is never mutated)

  • signing.pySigner/Verifier protocols + Ed25519Signer/Ed25519Verifier (real asymmetric crypto). The hardware path (YubiKey PIV slot via PKCS#11 + slot-9a attestation chain) implements the same Signer protocol and is the documented swap-in — deliberately not stubbed-as-working, so there's no false-green crypto.
  • seal.py — a detached receipt over canonical claims {bundle_sha256 + model_identity (#4) + authority (#5) + sealed_at}. seal_bundle() refuses a tampered or unbounded change (you never certify a self-escalation). verify_seal() runs offline gates G0–G6: structure · key pinning · signature · bundle binding · model bound + honest · bounded-and-matching · bundle integrity.
  • schema/seal.schema.json — the receipt schema (alg enum already includes piv-ecdsa-p256).

Why detached

The receipt binds the bundle by integrity.sha256 and never mutates it — so the bundle's own hash and every existing suite are untouched (no version bump, no schema churn).

Tests — tests/seal_test.py, 12/12

roundtrip · offline verify with the public key only (no secret) · wrong key rejected · forged signature → G2 · bundle tamper → G3/G6 · model swap → G4 · refuse-to-seal-unbounded · reject-unbounded → G5 · 0-violation property (every valid seal certifies a bounded change). Regression: stress/promote PASS, ceiling 8/8, authority 14/14, wedge 10/10, PR-1 29/29.

The one remaining piece (honest)

The signer here is a software Ed25519 key. The last step is the hardware-PIV signer — ECDSA-P256 via the YubiKey PIV slot + the Yubico attestation chain pinned compiled-in — which needs the physical token and drops into the same Signer protocol. That's the critic's D6–D7 crypto spike; everything it plugs into (the seal mechanics + the offline G0–G6 verifier) ships and is tested here.

Summary by Sourcery

Introduce a detached, offline-verifiable cryptographic seal that binds model identity, bounded authority, and bundle integrity into a single receipt.

New Features:

  • Add a CROSS-3 seal module that generates detached receipts over evidence bundles, binding model identity, authority ceiling, and bundle hash and enforcing bounded-autonomy constraints.
  • Provide Ed25519-based signing and verification abstractions to support offline seal verification with only a pinned public key.
  • Define a JSON schema for the seal receipt format and expose seal APIs via the core.seal package.

Enhancements:

  • Extend project dependencies to include the cryptography library for real asymmetric signing primitives.

Tests:

  • Add comprehensive seal tests covering round-trip sealing/verification, offline verification, key pinning, bundle tampering, model and authority mismatches, unbounded-change rejection, property-based boundedness checks, and schema conformance.

Fuses the #4 model-identity wedge + the #5 authority ceiling into ONE detached,
offline-checkable receipt: a named+hashed model produced THIS exact change AND it was
bounded by the ceiling — signed with a pinned key, checkable on an appliance that holds no
secrets. The thing competitors assert but cannot hand an auditor offline.

- core/seal/signing.py: Signer/Verifier protocols + Ed25519Signer/Ed25519Verifier (real
  asymmetric crypto). The hardware path (YubiKey PIV slot via PKCS#11 + attestation chain)
  implements the SAME Signer protocol and is the documented swap-in — NOT stubbed-as-working
  (no false-green crypto).
- core/seal/seal.py: detached receipt over canonical claims {bundle_sha256 + model_identity
  (#4) + authority (#5) + sealed_at}. seal_bundle() REFUSES a tampered or unbounded change.
  verify_seal() runs offline gates G0..G6 (structure / key pinning / signature / bundle
  binding / model bound+honest / bounded-and-matching / bundle integrity).
- core/seal/schema/seal.schema.json: the receipt schema (alg enum includes piv-ecdsa-p256).
- tests/seal_test.py: 12 tests — roundtrip, OFFLINE check (public key only), wrong key,
  forged signature (G2), bundle tamper (G3/G6), model swap (G4), refuse-to-seal-unbounded,
  reject-unbounded (G5), and a 0-violation property (every valid seal certifies a bounded
  change). + requirements: cryptography.

DETACHED by design: the seal binds the bundle by integrity.sha256 and never mutates it, so
the bundle's own hash + every existing test stay untouched. Next: the real PIV sign/verify
(ECDSA-P256 + Yubico attestation chain) against a physical YubiKey — same Signer protocol.

Green: seal 12/12; stress/promote PASS; ceiling 8/8; authority 14/14; wedge 10/10; PR-1 29/29.
@sourcery-ai

sourcery-ai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Reviewer's Guide

Introduces the CROSS-3 bounded-autonomy SEAL: a detached, offline-verifiable cryptographic receipt binding a bundle’s integrity hash, model identity, and authority ceiling, implemented with Ed25519 signing/verifying primitives, a structured verification pipeline (gates G0–G6), a JSON schema for the seal format, and comprehensive tests for round‑trip sealing, offline verification, tampering, and bounded‑authority guarantees.

File-Level Changes

Change Details Files
Add generic signing interfaces and a concrete Ed25519 software implementation for detached signatures, suitable for swapping to a hardware-backed signer later.
  • Define Signer and Verifier runtime-checkable protocols for detached signing and verification over arbitrary payload bytes.
  • Implement Ed25519Signer with key generation/import, signature creation, key-id derivation, and a helper to construct the corresponding verifier using only the public key.
  • Implement Ed25519Verifier that verifies signatures using only the pinned public key and exposes alg and key_id for key-pinning checks.
  • Introduce a stable key_id helper derived from sha256 of the raw public key, used to pin the verification key in seal validation.
core/seal/signing.py
Introduce the CROSS-3 seal format and verification logic as a detached, offline-verifiable receipt over evidence bundles, including authority-bounding and model-identity binding.
  • Define SEAL_VERSION and a SealError exception used when refusing to issue a seal for unbounded or integrity-failing bundles.
  • Add canonical JSON serialization helpers for claims to ensure deterministic bytes for signing and verification.
  • Implement build_claims to construct the exact claim set (seal_version, bundle_sha256, model identity, authority fields, sealed_at_utc) derived from an evidence bundle.
  • Add an internal _is_bounded helper that checks the change’s authority is within the configured Tier ceiling and allowed/effective are non-blocking.
  • Implement seal_bundle to integrity-verify the bundle, enforce bounded authority, build claims, sign them via the Signer protocol, and return a detached seal object with algorithm, key_id, and base64-encoded signature.
  • Introduce SealVerdict dataclass capturing validity, deciding gate (OK or G0–G6), and human-readable reason.
  • Implement verify_seal, which uses only the provided Verifier (public key) and runs gates G0–G6: structure validation, key pinning, signature verification, bundle hash binding, model identity consistency and honesty constraints, authority match and boundedness, and final bundle integrity re-check.
core/seal/seal.py
Expose the seal API surface (signers, verifiers, and sealing helpers) as a public core.seal package.
  • Add core/seal/init.py that re-exports seal version constants, error/verdict types, claim/serialization helpers, sealing/verification entrypoints, and signing abstractions and Ed25519 implementations.
  • Define all to make the expected public API explicit for importers (including tests).
core/seal/__init__.py
Define a JSON schema for seal receipts to enforce structure and algorithm enumeration at the interface boundary.
  • Add core/seal/schema/seal.schema.json describing the seal object, its claims, signature structure, and allowed algorithms (including piv-ecdsa-p256 for the future hardware signer).
core/seal/schema/seal.schema.json
Add comprehensive tests covering sealing semantics, offline verification behavior, failure modes by gate, schema compliance, and the bounded-autonomy safety property.
  • Create helpers to locate the project root, load the seal JSON schema, recompute bundle integrity hashes, and construct bounded/unbounded bundles via the existing SimulatorBackend and run_preflight pipeline.
  • Test that build_claims binds bundle_sha256, model identity, and authority correctly to the bundle.
  • Test successful seal→verify round-trip and pure offline verification using only the public key via Ed25519Verifier.from_public_bytes.
  • Test key-pinning and signature integrity by verifying with the wrong key (G1/G2) and with a corrupted signature (G2).
  • Test bundle tampering with and without recomputing the integrity hash to trigger bundle binding failures (G3) and integrity failures (G6).
  • Test model-identity swap in claims (re-signed with the correct key) to ensure G4 detects mismatches.
  • Test that seal_bundle refuses to seal unbounded bundles and that verify_seal rejects a synthetically sealed unbounded bundle at G5.
  • Test the 0-violation property by repeatedly sealing and verifying randomly-parameterized bounded bundles and asserting all valid seals represent non-blocked, allowed changes.
  • Validate that produced seals conform to the JSON schema when jsonschema is available, with a self-skipping behavior if the dependency is absent.
  • Provide a lightweight main runner to execute all tests sequentially without pytest, suitable for minimal CI environments.
tests/seal_test.py
Extend dependencies to support cryptographic signing required by the seal implementation.
  • Add the cryptography>=42 dependency to requirements.txt to provide Ed25519 primitives for the software signer/verifier.
requirements.txt

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • In verify_seal, you never check the seal_version on the seal or inside claims against SEAL_VERSION; adding an explicit version gate early (e.g., as part of G0) would make format evolution safer and avoid silently accepting future-incompatible receipts.
  • The key_id is currently only the first 16 hex chars of the SHA-256 of the public key (64 bits); if you expect many keys or long-lived deployments it may be worth extending this prefix length to reduce collision risk while keeping IDs short.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `verify_seal`, you never check the `seal_version` on the seal or inside `claims` against `SEAL_VERSION`; adding an explicit version gate early (e.g., as part of G0) would make format evolution safer and avoid silently accepting future-incompatible receipts.
- The `key_id` is currently only the first 16 hex chars of the SHA-256 of the public key (64 bits); if you expect many keys or long-lived deployments it may be worth extending this prefix length to reduce collision risk while keeping IDs short.

## Individual Comments

### Comment 1
<location path="core/seal/seal.py" line_range="98-107" />
<code_context>
+    reason: str
+
+
+def verify_seal(bundle: dict, seal: dict, verifier: Verifier) -> SealVerdict:
+    """Offline verification — uses ONLY the pinned public key. Runs gates G0..G6; the first
+    failure decides."""
+    # G0 — structure
+    try:
+        claims = seal["claims"]
+        sig = seal["signature"]
+        alg, kid, value = sig["alg"], sig["key_id"], sig["value"]
+        b_sha, model, authority = claims["bundle_sha256"], claims["model"], claims["authority"]
+    except (KeyError, TypeError):
+        return SealVerdict(False, "G0", "malformed seal")
+
+    # G1 — key pinning: only the pinned key + alg are trusted
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Consider explicitly validating `seal_version` in the verifier to catch incompatible or unexpected seal formats.

Right now we never compare `seal["seal_version"]` / `claims["seal_version"]` to `SEAL_VERSION`, so a seal with an older or newer format could still verify as long as the shape mostly matches and the signature is valid. Please add a version check (e.g., in G0 or a dedicated gate) that rejects unknown major versions so format changes can’t be silently accepted without explicit migration handling.

Suggested implementation:

```python
def verify_seal(bundle: dict, seal: dict, verifier: Verifier) -> SealVerdict:
    """Offline verification — uses ONLY the pinned public key. Runs gates G0..G6; the first
    failure decides."""
    # G0 — structure
    try:
        claims = seal["claims"]
        sig = seal["signature"]
        alg, kid, value = sig["alg"], sig["key_id"], sig["value"]
        b_sha, model, authority = claims["bundle_sha256"], claims["model"], claims["authority"]
        claims_seal_version = claims["seal_version"]
    except (KeyError, TypeError):
        return SealVerdict(False, "G0", "malformed seal")

    # G0 — seal version compatibility
    # We require a known major version so that format changes cannot be silently accepted.
    if not isinstance(claims_seal_version, str):
        return SealVerdict(False, "G0", "seal_version must be a string")

    seal_seal_version = seal.get("seal_version", claims_seal_version)
    if not isinstance(seal_seal_version, str):
        return SealVerdict(False, "G0", "seal.seal_version must be a string if present")

    if seal_seal_version != claims_seal_version:
        return SealVerdict(False, "G0", "seal_version mismatch between envelope and claims")

    # Compare major versions only (e.g., "1.x" compatible with "1.y"), reject unknown majors.
    expected_major = str(SEAL_VERSION).split(".", 1)[0]
    actual_major = claims_seal_version.split(".", 1)[0]
    if expected_major != actual_major:
        return SealVerdict(False, "G0", f"unsupported seal_version {claims_seal_version}")

    # G1 — key pinning: only the pinned key + alg are trusted
    if alg != verifier.alg() or kid != verifier.key_id():
        return SealVerdict(False, "G1", f"unpinned key/alg ({alg}/{kid})")

```

1. Ensure that `SEAL_VERSION` is defined at module scope (e.g., `SEAL_VERSION = "1.0"` or similar). If it is not yet defined, add it near the top of `core/seal/seal.py` and update its value according to your current seal format versioning scheme.
2. If your project already has a helper for parsing/handling version strings, you may want to replace the simple `split(".", 1)[0]` logic with that helper for consistency.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread core/seal/seal.py
Comment on lines +98 to +107
def verify_seal(bundle: dict, seal: dict, verifier: Verifier) -> SealVerdict:
"""Offline verification — uses ONLY the pinned public key. Runs gates G0..G6; the first
failure decides."""
# G0 — structure
try:
claims = seal["claims"]
sig = seal["signature"]
alg, kid, value = sig["alg"], sig["key_id"], sig["value"]
b_sha, model, authority = claims["bundle_sha256"], claims["model"], claims["authority"]
except (KeyError, TypeError):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Consider explicitly validating seal_version in the verifier to catch incompatible or unexpected seal formats.

Right now we never compare seal["seal_version"] / claims["seal_version"] to SEAL_VERSION, so a seal with an older or newer format could still verify as long as the shape mostly matches and the signature is valid. Please add a version check (e.g., in G0 or a dedicated gate) that rejects unknown major versions so format changes can’t be silently accepted without explicit migration handling.

Suggested implementation:

def verify_seal(bundle: dict, seal: dict, verifier: Verifier) -> SealVerdict:
    """Offline verification — uses ONLY the pinned public key. Runs gates G0..G6; the first
    failure decides."""
    # G0 — structure
    try:
        claims = seal["claims"]
        sig = seal["signature"]
        alg, kid, value = sig["alg"], sig["key_id"], sig["value"]
        b_sha, model, authority = claims["bundle_sha256"], claims["model"], claims["authority"]
        claims_seal_version = claims["seal_version"]
    except (KeyError, TypeError):
        return SealVerdict(False, "G0", "malformed seal")

    # G0 — seal version compatibility
    # We require a known major version so that format changes cannot be silently accepted.
    if not isinstance(claims_seal_version, str):
        return SealVerdict(False, "G0", "seal_version must be a string")

    seal_seal_version = seal.get("seal_version", claims_seal_version)
    if not isinstance(seal_seal_version, str):
        return SealVerdict(False, "G0", "seal.seal_version must be a string if present")

    if seal_seal_version != claims_seal_version:
        return SealVerdict(False, "G0", "seal_version mismatch between envelope and claims")

    # Compare major versions only (e.g., "1.x" compatible with "1.y"), reject unknown majors.
    expected_major = str(SEAL_VERSION).split(".", 1)[0]
    actual_major = claims_seal_version.split(".", 1)[0]
    if expected_major != actual_major:
        return SealVerdict(False, "G0", f"unsupported seal_version {claims_seal_version}")

    # G1 — key pinning: only the pinned key + alg are trusted
    if alg != verifier.alg() or kid != verifier.key_id():
        return SealVerdict(False, "G1", f"unpinned key/alg ({alg}/{kid})")
  1. Ensure that SEAL_VERSION is defined at module scope (e.g., SEAL_VERSION = "1.0" or similar). If it is not yet defined, add it near the top of core/seal/seal.py and update its value according to your current seal format versioning scheme.
  2. If your project already has a helper for parsing/handling version strings, you may want to replace the simple split(".", 1)[0] logic with that helper for consistency.

gesh75 added a commit that referenced this pull request Jun 1, 2026
…authority gate, ceiling, CROSS-3 seal (#9)

Squashed release of the 8-PR stack (#2..#8): the offline, hardware-rootable bounded-autonomy
receipt and everything under it. AUTO disabled by default; the only un-built piece is the
hardware-PIV signer (docs/PIV_HARDWARE_SIGNER_PLAN.md) — software Ed25519 runs the whole demo.

- core/llm: single shared LLM egress + adapter (local-first fallback chain, retry/cooldown,
  4-event hook bus; raw httpx, no anthropic SDK; air-gap fail-closed). [#2]
- evidence: model-identity wedge — change.model_identity sealed into the bundle hash; honest
  UNKNOWN / unattested states, never a faked attribution. [#3]
- core/risk: deterministic AUTO/HITL/HOTL/BLOCK authority model (severity != authority;
  AS/RD/RT -> BLOCK; spine/underlay -> >= HOTL) + the no-self-escalation ceiling. [#4]
- core/promote: gate rule G5 enforces the ceiling from the sealed required tier;
  change.authority sealed (bundle_version 1.1->1.2). [#5]
- core/seal: CROSS-3 detached, offline-verifiable bounded-autonomy receipt (Ed25519; gates
  G0..G6; refuses to seal an unbounded change). [#6]
- serve: /api/preflight/run emits the seal; GET /api/seal/pubkey + POST /api/seal/verify;
  examiner PDF shows the seal. [#7]
- docs: PIV hardware-signer plan — the final, hardware-gated CROSS-3 pass. [#8]

Property-tested safety invariants: no-self-escalation 0-violations, every-seal-is-bounded,
tamper detection, offline-keyless verify, air-gap fail-closed.
@gesh75

gesh75 commented Jun 1, 2026

Copy link
Copy Markdown
Owner Author

Superseded by the squashed release #9, now merged to main. This PR's changes are in main; closing for tidiness (branch kept for history).

@gesh75 gesh75 closed this Jun 1, 2026
@gesh75
gesh75 deleted the feat/llm-seal branch June 1, 2026 18:10
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