Skip to content

feat(001)!: require a verified platform seal by default#7

Merged
bartekus merged 1 commit into
mainfrom
feat/seal-required-by-default
Jul 3, 2026
Merged

feat(001)!: require a verified platform seal by default#7
bartekus merged 1 commit into
mainfrom
feat/seal-required-by-default

Conversation

@bartekus

@bartekus bartekus commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

What

Flip the platform-seal posture to fail-closed by default. An unsealed
certificate (no platform countersign), or a sealed one with no JWKS supplied to
adjudicate it, is now rejected (exit 1) unless --allow-unsealed is
passed.

Why

The platform countersign is what binds a produced run to its admission
contract. Under the old default, a factory could simply omit the countersign
and a tenant's verify-certificate still went green (exit 0 with a notice);
only the opt-in --require-sealed made it fail. For a verifier whose whole
posture is "do not trust the producer," the safer default is to reject a
certificate that is not bound to its admission contract, and make accepting one
the explicit, visible choice.

What changed

  • CLI: --require-sealed is replaced by --allow-unsealed (the opt-out).
    The old flag is kept as a hidden, deprecated no-op (it is now the default) so
    existing invocations keep running, with a one-line deprecation notice.
  • Core (certificate.rs): the seal branch messages now name the flipped
    default and the --allow-unsealed opt-out, and drop the stale --jwks-url
    reference (tenant-tail links no HTTP client; spec 003 §Behavior already
    forbids that flag).
  • Spec 001 §Behavior: amended to make the fail-closed default authoritative
    (the deliberate, human-authorized product decision behind this PR).
  • Tests: CLI exit-1-by-default and --allow-unsealed exit-0 cases, plus
    two core parity tests over the real signed fixture (seal-required rejects,
    unsealed-allowed notices). The seal posture had no direct test before.
  • Docs: website/docs and CLAUDE.md updated; the getting-started
    quickstart now passes --allow-unsealed for the unsealed fixture (which would
    otherwise now exit 1).

Verification

  • cargo fmt --check, cargo clippy -D warnings, cargo test --workspace all
    green (6 CLI tests, 6 certificate-parity tests, 87 core, 30 types).
  • Drove the binary end to end: no flag -> exit 1 (INVALID, names the opt-out);
    --allow-unsealed -> exit 0 (VERIFIED, notice); deprecated --require-sealed
    -> notice + exit 1; both flags -> opt-out wins.
  • spec-spine compile / index check / lint --fail-on-warn clean;
    .derived regenerated for spec 001.

Spec-Drift-Waiver: crates/tenant-tail-cli/src/main.rs and crates/tenant-tail-cli/tests/cli.rs are the CLI verb-surface wiring and exit-code test that expose the platform-seal default; the authoritative behavior change is in spec 001 §Behavior, amended in this PR. Spec 003 holds certificate verdict logic explicitly out of scope and spec 000 is workspace bootstrap, so neither is the correct home for seal semantics. Waived per .claude/rules/adversarial-prompt-refusal.md: waive with a citation rather than amend an off-topic owning spec to satisfy a mechanical coupling refresh.

An unsealed certificate, or a sealed one with no JWKS to adjudicate it,
previously verified with exit 0 and a notice; only --require-sealed made it
fail. That let a factory omit the platform countersign (what binds a run to
its admission contract) and still get a green verify. Flip the default to the
trust-nobody posture: such a certificate is now rejected (exit 1) unless
--allow-unsealed is passed.

- CLI: replace --require-sealed with --allow-unsealed (the opt-out); the old
  flag is kept as a hidden, deprecated no-op so existing invocations still run.
- Core: the seal message text now reflects the flipped default and the opt-out,
  and drops the stale --jwks-url reference (tenant-tail links no HTTP client).
- Spec 001 Behavior amended to make the fail-closed default authoritative.
- Tests: CLI exit-1-by-default + --allow-unsealed exit-0 cases, and two core
  parity tests over the real signed fixture (seal-required vs unsealed-allowed).
- Docs (website + CLAUDE.md) updated; the getting-started quickstart now passes
  --allow-unsealed for the unsealed fixture.
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

AI Code Review

Code Review

Bugs / Logic

Unverifiable test assertion — stderr containing "UNSEALED"
unsealed_certificate_is_rejected_by_default_exit_1 asserts stderr(&out).contains("UNSEALED"), but the error string is pushed to result.errors inside the core — it only reaches stderr if the CLI rendering layer writes errors there. The diff doesn't show that rendering path, so this assertion may silently pass for the wrong reason (e.g., if errors print to stdout and stderr happens to be empty). Confirm result.errors are written to stderr, not stdout, before relying on this test as a gate.

--require-sealed + --allow-unsealed combined not tested
seal_required = !args.allow_unsealed means --allow-unsealed unconditionally wins, even when --require-sealed is also present. That's the right call, but the deprecation notice fires and the seal is not required — a user who meant "use the old flag" gets a surprising opt-out. A single test confirming the combined invocation exits 0 with a notice would document the precedence explicitly.

Deprecation notice has no test
No CLI test verifies that --require-sealed alone emits the deprecation notice on stderr. The path is only exercised implicitly by the parity tests that don't touch the CLI.

Security

No regressions. The change is a net security improvement: fail-closed on absent seal is the correct trust-nobody posture. No injection surface in the hard-coded error strings.

Minor: error message leaks opt-out flag
"pass --allow-unsealed to accept an unsealed certificate" in the rejection error is fine for a CLI tool. No concern here.

Spec-Spine Compliance

  • specs/001-certificate-verify-core/spec.md is updated in the same commit as the implementation. Coupling discipline maintained. ✓
  • .derived/ shard hashes updated for both codebase-index and spec-registry. ✓
  • The references to spec 198 FR-014 are external OAP specs, not local specs/-owned claims — no local coupling violation. ✓
  • Old messages mentioned --jwks-url (a flag that CLAUDE.md explicitly says was intentionally omitted from tenant-tail). Those references are removed here. This is a documentation correctness fix alongside the behavioral change. ✓
  • [package.metadata.tenant-tail].spec annotations are unchanged; no new external uses: refs. ✓

One observation: CLAUDE.md now says "--require-sealed, now the default, is accepted as a no-op" — but the #[arg(long, hide = true)] annotation means --help won't surface it. That's intentional, but worth noting for users relying on --help output to discover options.

Performance

No performance concerns. All changes are in CLI argument parsing, error-message string construction, and test harness — none are on any hot path.


Automated review by Claude. Diff size: 141 lines.

@bartekus
bartekus merged commit 7f46e34 into main Jul 3, 2026
9 checks passed
@bartekus
bartekus deleted the feat/seal-required-by-default branch July 3, 2026 00:24
bartekus added a commit that referenced this pull request Jul 3, 2026
First release since v0.2.0, bundling PRs #5, #6, and #7. PR #7 is a breaking
change to verify-certificate, so this is a minor bump under 0.x.

- Breaking (#7, spec 001): verify-certificate requires a verified platform seal
  by default; an unsealed or unadjudicated certificate exits 1. Restore the prior
  behavior with --allow-unsealed. --require-sealed is now a deprecated no-op.
- Added (#6, spec 003): release builds assert the manylinux_2_17 glibc floor.
- Fixed (#5): hardened the verify cores, exit-code contract, and release pipeline.

Version bumped in lockstep across Cargo.toml, npm/package.json (main + five
platform pins), and py/pyproject.toml; Cargo.lock and the codebase-index shard
regenerated. New CHANGELOG.md carries the migration note for pinned tenants.
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