Skip to content

feat(cli): add tls bundle profile#587

Merged
EffortlessSteven merged 2 commits into
mainfrom
feat/tls-bundle-profile
May 12, 2026
Merged

feat(cli): add tls bundle profile#587
EffortlessSteven merged 2 commits into
mainfrom
feat/tls-bundle-profile

Conversation

@EffortlessSteven

Copy link
Copy Markdown
Member

Why

Implements the v0.8.0 TLS contract-pack profile defined in
docs/release/v0.8.0-tls-profile-design.md (PR-A, merged as #585).

This is PR-B of the v0.8.0 lane:

  • PR-A (docs: define TLS contract-pack profile #585): design — TLS profile contract and bundle layout.
  • PR-B (this PR): CLI dispatch — uselesskey bundle --profile tls.
  • PR-C (follow-up): xtask bundle-proof --profile tls and release
    evidence routing.

Per the design analysis, this PR is wiring: the negative chain variants
already exist in uselesskey-x509::srp::chain_negative. The CLI just
adds a new BundleProfile arm and orchestration.

What

  • BundleProfile::Tls on the clap-derived profile enum, with the
    matching manifest_name() and parse_manifest_profile() arms so
    verify-bundle round-trips the profile.
  • New BundleEntry variants (TlsValidLeaf, TlsValidChain,
    TlsNegativeChain, TlsEvidenceDoc) feeding the existing
    bundle-generation + receipt pipeline. The
    receipts/materialization.json and receipts/audit-surface.json
    shape is shared with scanner-safe and oidc, so existing tooling
    (verify-bundle, inspect-bundle) keeps working unchanged.
  • Four negative chains generated via the existing
    uselesskey-x509::srp::chain_negative variants: ExpiredLeaf,
    NotYetValidLeaf, HostnameMismatch { wrong_hostname }, UnknownCa.
  • evidence/tls-profile.md emitted alongside the cert bundle with the
    per-fixture rejection-class table from the design doc.
  • Constants TLS_EXPECTED_HOSTNAME and TLS_WRONG_HOSTNAME baked into
    the bundle so downstream callers can assert against fixed strings.
  • CHANGELOG.md [Unreleased] entry documenting the new profile.

Bundle layout produced

certs/valid-leaf.pem
certs/valid-chain.pem
certs/negative-expired-leaf.pem
certs/negative-not-yet-valid.pem
certs/negative-wrong-hostname.pem
certs/negative-untrusted-root.pem
evidence/tls-profile.md
receipts/materialization.json
receipts/audit-surface.json
manifest.json

Files changed

  • crates/uselesskey-cli/src/main.rsBundleProfile::Tls, new
    BundleEntry arms, TLS-specific chain generator, evidence markdown
    renderer.
  • crates/uselesskey-cli/tests/tls_profile.rs (new) — integration
    tests.
  • CHANGELOG.md[Unreleased] Added bullet.

Validation

Ran clean locally:

  • cargo build -p uselesskey-cli --all-features --locked
  • cargo test -p uselesskey-cli --all-features (24 + 4 + 4 passed,
    including the 4 new tls_profile tests)
  • cargo xtask fmt
  • cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
    (post-policy: activate Clippy 1.94/1.95 ratchets #505 Clippy ratchets active, clean)
  • cargo xtask scanner-safe-reference --check (unchanged; this PR
    does not touch scanner-safe outputs)
  • cargo xtask cratesio-smoke --path . --skip-install-cli
  • cargo xtask docs-sync --check
  • cargo xtask typos
  • cargo xtask no-blob
  • cargo xtask check-file-policy
  • cargo xtask publish-check
  • git diff --check

Out of scope

  • Committed expected/ bundle artifacts under examples/ — the TLS
    bundle is meant to be regenerated under target/, mirroring the OIDC
    pack. Any committed proof artifact lands with PR-C.
  • docs/how-to/ guide — separate doc PR once the adapter surface is
    available.
  • uselesskey-rustls and uselesskey-tonic adapter helpers — separate
    PR/release per the design's adapter contract section.
  • xtask bundle-proof --profile tls — PR-C.

Test plan

  • cargo test -p uselesskey-cli --all-features --test tls_profile passes.
  • uselesskey bundle --profile tls --out target/foo produces the
    six certificate fixtures, the evidence doc, both receipts, and
    manifest.json.
  • uselesskey verify-bundle --path target/foo returns
    status: "ok" for the generated bundle.
  • Mutating any bundle file makes verify-bundle fail with
    content mismatch.
  • Re-running the bundle command produces byte-identical output
    (determinism invariant).
  • Scanner-safe reference workflow unchanged.

Implements the v0.8.0 TLS contract-pack profile defined by
docs/release/v0.8.0-tls-profile-design.md (PR-A, #585).

This is PR-B of the v0.8.0 lane: CLI wiring only, no adapter helpers.

What landed:
- BundleProfile::Tls variant on the clap-derived profile enum, the
  manifest_name() arm, and the parse_manifest_profile() round-trip so
  `verify-bundle` reads the same profile string back.
- New BundleEntry variants (TlsValidLeaf, TlsValidChain,
  TlsNegativeChain, TlsEvidenceDoc) feeding the existing bundle
  generation + receipt pipeline. The receipts/materialization.json and
  receipts/audit-surface.json shape is shared with scanner-safe and
  oidc, so existing tooling (`verify-bundle`, `inspect-bundle`) works
  unchanged.
- Four negative chains generated via the existing
  uselesskey-x509::srp::chain_negative variants: ExpiredLeaf,
  NotYetValidLeaf, HostnameMismatch { wrong_hostname }, UnknownCa.
- evidence/tls-profile.md emitted alongside the cert bundle with the
  per-fixture rejection-class table from the design doc.
- Documented expected hostname and wrong hostname constants
  (TLS_EXPECTED_HOSTNAME, TLS_WRONG_HOSTNAME) baked into the bundle so
  downstream callers can assert against fixed strings.

Tests: crates/uselesskey-cli/tests/tls_profile.rs covers layout,
single-cert vs full-chain PEM block counts, byte-identical determinism
across two invocations, and a `verify-bundle` round-trip with drift
detection.

Out of scope for this PR (tracked separately):
- adapter helpers in uselesskey-rustls / uselesskey-tonic
- bundle-proof --profile tls runner (PR-C)
- committed expected/ artifacts under examples/
- how-to guide under docs/how-to/

Cross-refs:
- design: docs/release/v0.8.0-tls-profile-design.md
- negative variants: crates/uselesskey-x509/src/srp/chain_negative.rs
@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@EffortlessSteven has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 34 minutes and 38 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f918d934-a51b-4a53-9571-f72d9c221bae

📥 Commits

Reviewing files that changed from the base of the PR and between c7d7b7c and d855805.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • crates/uselesskey-cli/src/main.rs
  • crates/uselesskey-cli/tests/tls_profile.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tls-bundle-profile

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d54e2f54f0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

BundleEntry::TlsNegativeChain { variant, .. } => {
let valid = tls_valid_chain(fx, label);
let negative = valid.negative(variant.to_chain_negative());
Ok(Artifact::Text(negative.leaf_cert_pem().to_string()))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Emit the unknown-CA chain, not just its leaf

When this entry is certs/negative-untrusted-root.pem, this writes only negative.leaf_cert_pem(). ChainNegative::UnknownCa changes the generated root identity, but the leaf is still issued by the same intermediate subject/key used by certs/valid-chain.pem, so file-based consumers that pair this leaf with the bundle's only published intermediate/root can build a trusted path and may accept the supposed unknown-CA fixture instead of exercising that rejection class. Emit the negative chain, or at least its alternate intermediate/root, for this variant.

Useful? React with 👍 / 👎.

Self::OidcValidJwks | Self::OidcNegativeJwks { .. } => Format::Jwks,
Self::OidcValidToken | Self::OidcNegativeToken { .. } => Format::JsonManifest,
Self::TlsValidLeaf | Self::TlsValidChain | Self::TlsNegativeChain { .. } => Format::Pem,
Self::TlsEvidenceDoc => Format::Pem,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Do not record Markdown evidence as PEM X.509

TlsEvidenceDoc is generated as evidence/tls-profile.md, but this records its format as pem (and the entry kind is X.509), so the manifest and receipts advertise a Markdown document as certificate material. Consumers that select x509/pem artifacts from the bundle metadata to parse or install TLS certificates will try to parse the evidence text as a certificate. Exclude the evidence doc from certificate artifact metadata or give it a non-certificate kind/format.

Useful? React with 👍 / 👎.

Close the mutation gap exposed by `cargo mutants` on
render_tls_evidence_markdown - the function could be replaced with
`"xyzzy".into()` and all tests still passed. Add a content assertion
that names each of the 6 fixture filenames and each failure class.
@EffortlessSteven EffortlessSteven merged commit aa68340 into main May 12, 2026
6 checks passed
EffortlessSteven added a commit that referenced this pull request May 12, 2026
Completes the v0.8.0 TLS lane started by the design doc in #585 and the
CLI implementation in #587. Extends `cargo xtask bundle-proof` with a
new `--profile tls` arm that produces the release-evidence proof
artifact for the TLS contract pack, mirroring the OIDC precedent shipped
in v0.7.0.

What this PR does:

- Adds `tls` to `BUNDLE_PROOF_SUPPORTED_PROFILES` and wires the
  per-profile default-out-dir / json-filename / markdown-filename /
  markdown-title / claim-boundary / expected-artifacts helpers.
- Adds `TLS_CONTRACT_PACK_PROOF_CLAIM_BOUNDARY` (matches the design-doc
  scope: pack shape, not downstream verifier correctness; no revocation,
  no mTLS, no production CA custody).
- Drives `bundle`, `verify-bundle`, `inspect-bundle`, the CLI tls
  contract-pack tests, x509 owner tests, and `no-blob` against the
  generated TLS bundle, and validates the 7 expected artifacts:
  4 cert PEMs (valid leaf, valid chain, 4 negative classes) plus the
  per-fixture rejection evidence markdown.
- Adds `tls-contract-pack-proof` to `release_evidence_steps_minor()`
  after `oidc-contract-pack-proof`. Patch lane stays unchanged: full
  contract-pack proofs are minor-only by design.
- Adds the "TLS contract-pack proof" row to the minor release-summary
  table next to the scanner-safe and OIDC rows.

Tests:

- `bundle_proof_tls_profile_constant_includes_tls` covers the supported
  profile list, helper outputs, default out dir, and the 7 expected
  artifact paths.
- `bundle_proof_receipt_enforces_tls_contract_pack_contents` and
  `bundle_proof_receipt_rejects_incomplete_tls_contract_pack` mirror the
  OIDC receipt tests against a `tls_bundle_proof_manifest()` helper.
- `bundle_proof_markdown_summarizes_tls_contract_checks` covers the
  rendered markdown.
- `release_evidence_minor_step_list_includes_tls_contract_pack_proof`
  and `release_evidence_patch_step_list_excludes_tls_contract_pack_proof`
  pin the step-list wiring on both lanes.
- Updates the existing minor-lane dry-run / summary tests to also assert
  the new TLS step and artifacts.

Validation:

- `cargo xtask bundle-proof --profile tls --out target/release-evidence/tls`
  succeeds end-to-end. Generated bundle has the 6 PEMs + evidence/tls-profile.md
  + 2 receipts; `tls-contract-pack-proof.{json,md}` are written with all
  7 contract-pack checks present.
- `cargo xtask bundle-proof --profile scanner-safe` and `--profile oidc`
  still succeed (regression-clean).
- `cargo xtask release-evidence --version 0.8.0-dev --dry-run --summary`
  plans 16 commands including the new tls step; the `--patch --dry-run`
  variant plans 10 commands with tls excluded.
- `cargo fmt --check -p xtask` and `cargo clippy -p xtask --all-targets
  -- -D warnings` clean.

Out of scope:

- `crates/uselesskey-cli/` — the TLS profile already exists post-#587;
  this PR is xtask-only.
- Task-first TLS how-to docs (planned for PR-D).
- rustls/tonic adapter helpers (future release).

Cross-refs:

- Design: `docs/release/v0.8.0-tls-profile-design.md` (PR #585).
- CLI impl: PR #587.
- OIDC proof precedent (template followed here):
  `oidc-contract-pack-proof.{json,md}` and the
  `OIDC_CONTRACT_PACK_PROOF_CLAIM_BOUNDARY` constant.
EffortlessSteven added a commit that referenced this pull request May 12, 2026
Task-first user-facing how-to for the `uselesskey bundle --profile tls`
contract pack. Walks readers through generating the bundle, the six
certificate fixtures (valid leaf, valid chain, four negatives), the
expected verifier outcomes per fixture, the documented hostname
constants, and the release-grade evidence command.

Mirrors the OIDC how-to pattern: terse, task-first prose, explicit
"what this proves / does not prove" claim boundaries, and a
scanner-safety note that points back to publish-recovery and the
design doc for out-of-scope coverage.

Completes the v0.8.0 TLS contract-pack lane: design (#585) + CLI
profile (#587) + xtask bundle-proof (#588) + this user-facing piece.
Pure docs PR; no source changes.
@EffortlessSteven EffortlessSteven mentioned this pull request May 12, 2026
4 tasks
EffortlessSteven added a commit that referenced this pull request May 12, 2026
TLS contract-pack and public crate-surface cleanup release. Bumps
workspace to 0.8.0. CHANGELOG entry curated from merged PRs:
#485-#605 (TLS lane #585/#587/#588/#589, task-first how-tos
#590-#594, SRP collapse #595/#598/#599/#602, migration guide #603,
Clippy ratchets #505, dep bumps #484-#491).
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