Skip to content

feat: complete v0.3 roadmap with new key types, adapters, and tooling#1

Merged
EffortlessSteven merged 32 commits into
mainfrom
feat/roadmap-completion
Feb 12, 2026
Merged

feat: complete v0.3 roadmap with new key types, adapters, and tooling#1
EffortlessSteven merged 32 commits into
mainfrom
feat/roadmap-completion

Conversation

@EffortlessSteven

Copy link
Copy Markdown
Member

Summary

  • Add ECDSA (P-256/P-384), Ed25519, HMAC, X.509 (self-signed + chain), and JWK/JWKS support as new crates
  • Add adapter crates for jsonwebtoken, ring, rustcrypto, rustls, and aws-lc-rs integration
  • Add comprehensive xtask tooling: ci, pr, fmt, clippy, bdd, fuzz, mutants, deny, feature-matrix, publish-check, coverage, publish-preflight, no-blob, nextest

Changes

New key type crates

  • uselesskey-ecdsa — ECDSA P-256/P-384 fixtures via EcdsaFactoryExt
  • uselesskey-ed25519 — Ed25519 fixtures via Ed25519FactoryExt
  • uselesskey-hmac — HMAC HS256/HS384/HS512 fixtures via HmacFactoryExt
  • uselesskey-x509 — X.509 self-signed certs, 3-level chains, CRLs, and negative fixtures (expired, not-yet-valid, hostname mismatch, unknown CA, revoked) via X509FactoryExt
  • uselesskey-jwk — Typed JWK/JWKS helpers and JwksBuilder

New adapter crates

  • uselesskey-jsonwebtoken — Returns jsonwebtoken EncodingKey/DecodingKey directly
  • uselesskey-ring — Converts fixtures to ring signing key types
  • uselesskey-rustcrypto — Converts fixtures to RustCrypto signing/verifying types
  • uselesskey-rustls — Converts fixtures to rustls types with ServerConfig/ClientConfig builders
  • uselesskey-aws-lc-rs — Converts fixtures to aws-lc-rs key types

Core improvements

  • uselesskey-core: enhanced error types, Seed::from_env_value(), negative prop tests, factory env tests
  • uselesskey-rsa: RsaSpec with stable_bytes(), JWK support, extended prop tests
  • Facade crate updated with feature flags for all new key type crates

Tooling & CI

  • Comprehensive xtask commands for CI, PR-scoped testing, feature matrix, publish checks, coverage, etc.
  • PR-scoped testing with cargo xtask pr (JSON receipt output)
  • BDD test expansion: 9 features, 91 scenarios, 398 steps covering all key types

Docs & project files

  • CLAUDE.md, AGENTS.md, migration guide, release guide, requirements docs
  • Updated README with comprehensive usage examples
  • Updated roadmap and changelog

Evidence

  • Commits: 24
  • Tests: 200+ passed across 16 crates (BDD: 91 scenarios/398 steps all passed)
  • Lint: clippy clean (0 warnings)
  • Files: 105 changed, +16,914/-173 lines
  • Note: test_chain_der_rustls in uselesskey-rustls is a known slow test (~minutes) due to random-mode RSA-2048 keygen for 3 chain keys; all other tests pass quickly

Test Plan

  • cargo xtask test — full test suite (200+ tests passing)
  • cargo xtask clippy — clean
  • BDD scenarios — 91/91 passing
  • cargo xtask ci — full CI pipeline (includes fuzz, mutants, deny — requires additional tooling)

Unknowns

  • test_chain_der_rustls uses Factory::random() which generates 3 RSA-2048 keys; this can take several minutes in debug builds. Consider switching to deterministic or ECDSA keys in a follow-up.
  • Fuzz and mutation testing were not run as part of this verification (require cargo-fuzz and cargo-mutants to be installed).
  • uselesskey-aws-lc-rs adapter was added but may require platform-specific build dependencies (cmake/nasm).

- Updated CLAUDE.md to clarify the problem with secret scanners and added context on ecosystem gaps.
- Modified Cargo.lock to include `serde_json` and `uselesskey-core` dependencies.
- Revised README.md to improve clarity on outputs and added context on secret incidents.
- Expanded BDD tests in rsa.feature to cover additional scenarios for deterministic and random RSA key generation.
- Updated bdd.rs to support new scenarios and added handling for corrupted PEM and truncated DER.
- Introduced new features for JWK generation and negative fixtures with corresponding feature files.
- Added migration guide to assist users in transitioning from committed key fixtures to runtime generation.
- Implemented Ed25519 keypair generation with PKCS#8 support in the `uselesskey-ed25519` crate.
- Added specifications for Ed25519 keys, including stable encoding for deterministic derivation.
- Introduced X.509 certificate generation in the `uselesskey-x509` crate, supporting self-signed certificates and various key usages.
- Implemented negative testing fixtures for X.509 certificates, allowing for the generation of intentionally invalid certificates for error handling.
- Updated documentation for usage examples and negative fixture generation.
- Introduced new commands: `FeatureMatrix`, `NoBlob`, and `PublishCheck` in the xtask CLI.
- Implemented `ci` function to run common CI tasks including feature matrix checks.
- Added `run_feature_matrix` function to validate various feature combinations.
- Created `publish_check` function to perform dry-run publishes for crates in dependency order.
- Added HMAC secret fixture generation with tests for JWK and JWKS.
- Implemented new crates: `uselesskey-hmac` and `uselesskey-jwk` for HMAC and JWK handling.
- Updated documentation for release and requirements to reflect new features and commands.
- Introduced a new crate `uselesskey-jsonwebtoken` for integrating JWT functionality with uselesskey test fixtures.
- Implemented extension traits for encoding and decoding JWTs using RSA, ECDSA, Ed25519, and HMAC keys.
- Added examples demonstrating signing and verifying JWTs with different key types.
- Updated README to reflect the new crate and its usage.
- Enhanced existing examples to showcase JWK/JWKS generation and tempfile handling for key fixtures.
- Added `uselesskey-jsonwebtoken` as a member in the workspace and included necessary dependencies.
The no-blob check was flagging .rs test files that contain PEM markers
as strings (e.g., "-----BEGIN TEST KEY-----" in PEM corruption tests).
These are source code, not committed fixtures.

Also adds mutants.out/ to .gitignore.
…odebase

- Created `timeout.txt` to track timeout-related mutants.
- Created `unviable.txt` to document unviable mutants with specific code replacements using `Default::default()` for various structures across multiple crates.
- Bump Rust edition from 2021 to 2024 in Cargo.toml and fuzz/Cargo.toml.
- Update various dependencies to their latest versions, including:
  - thiserror to 2.0.18
  - blake3 to 1.8.3
  - dashmap to 6.1.0
  - tempfile to 3.24.0
  - proptest to 1.10.0
  - rstest to 0.26.1
  - anyhow to 1.0.101
  - clap to 4.5.57
  - cucumber to 0.22.1
  - tokio to 1.49.0
  - rsa to 0.9.10
  - ed25519-dalek to 2.2.0
  - serde to 1.0.228
  - serde_json to 1.0.149
  - base64 to 0.22.1
- Update Clippy MSRV to 1.92.
- Adjust versions of dependencies in the `uselesskey-bdd` crate.
- Refactor imports in various files for clarity and consistency.
- Ensure safety in environment variable manipulation in doctests.
…tures

- Added `X509Chain` struct for generating a three-level X.509 certificate chain (root CA, intermediate CA, leaf).
- Introduced `ChainSpec` struct to define specifications for certificate generation, including common names, validity periods, and RSA key sizes.
- Created utility functions for deterministic base time and serial number generation.
- Implemented negative testing scenarios for certificate chains, including hostname mismatches, unknown CAs, and expired certificates.
- Added comprehensive tests for chain generation, PEM formatting, determinism, and negative cases.
…25519 dead_code)

- x509: default validity 365d -> 3650d for self-signed + chain leaves
- x509: reuse RSA keys across chain/self-signed variants (no variant in key labels)
- x509: add tests for >=5y validity and key reuse across negative variants
- ed25519: cfg_attr allow(dead_code) for public field when jwk is disabled
- Introduced a new crate `uselesskey-ring` to provide integration with the `ring` library for RSA, ECDSA, and Ed25519 key types.
- Updated `Cargo.toml` files to include `uselesskey-ring` as a member and added necessary dependencies.
- Enhanced README.md to document the new crate and its usage examples.
- Implemented extension traits in `uselesskey-ring` for converting uselesskey fixtures into `ring` native signing key types.
- Added convenience builders for `rustls::ServerConfig` and `rustls::ClientConfig` in `uselesskey-rustls`.
- Implemented support for CRLs in X.509 chains, including tests for revoked certificates.
- Updated xtask scripts to include `uselesskey-ring` in publish checks and dependency tracking.
…ryptographic libraries

- Implement tests for deterministic factory creation and environment variable handling in `uselesskey-core`.
- Add comprehensive tests for ECDSA keypair generation and validation in `uselesskey-ecdsa`.
- Introduce tests for Ed25519 keypair functionality in `uselesskey-ed25519`.
- Create README files for `uselesskey-jsonwebtoken`, `uselesskey-ring`, `uselesskey-rustcrypto`, and `uselesskey-rustls` with usage examples and feature descriptions.
- Implement RustCrypto integration for keypair conversion in `uselesskey-rustcrypto`.
- Add tests for HMAC functionality in `uselesskey-hmac`.
- Ensure facade tests validate core items and re-export functionality across different key types.
Copilot AI review requested due to automatic review settings February 9, 2026 12:04
@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

  • New Features

    • ECDSA, Ed25519, HMAC key fixtures; X.509 certs with chain and negative variants; JWK/JWKS exports; adapters for jwt, rustls, ring, RustCrypto, aws‑lc.
    • Examples: JWKS, JWT signing, TLS server, tempfile helpers, negative-fixtures.
  • Documentation

    • Expanded README, architecture, migration and release guides; many BDD scenarios and examples added.
  • Build & Testing

    • Toolchain bumped to Rust 1.92/edition 2024; CI workflow and test harnesses expanded; improved test coverage and fuzzing support.

Walkthrough

Renames CI job and adds a main job; introduces many new workspace crates (ECDSA, Ed25519, HMAC, X.509, JWK, adapters), upgrades workspace edition/rust-version, expands xtask (plan/receipt/etc.), large test/BDD additions, new examples/docs, and numerous public API additions across fixture and adapter crates.

Changes

Cohort / File(s) Summary
CI / Workflows
.github/workflows/ci.yml, .github/settings.yml, .github/CODEOWNERS, clippy.toml, rust-toolchain.toml
Renamed testpr, added main job, pinned toolchain to 1.92 + nightly, added fetch-depth, caching, cargo-fuzz/cargo-mutants installs, base-branch fetch step, and receipt upload steps; updated repo metadata and MSRV.
Workspace / Root Manifests
Cargo.toml, deny.toml, .gitignore, rustfmt.toml, fuzz/Cargo.toml
Workspace edition→2024, rust-version→1.92; many new workspace members added; dependency version bumps; profile opt-level changes; added OpenSSL to allowed licenses; .gitignore updated for cargo-mutants artifacts; rustfmt options removed.
xtask / CI Orchestration
xtask/src/main.rs, xtask/src/plan.rs, xtask/src/receipt.rs, xtask/Cargo.toml
Large xtask expansion: Plan builder for impacted crates, plan-driven CI/PR flows, Runner/Receipt for step timing and JSON write, feature-matrix, no-blob scanning, publish preflight, fuzz/coverage helpers; added serde/tempfile deps and new public commands.
Core & utilities
crates/uselesskey-core/*
Factory API extended (new/random/deterministic/deterministic_from_env, clear_cache, mode, Debug), pub(crate) helpers (random_seed, downcast_or_panic), TempArtifact I/O APIs, negative utilities (pem/der), many unit/property tests, and version/metadata bumps.
Facade & root crate
crates/uselesskey/src/lib.rs, crates/uselesskey/Cargo.toml, examples/*
Facade reworked to feature-gated exports (rsa/ecdsa/ed25519/hmac/x509/jwk), new composite features (all-keys/full), many examples (jwks, jwt_signing, tempfiles, tls_server, negative_fixtures), README rewritten.
JWK crate
crates/uselesskey-jwk/*
New typed JWK/JWKS crate with builder, deterministic ordering, to_value/Display helpers, and tests.
RSA updates
crates/uselesskey-rsa/*
Package bumped to 0.2.0, RsaSpec expanded, JWK integration moved to typed uselesskey-jwk, new JWK/JWKS helpers and JSON helpers, internal field rename _private, and expanded tests.
ECDSA crate
crates/uselesskey-ecdsa/*
New crate: EcdsaSpec, EcdsaKeyPair, Factory extension, deterministic generation, PEM/SPKI/JWK helpers (feature-gated), mismatch/corrupt/truncate helpers, and tests.
Ed25519 crate
crates/uselesskey-ed25519/*
New crate: Ed25519Spec, Ed25519KeyPair, Factory extension, PEM/SPKI/JWK helpers (feature-gated), truncation/corruption/mismatch helpers, and tests.
HMAC crate
crates/uselesskey-hmac/*
New crate: HmacSpec, HmacSecret, Factory extension, deterministic secret generation, kid/JWK/JWKS helpers (feature-gated), and tests.
X.509 crate
crates/uselesskey-x509/*
New crate: X509Spec, X509Cert, X509Chain, ChainSpec, deterministic time/serial helpers, negative variants (expired, not-yet-valid, wrong-key-usage, revoked, hostname mismatch), CRL support, tempfile writers, and extensive tests.
Adapters / Integrations
crates/uselesskey-jsonwebtoken/*, crates/uselesskey-rustcrypto/*, crates/uselesskey-ring/*, crates/uselesskey-rustls/*, crates/uselesskey-aws-lc-rs/*
Five new adapter crates exposing feature-gated extension traits converting fixtures into jsonwebtoken, RustCrypto, ring, rustls, and aws-lc types; include manifests, implementations, tests, and READMEs.
BDD test harness & features
crates/uselesskey-bdd/*, crates/uselesskey-bdd/features/*, crates/uselesskey-bdd/tests/bdd.rs
Massive BDD expansion: many new Gherkin feature files (rsa, ecdsa, ed25519, hmac, jwk, jwks, x509, negative, seed, tempfile, chain, cross_key, etc.) and a large test-world implementation tracking artifacts across types and negative scenarios.
Tests / Proptest fixtures
crates/*/tests/*, crates/uselesskey-core/tests/*
Widespread unit, property, and integration tests added or expanded across crates, proptest regression seeds added, and many test utilities (fx() helpers).
Docs & Release
README.md, CHANGELOG.md, CLAUDE.md, GEMINI.md, CONTRIBUTING.md, docs/*
Extensive documentation: README rewrite, CHANGELOG, architecture, migration guide, release notes, requirements, roadmap, contributor guidance, and new per-crate READMEs.
Misc
.github/*, deny.toml, .gitignore
Repo metadata files added/updated, CODEOWNERS/settings.yml added, deny license list extended, and gitignore updated for mutants artifacts.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant GH as "GitHub Actions"
participant XTASK as "xtask (CI runner)"
participant RUN as "Receipt::Runner"
participant ART as "Artifact store (upload)"

GH->>XTASK: trigger pr/main job (checkout + run xtask)
XTASK->>RUN: Runner::new(output_path)
XTASK->>RUN: run plan (build_plan -> execute steps)
RUN->>XTASK: step(result) (records status & duration)
XTASK->>ART: upload receipt JSON (target/xtask/receipt.json)
RUN->>ART: write() (persist receipt JSON)
GH->>ART: store artifact (receipt uploaded)

(Note: rectangles/colors omitted for simplicity)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐇 I hopped through crates and CI streams,
Seeds and JWKs and test-time dreams.
Chains and certs and adapters stitched,
Receipts that tell what each step hitched,
A rabbit’s cheer — the fixtures gleam!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: complete v0.3 roadmap with new key types, adapters, and tooling' is clear, specific, and accurately summarizes the main change—completing a major feature roadmap with multiple new cryptographic key types, adapter integrations, and tooling enhancements.
Description check ✅ Passed The pull request description is comprehensive and directly related to the changeset. It clearly explains the new crates, adapters, core improvements, tooling enhancements, and documentation updates introduced by the PR, with evidence of test results and known limitations.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/roadmap-completion

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.

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @EffortlessSteven, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request marks the completion of the v0.3 roadmap for the uselesskey library, focusing on expanding its utility as a test-fixture generation tool. The primary goal is to prevent the need for committing secret-shaped blobs into version control by providing robust runtime generation of various cryptographic key types and X.509 certificates. This release significantly broadens the supported algorithms and output formats, introduces specialized adapter crates for integration with external crypto libraries, and establishes a comprehensive set of development and CI/CD tooling to ensure project quality and maintainability.

Highlights

  • New Key Types and X.509 Support: Introduced new crates for ECDSA (P-256/P-384), Ed25519, HMAC (HS256/HS384/HS512), X.509 certificates (self-signed and 3-level chains), and JWK/JWKS helpers, significantly expanding the library's cryptographic fixture generation capabilities.
  • New Adapter Crates for Ecosystem Integration: Added dedicated adapter crates for seamless integration with popular Rust crypto and TLS libraries, including jsonwebtoken, ring, rustcrypto, rustls, and aws-lc-rs, allowing direct conversion of uselesskey fixtures to native types of these libraries.
  • Enhanced Tooling and CI/CD Workflow: Implemented a comprehensive suite of xtask commands to streamline development and CI processes, covering tasks like ci, pr (PR-scoped testing), fmt, clippy, bdd, fuzz, mutants, deny, feature-matrix, publish-check, coverage, publish-preflight, and no-blob.
  • Core Library Improvements: The uselesskey-core crate received enhancements to error types, improved Seed::from_env_value() parsing, and expanded property-based tests for negative fixtures and factory environment handling.
  • Updated Documentation and Project Structure: Added new documentation files (AGENTS.md, CLAUDE.md, migration.md, release.md, requirements-v0.3.md) and updated the main README.md to provide comprehensive usage examples, design principles, and a clear roadmap.
Changelog
  • .gitignore
    • Added mutants.out*/ to ignore mutation testing output directories.
    • Added nul to ignore temporary files.
  • AGENTS.md
    • New file providing guidance to Claude Code AI for repository interaction, detailing project overview, strategic positioning, design principles, build commands, and architecture.
  • CHANGELOG.md
    • Added uselesskey-rustcrypto adapter crate for RustCrypto native types (rsa, p256, p384, ed25519-dalek, hmac).
    • Added uselesskey-aws-lc-rs adapter crate for aws-lc-rs native types with native feature for wasm-safe builds.
    • Added ChainNegative::RevokedLeaf variant with CRL signed by intermediate CA (uselesskey-x509).
    • Added RustlsServerConfigExt / RustlsClientConfigExt / RustlsMtlsExt config builders (uselesskey-rustls).
    • Added server_config_mtls_rustls_with_provider() / client_config_mtls_rustls_with_provider() for mTLS with explicit crypto provider (uselesskey-rustls).
    • Added uselesskey-ring adapter crate for ring 0.17 native signing key types.
    • Added cross-key failure test in uselesskey-jsonwebtoken.
    • Added deterministic-mode test in uselesskey-ring.
    • Added per-crate README files for all adapter crates.
    • Added crate-level //! docs for uselesskey-hmac and uselesskey-jwk.
    • Added 'Why not just...' comparison section in root README.
    • Updated README with X.509, adapter crates, TLS config builder, and ecosystem sections.
  • CLAUDE.md
    • New file providing guidance to Claude Code AI for repository interaction, detailing project overview, strategic positioning, design principles, build commands, and architecture.
  • Cargo.lock
    • New file, automatically generated by Cargo, reflecting updated and new dependencies.
  • Cargo.toml
    • Updated workspace edition to '2024' and rust-version to '1.92'.
    • Updated various workspace dependency versions to their latest major releases (e.g., thiserror = "2.0.18", blake3 = "1.8.3", dashmap = "6.1.0", clap = "4.5.57", cucumber = "0.22.1", tokio = "1.49.0", rsa = "0.9.10", serde = "1.0.228", serde_json = "1.0.149", base64 = "0.22.1").
    • Added new workspace members for uselesskey-jwk, uselesskey-ecdsa, uselesskey-ed25519, uselesskey-hmac, uselesskey-x509, uselesskey-jsonwebtoken, uselesskey-rustls, uselesskey-ring, uselesskey-rustcrypto, uselesskey-aws-lc-rs.
  • README.md
    • Significantly expanded the README to include sections on 'The Problem', 'Why Not Just...', 'What You Get', 'Quick Start', 'JWK / JWKS', 'Tempfiles', 'X.509 Certificates', 'X.509 Negative Fixtures', 'Negative Fixtures (Keys)', 'Adapter Crates', 'Feature Flags', 'Why This Crate?', 'When NOT to use this crate', and 'Ecosystem'.
  • clippy.toml
    • Updated minimum supported Rust version (MSRV) to '1.92'.
  • crates/uselesskey-aws-lc-rs/Cargo.toml
    • New crate definition for uselesskey-aws-lc-rs adapter.
  • crates/uselesskey-aws-lc-rs/README.md
    • New README for uselesskey-aws-lc-rs adapter.
  • crates/uselesskey-aws-lc-rs/src/lib.rs
    • New implementation for uselesskey-aws-lc-rs adapter, providing AwsLcRsRsaKeyPairExt, AwsLcRsEcdsaKeyPairExt, and AwsLcRsEd25519KeyPairExt traits.
  • crates/uselesskey-bdd/Cargo.toml
    • Updated dependencies to include new key types (ed25519-dalek, p256, p384, x509-parser) and enable corresponding features in uselesskey.
  • crates/uselesskey-bdd/features/ecdsa.feature
    • New BDD feature file for ECDSA fixtures, covering determinism, random mode, key formats, negative fixtures, and JWK support.
  • crates/uselesskey-bdd/features/ed25519.feature
    • New BDD feature file for Ed25519 fixtures, covering determinism, random mode, key formats, negative fixtures, and JWK support.
  • crates/uselesskey-bdd/features/hmac.feature
    • New BDD feature file for HMAC fixtures, covering determinism and JWK/JWKS support.
  • crates/uselesskey-bdd/features/jwk.feature
    • New BDD feature file for JWK generation, covering public/private JWK fields, JWKS structure, and KID determinism.
  • crates/uselesskey-bdd/features/negative.feature
    • New BDD feature file for generic negative fixtures, covering various PEM corruption types and DER truncation.
  • crates/uselesskey-bdd/features/rsa.feature
    • Expanded BDD feature file for RSA fixtures, adding scenarios for determinism, random mode, key formats, and mismatched keys.
  • crates/uselesskey-bdd/features/seed.feature
    • New BDD feature file for seed parsing, covering hex and string seed formats and their deterministic behavior.
  • crates/uselesskey-bdd/features/tempfile.feature
    • New BDD feature file for tempfile artifacts, covering writing private/public keys to temporary files.
  • crates/uselesskey-bdd/features/x509.feature
    • New BDD feature file for X.509 certificate fixtures, covering determinism, random mode, certificate formats, metadata, negative fixtures (expired, not-yet-valid, corruption), and tempfile outputs.
  • crates/uselesskey-bdd/tests/bdd.rs
    • Updated BDD test implementation to support new key types (Ed25519, ECDSA, HMAC, X.509) and their associated features, including JWK, negative fixtures, and tempfiles.
  • crates/uselesskey-core/Cargo.toml
    • Updated crate metadata including description, categories, keywords, readme, and authors.
    • Removed publish = false.
  • crates/uselesskey-core/src/derive.rs
    • Added tests for derive_seed to ensure determinism across different derivation versions.
  • crates/uselesskey-core/src/error.rs
    • Added tests for error messages to ensure they are readable and informative.
  • crates/uselesskey-core/src/factory.rs
    • Added comprehensive documentation and examples for Factory and Mode.
    • Implemented Debug trait for Factory to include cache size in debug output.
    • Added tests for clear_cache behavior, type mismatch panics in get_or_init, and Debug output.
  • crates/uselesskey-core/src/id.rs
    • Added extensive documentation and examples for Seed and ArtifactId.
    • Added tests for Seed debug redaction, hex parsing, and ArtifactId fingerprinting.
  • crates/uselesskey-core/src/lib.rs
    • Added detailed documentation on the library's architecture and extension pattern.
  • crates/uselesskey-core/src/negative/der.rs
    • Added documentation and examples for truncate_der and flip_byte functions.
    • Added tests for flip_byte and truncate_der functionality.
  • crates/uselesskey-core/src/negative/mod.rs
    • Added an overview and examples for negative fixture helpers.
  • crates/uselesskey-core/src/negative/pem.rs
    • Added documentation and examples for CorruptPem enum and corrupt_pem function.
    • Added tests for various PEM corruption scenarios.
  • crates/uselesskey-core/src/sink/tempfile.rs
    • Added documentation and examples for TempArtifact struct.
    • Implemented Debug trait for TempArtifact.
    • Added tests for TempArtifact functionality, including round-trip reads, invalid UTF-8 handling, and file deletion on drop.
  • crates/uselesskey-core/tests/core_prop.proptest-regressions
    • New file for proptest regression seeds.
  • crates/uselesskey-core/tests/core_prop.rs
    • Added extensive property-based tests for Seed::from_env_value parsing, derivation uniqueness, truncate_der, and corrupt_pem.
  • crates/uselesskey-core/tests/factory_env.rs
    • New tests for Factory::deterministic_from_env to verify correct environment variable parsing and error handling.
  • crates/uselesskey-core/tests/negative_prop.rs
    • New property-based tests for corrupt_pem, flip_byte, and truncate_der functions, ensuring expected behavior across various inputs.
  • crates/uselesskey-ecdsa/Cargo.toml
    • New crate definition for uselesskey-ecdsa.
  • crates/uselesskey-ecdsa/src/keypair.rs
    • New implementation for ECDSA keypair generation and output formats.
  • crates/uselesskey-ecdsa/src/lib.rs
    • New library definition for uselesskey-ecdsa, providing ECDSA key fixtures.
  • crates/uselesskey-ecdsa/src/spec.rs
    • New specification for ECDSA key generation (EcdsaSpec).
  • crates/uselesskey-ecdsa/tests/jwk_private.rs
    • New tests for ECDSA JWK generation and format validation.
  • crates/uselesskey-ecdsa/tests/keypair.rs
    • New tests for ECDSA keypair parsing, determinism, negative fixtures, and tempfile outputs.
  • crates/uselesskey-ed25519/Cargo.toml
    • New crate definition for uselesskey-ed25519.
  • crates/uselesskey-ed25519/src/keypair.rs
    • New implementation for Ed25519 keypair generation and output formats.
  • crates/uselesskey-ed25519/src/lib.rs
    • New library definition for uselesskey-ed25519, providing Ed25519 key fixtures.
  • crates/uselesskey-ed25519/src/spec.rs
    • New specification for Ed25519 key generation (Ed25519Spec).
  • crates/uselesskey-ed25519/tests/jwk_private.rs
    • New tests for Ed25519 JWK generation and format validation.
  • crates/uselesskey-ed25519/tests/keypair.rs
    • New tests for Ed25519 keypair parsing, determinism, negative fixtures, and tempfile outputs.
  • crates/uselesskey-hmac/Cargo.toml
    • New crate definition for uselesskey-hmac.
  • crates/uselesskey-hmac/README.md
    • New README for uselesskey-hmac.
  • crates/uselesskey-hmac/src/lib.rs
    • New library definition for uselesskey-hmac, providing HMAC secret fixtures.
  • crates/uselesskey-hmac/src/secret.rs
    • New implementation for HMAC secret generation and output formats.
  • crates/uselesskey-hmac/src/spec.rs
    • New specification for HMAC secret generation (HmacSpec).
  • crates/uselesskey-jsonwebtoken/Cargo.toml
    • New crate definition for uselesskey-jsonwebtoken.
  • crates/uselesskey-jsonwebtoken/README.md
    • New README for uselesskey-jsonwebtoken adapter.
  • crates/uselesskey-jsonwebtoken/src/lib.rs
    • New implementation for uselesskey-jsonwebtoken adapter, providing JwtKeyExt trait for various key types.
  • crates/uselesskey-jwk/Cargo.toml
    • New crate definition for uselesskey-jwk.
  • crates/uselesskey-jwk/README.md
    • New README for uselesskey-jwk.
  • crates/uselesskey-jwk/src/lib.rs
    • New library definition for uselesskey-jwk, providing typed JWK/JWKS helpers.
  • crates/uselesskey-ring/Cargo.toml
    • New crate definition for uselesskey-ring adapter.
  • crates/uselesskey-ring/README.md
    • New README for uselesskey-ring adapter.
  • crates/uselesskey-ring/src/lib.rs
    • New implementation for uselesskey-ring adapter, providing RingRsaKeyPairExt, RingEcdsaKeyPairExt, and RingEd25519KeyPairExt traits.
  • crates/uselesskey-rsa/Cargo.toml
    • Updated crate metadata including description, categories, keywords, readme, and authors.
    • Removed publish = false.
    • Updated jwk feature to depend on uselesskey-jwk.
  • crates/uselesskey-rsa/src/keypair.rs
    • Updated RsaKeyPair struct to include _private and public fields.
    • Added public_key_jwk, private_key_jwk, public_jwks, public_jwk_json, public_jwks_json, private_key_jwk_json methods for JWK support.
  • crates/uselesskey-rsa/src/lib.rs
    • Added extensive documentation and examples for RSA key fixtures, including deterministic mode, negative fixtures, tempfiles, and JWK support.
  • crates/uselesskey-rsa/src/spec.rs
    • Added documentation and examples for RsaSpec.
    • Added tests for RsaSpec defaults, new method, and stable bytes encoding.
  • crates/uselesskey-rsa/tests/rsa_prop.rs
    • Added tests to ensure panics for RSA bits too small or custom public exponent.
    • Added extensive property-based tests for all spec configurations, tempfile outputs, and JWK determinism/fields.
  • crates/uselesskey-rustcrypto/Cargo.toml
    • New crate definition for uselesskey-rustcrypto adapter.
  • crates/uselesskey-rustcrypto/README.md
    • New README for uselesskey-rustcrypto adapter.
  • crates/uselesskey-rustcrypto/src/lib.rs
    • New implementation for uselesskey-rustcrypto adapter, providing RustCryptoRsaExt, RustCryptoEcdsaExt, RustCryptoEd25519Ext, and RustCryptoHmacExt traits.
  • crates/uselesskey-rustls/Cargo.toml
    • New crate definition for uselesskey-rustls adapter.
  • crates/uselesskey-rustls/README.md
    • New README for uselesskey-rustls adapter.
  • crates/uselesskey-rustls/src/config.rs
    • New implementation for rustls config builders, providing RustlsServerConfigExt, RustlsClientConfigExt, and RustlsMtlsExt traits.
  • crates/uselesskey-rustls/src/lib.rs
    • New library definition for uselesskey-rustls, providing rustls-pki-types integration.
  • crates/uselesskey-x509/Cargo.toml
    • New crate definition for uselesskey-x509.
  • crates/uselesskey-x509/src/cert.rs
    • New implementation for X.509 certificate generation and output.
  • crates/uselesskey-x509/src/chain.rs
    • New implementation for X.509 certificate chain generation and output.
  • crates/uselesskey-x509/src/chain_negative.rs
    • New implementation for X.509 certificate chain negative fixtures.
  • crates/uselesskey-x509/src/chain_spec.rs
    • New specification for X.509 certificate chain generation (ChainSpec).
  • crates/uselesskey-x509/src/lib.rs
    • New library definition for uselesskey-x509, providing X.509 certificate fixtures.
  • crates/uselesskey-x509/src/negative.rs
    • New implementation for X.509 negative fixture types.
  • crates/uselesskey-x509/src/spec.rs
    • New specification for X.509 certificate generation (X509Spec).
  • crates/uselesskey-x509/src/util.rs
    • New utility functions for X.509 certificate generation.
  • crates/uselesskey/Cargo.toml
    • Updated crate metadata including description, categories, keywords, readme, documentation, and authors.
    • Added package.metadata.docs.rs configuration.
    • Added new key type crates (uselesskey-jwk, uselesskey-ecdsa, uselesskey-ed25519, uselesskey-hmac, uselesskey-x509) as optional dependencies.
    • Updated feature flags to gate new key types and JWK support, and added all-keys and full convenience features.
    • Added tempfile to dev-dependencies.
  • crates/uselesskey/examples/jwks.rs
    • New example demonstrating JWKS generation.
  • crates/uselesskey/examples/jwt_rs256_jwks.rs
    • New example demonstrating JWT RS256 JWKS generation.
  • crates/uselesskey/examples/tempfile_paths.rs
    • New example demonstrating writing key fixtures to temporary files.
  • crates/uselesskey/examples/tempfiles.rs
    • New example demonstrating tempfile usage for X.509 certificates.
  • crates/uselesskey/src/lib.rs
    • Updated documentation and examples to reflect new key types, deterministic mode, negative fixtures, temporary files, and JWK support.
    • Added new pub use statements for new key types and features.
    • Updated prelude module to conditionally re-export items based on enabled features.
  • crates/uselesskey/tests/facade.rs
    • New tests for facade re-exports, verifying that prelude and feature-gated modules are correctly exposed.
  • deny.toml
    • Added 'OpenSSL' to the list of allowed licenses.
  • docs/architecture.md
    • Updated architecture documentation to include new crates (uselesskey-jwk, uselesskey-ecdsa, uselesskey-ed25519, uselesskey-hmac, uselesskey-x509, uselesskey-jsonwebtoken) and expanded xtask commands.
  • docs/migration.md
    • New migration guide for replacing committed key fixtures with runtime generation, covering identification, replacement, handling leaked history, deterministic mode setup, and common migration patterns.
  • docs/release.md
    • New release guide detailing the publish order for uselesskey workspace crates, dry run, and publish commands.
  • docs/requirements-v0.3.md
    • New requirements document outlining the executable specification for uselesskey v0.3+.
  • docs/requirements.md
    • Added a reference to requirements-v0.3.md.
  • docs/roadmap.md
    • Updated roadmap to reflect implemented features (ECDSA, Ed25519, JWK, HMAC, X.509 leaf, jsonwebtoken adapter) and planned/future considerations.
  • fuzz/Cargo.toml
    • Updated edition to '2024'.
  • rust-toolchain.toml
    • Updated Rust toolchain channel to '1.92'.
  • rustfmt.toml
    • Removed imports_granularity, group_imports, format_code_in_doc_comments, and wrap_comments settings.
  • xtask/Cargo.toml
    • Added serde and serde_json to dependencies.
    • Added tempfile to dev-dependencies.
  • xtask/src/main.rs
    • Added new Cmd variants for FeatureMatrix, NoBlob, PublishCheck, Pr, DepGuard, Coverage, PublishPreflight.
    • Updated main function to handle new commands and orchestrate CI/PR workflows.
    • Introduced FEATURE_MATRIX constant and functions for ci, feature_matrix_cmd, publish_check, is_llvm_cov_installed, coverage, run_coverage, publish_preflight, run_publish_preflight, check_crate_metadata, fuzz_pr, list_fuzz_targets, no_blob_gate, find_secret_blobs, walk_for_blobs, is_ignored_dir, should_scan_path, is_secret_extension, contains_pem_markers, count_bdd_scenarios, and dep_guard.
  • xtask/src/plan.rs
    • New file, defining the Plan struct and build_plan function for PR-scoped testing based on changed files.
  • xtask/src/receipt.rs
    • New file, defining the Receipt struct and Runner struct for generating structured test receipts.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/ci.yml
Activity
  • The pull request consists of 24 commits.
  • All 200+ tests across 16 crates passed, including 91 BDD scenarios with 398 steps.
  • Clippy checks passed with 0 warnings.
  • A known slow test, test_chain_der_rustls in uselesskey-rustls, was identified due to random-mode RSA-2048 key generation.
  • Fuzz and mutation testing were not executed as part of this PR's verification.
  • The uselesskey-aws-lc-rs adapter might introduce platform-specific build dependencies (cmake/nasm).
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@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: 7af7851a01

ℹ️ 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".

Comment thread xtask/src/main.rs
Comment on lines +866 to +885
let mut versions: Vec<String> = Vec::new();
for line in stdout.lines() {
let line = line.trim();
if let Some(rest) = line.strip_prefix("rand_core v") {
let version = rest.split_whitespace().next().unwrap_or(rest);
if !versions.contains(&version.to_string()) {
versions.push(version.to_string());
}
}
}

if versions.is_empty() {
eprintln!("dep-guard: rand_core has no duplicates (ok)");
return Ok(());
}

// Multiple versions found in duplicates output
bail!(
"dep-guard: multiple versions of rand_core resolved: {}. \
Only rand_core 0.6.x is allowed. \

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 Enforce rand_core major pin, not just duplicates

The dep-guard only fails when cargo tree --duplicates shows multiple rand_core versions, but it never checks that the single resolved version is still 0.6.x. If the workspace or a transitive dependency upgrades to rand_core 0.7.x (with no duplicates), this guard will pass even though the comment and error message state that only 0.6.x is allowed. That means deterministic derivation could silently change without the guard catching it; consider parsing the version and failing when the sole version is not 0.6.x.

Useful? React with 👍 / 👎.

@gemini-code-assist gemini-code-assist 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.

Code Review

This is a massive and impressive feature addition. The new key types, adapter crates, and extensive tooling demonstrate a huge amount of work and thoughtful design. The modular architecture with extension traits and separate adapter crates is excellent. The documentation, examples, and comprehensive test suites (including BDD, property-based, and negative tests) are top-notch. The xtask tooling is particularly impressive, with PR-scoping and pre-publish checks that will greatly improve the development workflow. I've found a couple of issues to address, but overall this is a fantastic contribution that significantly expands the library's capabilities.

Comment thread Cargo.toml

# RSA
rsa = { version = "0.9", features = ["pem", "pkcs8", "pkcs1"] }
rsa = { version = "0.9.10", features = ["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.

critical

The rsa dependency in the workspace is configured with features = ["pem"], which removes the pkcs8 feature that was previously enabled. The uselesskey-rsa crate relies on methods like to_pkcs8_der(), which require the pkcs8 feature from the rsa crate. Without this feature, the build will likely fail. Please restore the pkcs8 feature.

Suggested change
rsa = { version = "0.9.10", features = ["pem"] }
rsa = { version = "0.9.10", features = ["pem", "pkcs8"] }

Comment thread AGENTS.md
@@ -0,0 +1,128 @@
# CLAUDE.md

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The filename is AGENTS.md, but the content starts with # CLAUDE.md. This is confusing and suggests a potential copy-paste or rename error. Please either rename the file to CLAUDE.md or update the content to match the filename for consistency.

x509-parser = "0.18.1"
serde_json.workspace = true
uselesskey = { path = "../uselesskey", features = ["jwk", "ed25519", "ecdsa", "x509", "hmac"] }
uselesskey-core = { path = "../uselesskey-core" } # for TempArtifact

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The BDD crate adds a direct dependency on uselesskey-core to access TempArtifact. This type is not re-exported by the main uselesskey facade, making it less discoverable for users who might also need it.

To improve ergonomics, consider re-exporting TempArtifact from the uselesskey facade crate, perhaps under a sink module. This would allow users to depend only on uselesskey for all public types.

For example, in crates/uselesskey/src/lib.rs:

pub mod sink {
    pub use uselesskey_core::sink::*;
}

Then you could remove the direct dependency on uselesskey-core here.

Copilot AI 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.

Pull request overview

This PR substantially expands the uselesskey workspace into a v0.3-complete “test-fixture layer” by adding multiple new key/cert fixture crates, typed JWK/JWKS support, several integration adapter crates, and CI/xtask tooling to operationalize feature-matrix, receipts, and PR-scoped verification.

Changes:

  • Added new fixture crates for ECDSA, Ed25519, HMAC, X.509 (including chains/negative variants), plus a typed uselesskey-jwk crate.
  • Added adapter crates to bridge fixtures into downstream ecosystems (jsonwebtoken, ring, rustls, rustcrypto, aws-lc-rs).
  • Added/expanded xtask + CI automation (PR-scoped plans, receipts, coverage hooks) and updated docs/guides accordingly.

Reviewed changes

Copilot reviewed 103 out of 105 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
xtask/src/receipt.rs Adds JSON “receipt” recording for xtask steps/feature-matrix/BDD counts.
xtask/Cargo.toml Adds serde/serde_json for receipt serialization; tempfile for tests.
rustfmt.toml Removes several formatting options from rustfmt config.
rust-toolchain.toml Pins toolchain channel to 1.92.
fuzz/Cargo.toml Updates fuzz crate edition to 2024.
docs/roadmap.md Rewrites roadmap into implemented/planned structure for v0.3.
docs/requirements.md Expands requirements framing and links to v0.3 spec doc.
docs/requirements-v0.3.md Adds executable v0.3 requirements/acceptance gates doc.
docs/release.md Adds release/publish order documentation.
docs/migration.md Adds migration guide from committed fixtures to runtime generation.
docs/architecture.md Updates architecture overview for new crates/tooling.
deny.toml Adds OpenSSL to allowed license list.
crates/uselesskey/tests/facade.rs Adds facade re-export tests across features.
crates/uselesskey/src/lib.rs Expands facade docs, feature-gated re-exports, and prelude.
crates/uselesskey/examples/tempfiles.rs Adds X.509 tempfile example.
crates/uselesskey/examples/tempfile_paths.rs Adds more extensive tempfile-path usage example.
crates/uselesskey/examples/jwt_rs256_jwks.rs Adds RSA JWK/JWKS example for JWT flows.
crates/uselesskey/examples/jwks.rs Adds example building a JWKS from multiple key types.
crates/uselesskey/Cargo.toml Makes key-type crates optional, defines feature bundles, sets default rsa.
crates/uselesskey-x509/src/util.rs Adds deterministic time/serial helpers for X.509 derivation.
crates/uselesskey-x509/src/spec.rs Adds X509Spec/KeyUsage definitions and stable bytes encoding.
crates/uselesskey-x509/src/negative.rs Adds X.509 negative fixture spec mutations + PEM/DER corruption wrappers.
crates/uselesskey-x509/src/lib.rs Introduces uselesskey-x509 crate entry and exports.
crates/uselesskey-x509/src/chain_spec.rs Adds chain spec with stable encoding and SAN ordering stability.
crates/uselesskey-x509/Cargo.toml Adds x509 fixture crate deps (rcgen/time/x509-parser/etc).
crates/uselesskey-rustls/README.md Documents rustls adapter features and example usage.
crates/uselesskey-rustls/Cargo.toml Adds rustls adapter crate with feature-gated rustls dependency.
crates/uselesskey-rustcrypto/README.md Documents RustCrypto adapter capabilities and example usage.
crates/uselesskey-rustcrypto/Cargo.toml Adds RustCrypto adapter crate and feature matrix.
crates/uselesskey-rsa/src/spec.rs Adds richer docs/tests for RsaSpec and stable_bytes behavior.
crates/uselesskey-rsa/src/lib.rs Expands crate docs and exports ordering.
crates/uselesskey-rsa/src/keypair.rs Switches to typed JWK/JWKS types and adds JSON helper accessors.
crates/uselesskey-rsa/Cargo.toml Makes crates publishable metadata; wires typed uselesskey-jwk feature.
crates/uselesskey-ring/src/lib.rs Adds ring adapter extension traits + integration tests.
crates/uselesskey-ring/README.md Documents ring adapter usage.
crates/uselesskey-ring/Cargo.toml Adds ring adapter crate and feature wiring.
crates/uselesskey-jwk/README.md Documents typed JWK/JWKS helper crate purpose.
crates/uselesskey-jwk/Cargo.toml Adds typed JWK/JWKS helper crate deps.
crates/uselesskey-jsonwebtoken/README.md Documents jsonwebtoken adapter usage and feature set.
crates/uselesskey-jsonwebtoken/Cargo.toml Adds jsonwebtoken adapter crate + dev-dep provider features for tests.
crates/uselesskey-hmac/src/spec.rs Adds HMAC spec enum with stable bytes encoding + tests.
crates/uselesskey-hmac/src/secret.rs Implements HMAC secret fixture generation + optional JWK/JWKS output.
crates/uselesskey-hmac/src/lib.rs Adds uselesskey-hmac crate entry and exports.
crates/uselesskey-hmac/README.md Documents HMAC fixture crate.
crates/uselesskey-hmac/Cargo.toml Adds HMAC crate metadata and optional JWK dependencies.
crates/uselesskey-ed25519/tests/keypair.rs Adds integration tests for Ed25519 parseability, determinism, negatives, tempfiles.
crates/uselesskey-ed25519/tests/jwk_private.rs Adds Ed25519 JWK/JWKS tests behind jwk feature.
crates/uselesskey-ed25519/src/spec.rs Adds Ed25519Spec with stable bytes encoding + tests.
crates/uselesskey-ed25519/src/lib.rs Adds crate docs and exports.
crates/uselesskey-ed25519/Cargo.toml Adds Ed25519 crate metadata and optional JWK deps.
crates/uselesskey-ecdsa/tests/keypair.rs Adds integration tests for ECDSA parseability, determinism, negatives, tempfiles.
crates/uselesskey-ecdsa/tests/jwk_private.rs Adds ECDSA JWK/JWKS tests behind jwk feature.
crates/uselesskey-ecdsa/src/spec.rs Adds EcdsaSpec with stable encoding + tests.
crates/uselesskey-ecdsa/src/lib.rs Adds crate docs and exports.
crates/uselesskey-ecdsa/Cargo.toml Adds ECDSA crate metadata and optional JWK deps.
crates/uselesskey-core/tests/negative_prop.rs Adds proptest coverage for PEM/DER negative helpers.
crates/uselesskey-core/tests/factory_env.rs Adds tests for deterministic_from_env behavior and error cases.
crates/uselesskey-core/tests/core_prop.proptest-regressions Adds proptest regression seeds file for deterministic reruns.
crates/uselesskey-core/src/sink/tempfile.rs Expands TempArtifact docs/debug + adds unit tests (including cleanup).
crates/uselesskey-core/src/negative/pem.rs Expands PEM corruption docs and adds unit tests; small indexing refactor.
crates/uselesskey-core/src/negative/mod.rs Expands module-level docs and re-export ordering.
crates/uselesskey-core/src/negative/der.rs Expands DER helper docs and adds unit tests.
crates/uselesskey-core/src/lib.rs Expands crate-level docs and architecture overview.
crates/uselesskey-core/src/id.rs Expands Seed docs; simplifies error mapping; adds tests.
crates/uselesskey-core/src/factory.rs Adds extensive docs/debug impl; adds tests for cache clear/type mismatch.
crates/uselesskey-core/src/error.rs Adds tests asserting error messages.
crates/uselesskey-core/src/derive.rs Adds tests for derivation version behavior.
crates/uselesskey-core/Cargo.toml Makes core crate publishable metadata.
crates/uselesskey-bdd/features/x509.feature Adds BDD coverage for X.509 fixtures, determinism, negatives, tempfiles.
crates/uselesskey-bdd/features/tempfile.feature Adds BDD scenarios for tempfile outputs.
crates/uselesskey-bdd/features/seed.feature Adds BDD scenarios for seed parsing forms.
crates/uselesskey-bdd/features/rsa.feature Expands RSA BDD scenarios (determinism, random mode, formats, mismatch determinism).
crates/uselesskey-bdd/features/negative.feature Adds BDD scenarios for PEM corruption and DER truncation.
crates/uselesskey-bdd/features/jwk.feature Adds BDD scenarios for RSA JWK/JWKS and kid stability.
crates/uselesskey-bdd/features/hmac.feature Adds BDD scenarios for HMAC fixtures and JWK/JWKS.
crates/uselesskey-bdd/features/ed25519.feature Adds BDD scenarios for Ed25519 fixtures and JWK/JWKS.
crates/uselesskey-bdd/features/ecdsa.feature Adds BDD scenarios for ECDSA fixtures and JWK coverage.
crates/uselesskey-bdd/Cargo.toml Expands BDD deps to cover new key types and parsing.
crates/uselesskey-aws-lc-rs/src/lib.rs Adds aws-lc-rs adapter extension traits + integration tests, with native gate.
crates/uselesskey-aws-lc-rs/README.md Documents aws-lc-rs adapter features and usage.
crates/uselesskey-aws-lc-rs/Cargo.toml Adds aws-lc-rs adapter crate with native optional dep.
clippy.toml Bumps MSRV to 1.92.
Cargo.toml Expands workspace members; bumps workspace edition/MSRV and dependency versions; tweaks opt-level.
CLAUDE.md Adds repo guidance doc for Claude Code usage and project conventions.
CHANGELOG.md Adds changelog with unreleased additions and 0.1.0 entry.
AGENTS.md Adds agent guidance doc (currently appears to duplicate CLAUDE.md).
.gitignore Adds mutants output ignore and nul entry.
.github/workflows/ci.yml Splits CI into PR-scoped xtask pr and main xtask ci, uploads receipts, installs tools.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread xtask/src/receipt.rs
Comment on lines +66 to +74
Ok(()) => {
let secs = start.elapsed().as_secs_f64();
eprintln!("==> {name} [ok, {secs:.1}s]");
self.receipt.steps.push(StepReceipt {
name: name.to_string(),
status: "ok".to_string(),
duration_ms: start.elapsed().as_millis() as u64,
details,
});

Copilot AI Feb 9, 2026

Copy link

Choose a reason for hiding this comment

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

step() calls start.elapsed() multiple times; the logged seconds and recorded duration_ms can diverge slightly, and it does extra work. Capture the elapsed duration once (e.g., let elapsed = start.elapsed()) and use it for both display and duration_ms.

Copilot uses AI. Check for mistakes.
Comment thread CLAUDE.md
Comment on lines +127 to +129
- `rustfmt.toml` - Formatting: Unix newlines, crate-level imports
- `clippy.toml` - MSRV 1.88
- `deny.toml` - Allowed licenses: MIT, Apache-2.0, BSD-3-Clause, ISC, CC0-1.0

Copilot AI Feb 9, 2026

Copy link

Choose a reason for hiding this comment

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

This “Configuration Files” section is stale relative to the repo changes in this PR: rustfmt no longer enforces crate-level import grouping, clippy MSRV is now 1.92, and deny.toml now allows the OpenSSL license. Please update these bullets so they reflect the current settings.

Suggested change
- `rustfmt.toml` - Formatting: Unix newlines, crate-level imports
- `clippy.toml` - MSRV 1.88
- `deny.toml` - Allowed licenses: MIT, Apache-2.0, BSD-3-Clause, ISC, CC0-1.0
- `rustfmt.toml` - Formatting: Unix newlines
- `clippy.toml` - MSRV 1.92
- `deny.toml` - Allowed licenses: MIT, Apache-2.0, BSD-3-Clause, ISC, CC0-1.0, OpenSSL

Copilot uses AI. Check for mistakes.
Comment thread AGENTS.md
Comment on lines +1 to +3
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Copilot AI Feb 9, 2026

Copy link

Choose a reason for hiding this comment

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

File header appears incorrect: AGENTS.md starts with # CLAUDE.md, which is confusing and looks like a copy/paste error. Consider renaming the heading (and/or content) so it accurately describes AGENTS.md.

Suggested change
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
# AGENTS.md
This file provides guidance to AI coding agents (for example, Claude Code at claude.ai/code) when working with code in this repository.

Copilot uses AI. Check for mistakes.
Comment thread AGENTS.md
Comment on lines +125 to +127
- `rustfmt.toml` - Formatting: Unix newlines, crate-level imports
- `clippy.toml` - MSRV 1.88
- `deny.toml` - Allowed licenses: MIT, Apache-2.0, BSD-3-Clause, ISC, CC0-1.0

Copilot AI Feb 9, 2026

Copy link

Choose a reason for hiding this comment

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

This “Configuration Files” section is inconsistent with the actual repo configuration in this PR (rustfmt import settings removed, clippy MSRV bumped to 1.92, deny.toml allowlist expanded). Please update these bullets to match the current config to avoid misleading contributors/agents.

Suggested change
- `rustfmt.toml` - Formatting: Unix newlines, crate-level imports
- `clippy.toml` - MSRV 1.88
- `deny.toml` - Allowed licenses: MIT, Apache-2.0, BSD-3-Clause, ISC, CC0-1.0
- `rustfmt.toml` - Formatting configuration (e.g., Unix newlines)
- `clippy.toml` - MSRV 1.92
- `deny.toml` - Dependency policy (license/ban) configuration, including common permissive licenses (MIT, Apache-2.0, BSD-3-Clause, ISC, CC0-1.0, and others)

Copilot uses AI. Check for mistakes.
Comment thread docs/roadmap.md
Comment on lines +40 to +52
## Planned

### X.509 — cert chains and negative fixtures

- [ ] **Cert chain fixtures**
- Root CA → Intermediate → Leaf
- Deterministic serial numbers

- [ ] **X.509 negative fixtures**
- Expired certificates
- Wrong SAN (hostname mismatch)
- Unknown CA (untrusted root)
- Revoked cert (with CRL/OCSP stub)

Copilot AI Feb 9, 2026

Copy link

Choose a reason for hiding this comment

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

Roadmap lists X.509 chains and negative fixtures as planned/unimplemented, but this PR adds X509Chain/ChainSpec and negative variants (expired/not-yet-valid, etc.). Please move these items into “Implemented” (or at least mark them as complete) to keep the roadmap accurate.

Copilot uses AI. Check for mistakes.
Comment thread docs/architecture.md
Comment on lines +35 to +37
- `crates/uselesskey-x509`
- X.509 certificate fixtures (self-signed for now)
- deterministic validity/serial in deterministic mode

Copilot AI Feb 9, 2026

Copy link

Choose a reason for hiding this comment

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

This section says X.509 is “self-signed for now”, but the crate docs and modules in this PR include chain generation (ChainSpec, X509Chain). Please update the workspace layout description to reflect that chain fixtures are implemented.

Copilot uses AI. Check for mistakes.
Comment thread docs/architecture.md
Comment on lines +99 to +103
├── RsaFactoryExt (uselesskey-rsa) → fx.rsa(label, spec)
├── EcdsaFactoryExt (uselesskey-ecdsa) → fx.ecdsa(label, spec)
├── Ed25519FactoryExt (uselesskey-ed25519) → fx.ed25519(label)
├── HmacFactoryExt (uselesskey-hmac) → fx.hmac(label, spec)
└── X509FactoryExt (uselesskey-x509) → fx.x509(label, spec)

Copilot AI Feb 9, 2026

Copy link

Choose a reason for hiding this comment

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

The extension-pattern diagram/documentation is out of date: Ed25519FactoryExt is shown as fx.ed25519(label) but the actual API is fx.ed25519(label, spec). Please update the diagram to match the current method signature.

Copilot uses AI. Check for mistakes.
Comment thread CLAUDE.md
Key type support is added via extension traits on `Factory`:
- `RsaFactoryExt` → `fx.rsa(label, spec)`
- `EcdsaFactoryExt` → `fx.ecdsa(label, spec)`
- `Ed25519FactoryExt` → `fx.ed25519(label)`

Copilot AI Feb 9, 2026

Copy link

Choose a reason for hiding this comment

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

The documented extension signature for Ed25519 is out of date: Ed25519FactoryExt currently takes (label, spec), not just label. Please update this section to match the actual API so users don’t copy/paste a non-compiling example.

Suggested change
- `Ed25519FactoryExt``fx.ed25519(label)`
- `Ed25519FactoryExt``fx.ed25519(label, spec)`

Copilot uses AI. Check for mistakes.
Comment thread docs/architecture.md
Comment on lines +115 to +126
## Adapter crates

Adapter crates provide native integration with downstream libraries. They are separate crates (not features) to avoid coupling uselesskey's versioning to downstream crate versions.

- `uselesskey-jsonwebtoken` — returns `jsonwebtoken::EncodingKey` / `DecodingKey` directly

### Planned adapters

```
uselesskey-rustls → returns PrivateKeyDer/CertificateDer directly
uselesskey-ring → returns ring's native key types
```

Copilot AI Feb 9, 2026

Copy link

Choose a reason for hiding this comment

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

This “Planned adapters” section lists uselesskey-rustls and uselesskey-ring as future work, but both crates are added in this PR. Please update this section (e.g., move them into the implemented adapters list, and reserve “Planned” for adapters not yet in the workspace).

Copilot uses AI. Check for mistakes.
Comment thread .github/workflows/ci.yml
Comment on lines +27 to +32
- name: Install cargo-fuzz
run: cargo install cargo-fuzz

- name: Install cargo-mutants
run: cargo install cargo-mutants

Copilot AI Feb 9, 2026

Copy link

Choose a reason for hiding this comment

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

CI installs cargo subcommands via cargo install without --locked, which can introduce non-reproducible builds if a new release of cargo-fuzz/cargo-mutants lands. Use cargo install --locked ... (and consider pinning versions and/or caching/binstalling these tools to reduce CI time/flakiness).

Copilot uses AI. Check for mistakes.
- Moved downcast logic to a separate function `downcast_or_panic` for better readability and reuse.
- Added tests for `downcast_or_panic` to ensure it correctly handles type mismatches and returns expected values.
- Created a new test module `factory_tests` to encapsulate tests related to the `Factory` struct.
- Improved the `random_seed` function to ensure it generates a seed of the expected length.
- Updated existing tests to enhance clarity and maintainability, including assertions for cache behavior and debug output.
- Added additional tests for X.509 certificate specifications and negative scenarios to ensure comprehensive coverage.

@coderabbitai coderabbitai 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.

Actionable comments posted: 80

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
crates/uselesskey-core/src/negative/pem.rs (1)

76-76: ⚠️ Potential issue | 🟡 Minor

Truncate { bytes } counts chars, not bytes.

pem.chars().take(bytes).collect() counts Unicode scalar values, not bytes. The field is named bytes and the doc says "Truncate the PEM string to bytes characters" — mixing terminology. For ASCII PEM this is equivalent, but if anyone passes non-ASCII, result.len() (which measures bytes) won't equal the bytes field.

Either rename the field to chars (and update the doc), or use byte-level truncation:

Option A: rename to chars
-    Truncate { bytes: usize },
+    Truncate { chars: usize },
-        CorruptPem::Truncate { bytes } => pem.chars().take(bytes).collect(),
+        CorruptPem::Truncate { chars } => pem.chars().take(chars).collect(),
Option B: truncate by actual bytes
-        CorruptPem::Truncate { bytes } => pem.chars().take(bytes).collect(),
+        CorruptPem::Truncate { bytes } => {
+            let end = pem.floor_char_boundary(bytes.min(pem.len()));
+            pem[..end].to_string()
+        }
crates/uselesskey-core/src/id.rs (1)

97-109: ⚠️ Potential issue | 🟡 Minor

64-char non-hex strings produce an error instead of falling back to hashing.

If a user passes a 64-character string that isn't valid hex (e.g., a long label), parse_hex_32 returns Err and the hash fallback is never reached. The doc says the function is "intentionally forgiving," but this case silently breaks that contract.

Consider attempting hex parse and falling back on failure:

Proposed fix
         if hex.len() == 64 {
-            return parse_hex_32(hex).map(Self);
+            if let Ok(bytes) = parse_hex_32(hex) {
+                return Ok(Self(bytes));
+            }
         }
crates/uselesskey-rsa/src/spec.rs (2)

48-68: ⚠️ Potential issue | 🟡 Minor

Misleading # Panics section on new() — the function itself doesn't validate.

The doc comment says "The factory will panic if bits < 1024", but new() performs no validation and never panics. This violates the Rust convention that # Panics documents this function's panic conditions. A reader calling RsaSpec::new(512) will expect a panic from new(), not from a downstream factory call.

Either move the note to a non-Panics section (e.g., a # Notes section clarifying "The factory rejects bits < 1024"), or add validation here.

Option A: Reword the doc to avoid the misleading `# Panics`
     /// Create a spec with custom bit size and default exponent (65537).
     ///
-    /// # Panics
+    /// # Notes
     ///
-    /// The factory will panic if `bits < 1024`.
+    /// The factory will panic at key-generation time if `bits < 1024`.
Option B: Validate eagerly in the constructor
     pub fn new(bits: usize) -> Self {
+        assert!(bits >= 1024, "RSA bits too small: {bits}");
         Self {
             bits,
             exponent: 65537,
         }
     }

73-79: ⚠️ Potential issue | 🟡 Minor

Silent clamping in stable_bytes can produce collisions for extreme bits values.

u32::try_from(self.bits).unwrap_or(u32::MAX) means any bits > u32::MAX (on 64-bit) maps to the same encoding as u32::MAX. Two distinct RsaSpec values would then share the same cache key, violating the method's stated purpose ("Stable encoding for cache keys / deterministic derivation").

In practice RSA bit sizes never exceed u32::MAX, and the test at Line 108 even validates this clamping. If that's intentional, a brief doc comment noting this trade-off would be helpful. If not, consider panicking instead of clamping.

crates/uselesskey-rsa/src/keypair.rs (1)

49-58: 🧹 Nitpick | 🔵 Trivial

_private field is always retained in memory even when unused.

When the jwk feature is disabled, _private: RsaPrivateKey is stored but never accessed (the serialized DER/PEM forms are used instead). For RSA-2048+ keys, this is a non-trivial amount of memory per cached fixture. If memory efficiency matters, you could avoid storing the full key object when jwk is off.

Possible approach
 struct Inner {
-    /// Kept for potential signing methods; not currently used.
-    _private: RsaPrivateKey,
     #[cfg(feature = "jwk")]
+    private: RsaPrivateKey,
+    #[cfg(feature = "jwk")]
     public: RsaPublicKey,
     pkcs8_der: Arc<[u8]>,

This would require adjusting load_inner accordingly.

crates/uselesskey-core/src/factory.rs (1)

239-255: 🧹 Nitpick | 🔵 Trivial

Minor: unnecessary downcast on the cache-miss path.

On the Vacant branch, arc (typed Arc<T>) is created at line 247 and then erased. The erased arc_any is returned and then downcast back to Arc<T> at line 254. You could return arc directly on the miss path to avoid the round-trip.

♻️ Proposed optimization
         let arc_any = match self.inner.cache.entry(id.clone()) {
             Entry::Occupied(o) => o.get().clone(),
             Entry::Vacant(v) => {
                 let seed = self.seed_for(&id);
                 let mut rng = ChaCha20Rng::from_seed(seed.0);
                 let value = init(&mut rng);
                 let arc: Arc<T> = Arc::new(value);
                 let arc_any: Arc<dyn Any + Send + Sync> = arc.clone();
-                v.insert(arc_any.clone());
-                arc_any
+                v.insert(arc_any);
+                return arc;
             }
         };
 
         downcast_or_panic::<T>(arc_any, &id)
🤖 Fix all issues with AI agents
In @.github/workflows/ci.yml:
- Around line 27-31: Update the GitHub Actions steps named "Install cargo-fuzz"
and "Install cargo-mutants" to avoid compiling from source on every run: add the
--locked flag to cargo install for reproducible installs, and prefer installing
prebuilt binaries (e.g., use binstall or download a release tarball) or rely on
the existing Rust cache (~/.cargo/bin) to restore those tools rather than
running cargo install unconditionally; ensure the workflow checks the cache
before installing and only falls back to install when the binary is missing.
- Around line 10-12: The CI workflow jobs lack timeout-minutes which can allow
hung xtask runs to consume minutes indefinitely; add a reasonable
timeout-minutes (e.g., 30 or 60) to each job definition—specifically update the
job keyed as pr and the other CI job referenced around the same block (the
second job at lines ~45-47) to include timeout-minutes: <minutes> so the GitHub
Actions runner will cancel long-running or stuck builds.
- Around line 37-43: The "Upload receipt" artifact step currently runs only on
success and is skipped if the preceding "xtask pr" step fails; update the Upload
receipt steps (the step with name "Upload receipt" that uses
actions/upload-artifact@v4 and uploads name: receipt-pr path:
target/xtask/receipt.json) to include if: always() so the receipt artifact is
uploaded regardless of job success; apply the same change to the equivalent
"Upload receipt" in the main job.

In `@AGENTS.md`:
- Line 1: The markdown heading inside the file is incorrect: change the
top-level heading text from "# CLAUDE.md" to "# AGENTS.md" (update the heading
string at the top of the file) so the file heading matches the filename and
project conventions.
- Around line 77-87: AGENTS.md's workspace list and the "cargo xtask ci"
description are out of sync with CLAUDE.md; update the AGENTS.md entries to
include the missing adapter crates (uselesskey-rustls, uselesskey-ring,
uselesskey-rustcrypto, uselesskey-aws-lc-rs) and align the "cargo xtask ci"
description to match CLAUDE.md (include "matrix + guard + bdd + no-blob +
mutants + fuzz"), or choose one canonical source and regenerate the other from
it so the lists (the crate lines like `crates/uselesskey-rustls`, etc.) and the
`cargo xtask ci` description remain consistent across both documents.
- Line 126: Update the MSRV mention in AGENTS.md so the line currently reading
"`clippy.toml` - MSRV 1.88" matches the actual toolchain version; change it to
"`clippy.toml` - MSRV 1.92" to be consistent with rust-toolchain.toml and
Cargo.toml (search for the exact string "`clippy.toml` - MSRV 1.88" and replace
it with the updated version).

In `@Cargo.toml`:
- Around line 71-76: The change raised opt-level = 2 for both [profile.dev] and
[profile.test], which increases incremental compile times workspace-wide;
instead revert those global opt-levels to a lower level and add a per-crate
profile override for only the RSA/crypto crate(s) that need higher optimization
(use the Cargo profile override header syntax e.g.
[profile.dev.package."your_rsa_crate_name"] and
[profile.test.package."your_rsa_crate_name"] with opt-level = 2), so only the
crate(s) requiring faster release-like codegen pay the compile cost while other
crates keep fast incremental builds.

In `@CLAUDE.md`:
- Line 87: Update the X.509 description so both files match: pick either "X.509
self-signed certificate fixtures" or "X.509 certificate fixtures" and apply it
consistently in CLAUDE.md and AGENTS.md; specifically modify the entry for
crates/uselesskey-x509 and its description referencing the X509FactoryExt trait
so the two files are identical.
- Around line 69-74: Add a blank line immediately before the fenced code block
that begins with "```bash" (the code block containing "cargo test -p
uselesskey-core test_name" and "cargo test -p uselesskey-rsa test_name") in
CLAUDE.md so the markdown conforms to MD031; ensure there is an empty line
separating the preceding paragraph and the code fence.

In `@crates/uselesskey-aws-lc-rs/Cargo.toml`:
- Around line 22-28: The "all" feature currently enables rsa, ecdsa, and ed25519
but not native, so consumers using default-features = false with features =
["all"] won't enable aws-lc-rs and the conversion traits (AwsLcRsRsaKeyPairExt,
AwsLcRsEcdsaKeyPairExt, AwsLcRsEd25519KeyPairExt) remain unavailable; update the
Cargo.toml [features] section by adding "native" to the "all" feature so it
becomes all = ["native", "rsa", "ecdsa", "ed25519"] to ensure aws-lc-rs and the
gated conversion traits are enabled when users opt into "all".

In `@crates/uselesskey-aws-lc-rs/src/lib.rs`:
- Around line 30-34: The doc example uses the misleading variable name
`ring_kp`; update the example to use a name that reflects this crate (e.g.,
`aws_kp`) wherever `ring_kp` appears in the rsa_key_pair_aws_lc_rs example
block, and adjust any subsequent references like `ring_kp.public_modulus_len()`
to `aws_kp.public_modulus_len()` so the example matches the aws-lc-rs adapter
naming.

In `@crates/uselesskey-bdd/Cargo.toml`:
- Around line 14-16: The inline dev-dependencies p256, p384 and x509-parser in
Cargo.toml should be moved to the workspace dependency table; remove the version
strings here and change each to use workspace = true (preserving feature lists
for p256 and p384 like ["ecdsa","pkcs8","pem"]) and then add their versions once
under [workspace.dependencies] in the workspace Cargo.toml so the workspace
centrally manages their versions and prevents drift.

In `@crates/uselesskey-bdd/features/chain.feature`:
- Around line 61-68: Add missing step definitions referenced by the "certificate
chain supports multiple SANs" scenario: implement functions (e.g.,
step_generate_certificate_chain_for_domain, step_add_san_to_chain,
step_leaf_certificate_should_contain_san) in crates/uselesskey-bdd/tests/bdd.rs
that create a deterministic certificate chain for a domain, append SAN entries
to the chain, and assert that the leaf certificate's SAN extension contains the
given names; reuse and adapt the existing single-certificate X.509 helpers in
bdd.rs (parsing, SAN handling, deterministic factory) to build the chain,
mutate/add SANs on the leaf, and perform the assertions so the new scenario
steps are wired to the feature file.

In `@crates/uselesskey-bdd/features/cross_key.feature`:
- Around line 55-61: The RSA 3072 key generated for label "rsa-3072" is never
asserted; update the scenario to either remove the "When I generate an RSA key
for label \"rsa-3072\" with spec 3072" step or add an assertion that checks its
modulus length (the JWK n value) falls between the lengths of "rsa-2048" and
"rsa-4096". Concretely, augment the existing Then step (or add a new Then step)
to assert that length(n for "rsa-2048") < length(n for "rsa-3072") < length(n
for "rsa-4096") so the "rsa-3072" generation is actually validated.

In `@crates/uselesskey-bdd/features/ecdsa.feature`:
- Around line 143-149: The ES384 scenario "ES384 public JWK has correct format"
is missing parity checks present in the ES256 scenario; update that scenario by
adding the assertions 'And the ECDSA public JWK should have a kid' and 'And the
ECDSA public JWK should have x and y parameters' so the scenario includes checks
for kid and the x/y coordinates (matching the ES256 checks).

In `@crates/uselesskey-bdd/features/hmac.feature`:
- Around line 1-4: The feature declares HS256/HS384/HS512 but only exercises
HS256; add two new scenarios to crates/uselesskey-bdd/features/hmac.feature that
mirror the existing HS256 deterministic/stability scenario(s) but set the
algorithm to HS384 and HS512 respectively (e.g., "Scenario: deterministic HS384
HMAC fixture" and "Scenario: deterministic HS512 HMAC fixture"), using the same
Given/When/Then steps and assertions as the HS256 scenarios so they validate
stable output length and values for the HMAC fixture generator referenced by the
Feature: HMAC fixtures.

In `@crates/uselesskey-bdd/features/jwks.feature`:
- Around line 93-101: Add a check for the ECDSA key's "alg" field in the JWKS
ECDSA scenario to match the RSA scenario parity: update the "ECDSA JWKS key
contains required fields" scenario (where you call public_jwk() via the
deterministic factory and generate an ES256 key for label "ecdsa-fields") to
include an assertion that the JWKS EC key contains field "alg" (expecting
"ES256" or at least presence), similar to the RSA scenario's alg assertion.

In `@crates/uselesskey-bdd/features/x509.feature`:
- Around line 141-145: The X.509 identity PEM tempfile scenario only checks the
filename suffix and misses verifying file contents; update the scenario for
"X.509 identity PEM writes to tempfile" to also read the tempfile and assert its
contents equal the certificate's identity_pem() (similar to the cert and key
scenarios). Locate the scenario that generates the X.509 with label "temp-chain"
and add a step like "reading the X.509 identity tempfile should match the
identity_pem()" so the test reads the tempfile path produced and compares the
file contents to identity_pem() for the generated identity.

In `@crates/uselesskey-bdd/tests/bdd.rs`:
- Around line 9-90: UselessWorld has repeated groups of fields for
RSA/Ed25519/ECDSA; extract that pattern into a new KeyArtifacts struct (named
KeyArtifacts) containing the repeated Option fields (pkcs8_pem_1/2,
spki_der_1/2, pkcs8_der_original, mismatch_1/2, corrupted_pem, truncated_der,
kid_1/2) and replace the per-key flat fields in UselessWorld with rsa_artifacts:
KeyArtifacts, ed25519_artifacts: KeyArtifacts, ecdsa_artifacts: KeyArtifacts;
update any code that referenced the old flat fields to use the new nested fields
and ensure the World derive macro still works with the nested struct (if it
fails, keep the flat layout but extract a private helper type or implement
Default/From conversions to reduce duplication).
- Around line 565-753: This file duplicates many Ed25519 step functions that
mirror RSA/ECDSA ones; split the long BDD file into per-key-type modules to
improve navigability: extract the Ed25519 When/Then functions (e.g.,
gen_ed25519, gen_ed25519_again, gen_ed25519_second, get_ed25519_mismatch,
corrupt_ed25519_bad_header, truncate_ed25519_der, capture_ed25519_kid and all
ed25519_* Then functions) into a new module (suggest steps/ed25519.rs), move
ECDSA and RSA blocks into steps/ecdsa.rs and steps/rsa.rs respectively, export
or import the shared UselessWorld type and any helper uses (ed25519_dalek
imports) from the new modules, add mod declarations and use the step functions
from the original tests/bdd.rs (or replace the large blocks with mod includes),
and ensure visibility (pub) and imports compile; this keeps behavior unchanged
while reducing file size and making each key-type block isolated.

In `@crates/uselesskey-core/src/derive.rs`:
- Around line 53-67: The test derive_seed_unknown_version_is_deterministic
triggers an eprintln from the unknown-version fallback in derive_seed, producing
noisy stderr; update the test or the fallback so tests don't print: either (A)
change the fallback in derive_seed to use a log macro (e.g., log::warn!) instead
of eprintln! so callers/tests can control/silence it, or (B) suppress/capture
stderr inside the test derive_seed_unknown_version_is_deterministic by
temporarily redirecting stderr (for example using the gag crate or a std-based
temporary stderr redirect) for the duration of the derive_seed calls so the
determinism assertion still runs without emitting to stderr.

In `@crates/uselesskey-core/src/sink/tempfile.rs`:
- Around line 156-159: The current read_to_string method uses
String::from_utf8_lossy which silently replaces invalid UTF-8; change it to
return an error on invalid UTF-8 by using String::from_utf8(bytes).map_err(...)
instead of from_utf8_lossy so callers get a clear error; update the
implementation in the read_to_string function to call self.read_to_bytes(), then
attempt strict conversion with String::from_utf8 and propagate a suitable Error
(or map to the existing Error type) to preserve behavior consistent with
std::fs::read_to_string.
- Around line 195-214: The test tempfile_deleted_on_drop uses a fragile
sleep-based polling loop to wait for deletion; instead, after creating
TempArtifact via TempArtifact::new_string inside the inner block and capturing
path = temp.path().to_path_buf(), remove the loop and immediately assert that
the path does not exist (call assert!(!path.exists(), ...)) since NamedTempFile
removal is synchronous; update the tempfile_deleted_on_drop test to drop the
TempArtifact by leaving the inner scope and perform a single immediate assertion
on the captured path variable.

In `@crates/uselesskey-core/tests/core_prop.rs`:
- Around line 13-25: The local variable `_id` created via `ArtifactId::new`
inside `derive_seed_for_test` is unused dead code; remove that construction so
the function only uses `Factory::deterministic(*master)` and
`fx.get_or_init(...)` to produce the seed, and then clean up the `ArtifactId`
import if it becomes unused elsewhere; ensure `derive_seed_for_test`,
`Factory::deterministic`, and `fx.get_or_init` remain unchanged except for
deleting the `_id` line.

In `@crates/uselesskey-core/tests/factory_env.rs`:
- Around line 23-27: Add a brief SAFETY comment documenting the invariant around
calling the unsafe std::env::set_var/remove_var in the test helper: in
EnvGuard::drop (the drop implementation shown) annotate the unsafe {
std::env::remove_var(&self.key) } with a // SAFETY: comment explaining that the
env var name is uniquely generated for the test and therefore removing it is
safe even if it does not exist; likewise locate the corresponding unsafe
std::env::set_var usage in the EnvGuard constructor (or test setup) and add a
matching // SAFETY: comment describing the scoped safety guarantee.
- Around line 5-11: The helper unique_env_var currently uses only
SystemTime::as_nanos() which can collide across threads; modify unique_env_var
to append a per-thread identifier or random component (e.g.,
std::thread::current().id() or a small RNG/UUID) to the format string so
generated names are unique even when invoked concurrently; update the format
call in unique_env_var to include the chosen thread_id or random token alongside
{suffix} and {nanos}.

In `@crates/uselesskey-core/tests/negative_prop.rs`:
- Around line 52-71: The expression target_bytes = pem.len() / fraction.max(1)
is redundant because fraction is drawn from 1usize..10 and therefore always ≥ 1;
update the test function corrupt_pem_truncate_produces_correct_length to remove
the .max(1) (compute target_bytes = pem.len() / fraction) and keep the existing
prop_assume! checks (target_bytes < pem.len() and target_bytes > 0) so behavior
and assertions remain unchanged.

In `@crates/uselesskey-ecdsa/Cargo.toml`:
- Around line 23-27: The blake3 dependency is not optional but sits under the
"Optional JWK/JWKS support" block; move the blake3 entry out of that comment
group into the main [dependencies] section (the same section that contains
rand_core) so it is a required dependency; update its entry accordingly (remove
it from the optional block and add the blake3 declaration under [dependencies])
and ensure there are no optional = true flags left for blake3.

In `@crates/uselesskey-ecdsa/src/keypair.rs`:
- Around line 26-40: The Inner struct holds a redundant spec: EcdsaSpec field
that duplicates the spec already stored on EcdsaKeyPair and is unused (marked
#[allow(dead_code)]); remove the spec: EcdsaSpec field from Inner and any
references to Inner::spec, keeping EcdsaKeyPair::spec as the single source of
truth to eliminate dead weight in the cache (update constructors or builders
that populated Inner to stop initializing spec and update any code that accessed
Inner::spec to read from EcdsaKeyPair::spec instead).
- Around line 150-202: Extract the duplicated uncompressed-point parsing into a
private helper (e.g., a function like split_uncompressed_point or
parse_uncompressed_coords) that takes &self.inner.public_key_bytes (or &[u8])
and returns the x and y slices after asserting bytes[0] == 0x04 and computing
coord_len = (bytes.len()-1)/2; then call that helper from public_jwk and
private_key_jwk to obtain x and y and encode them instead of repeating the
assertion and slicing logic.

In `@crates/uselesskey-ecdsa/src/spec.rs`:
- Around line 37-45: Add a brief doc comment to the EcdsaSpec::stable_bytes
function (or adjacent to the EcdsaSpec type) explaining that the 4-byte
encodings intentionally overlap with HmacSpec (e.g., Es256 == Hs256) but this is
safe because ArtifactId includes a separate domain field and uniqueness is only
required within the same domain; reference HmacSpec and ArtifactId::domain in
the comment so future contributors understand the domain separation guarantee.

In `@crates/uselesskey-ecdsa/tests/keypair.rs`:
- Around line 116-129: The test tempfiles_match_in_memory only exercises
EcdsaSpec::es256(); update it to run the same assertions for both
EcdsaSpec::es256() and EcdsaSpec::es384() by iterating over the specs (e.g., a
small loop or array of specs) and creating a key from Factory::random() for each
spec, then calling key.write_private_key_pkcs8_pem(),
key.write_public_key_spki_pem(), reading the tempfile contents and asserting
they equal key.private_key_pkcs8_pem() and key.public_key_spki_pem()
respectively; keep the existing helper calls (Factory::random(),
write_private_key_pkcs8_pem, write_public_key_spki_pem, private_key_pkcs8_pem,
public_key_spki_pem) but run them for both EcdsaSpec::es256() and
EcdsaSpec::es384().

In `@crates/uselesskey-ed25519/Cargo.toml`:
- Line 11: The crate's Cargo.toml sets readme = "../../README.md" which is
inconsistent with other adapter crates; either add a crate-level README.md and
update the readme key to "README.md", or change the readme value to "README.md"
to match the other crates; locate the readme entry in Cargo.toml (the readme
key) and replace the path or add a local README.md file accordingly so all
adapters use the same crate-level readme convention.
- Around line 20-24: The blake3 entry is a required dependency but is placed
under the "Optional JWK/JWKS support" comment which is misleading; open
Cargo.toml and move the blake3.workspace = true line out of that optional block
(place it above the optional JWK/JWKS comment, e.g., directly after the other
non-optional dependencies around where serde_json and base64 are declared) so it
is clearly in the required dependencies section, and do not mark it optional or
gate it behind the jwk feature (leave blake3 as a plain workspace dependency).

In `@crates/uselesskey-ed25519/src/spec.rs`:
- Around line 24-28: Ed25519Spec's stable_bytes uses ASCII magic ([b'E', b'd',
0x01, 0x00]) while EcdsaSpec and HmacSpec use numeric discriminants, so make the
encoding consistent: change Ed25519Spec::stable_bytes to return the same numeric
discriminant form as the others (e.g., [0, 0, 0, 1]) or refactor all specs to
use a shared enum/constant for discriminants; update the
Ed25519Spec::stable_bytes implementation (and optionally add a shared
SPEC_DISCRIMINANT constant used by EcdsaSpec, HmacSpec, RsaSpec) so all spec
types follow the same convention.

In `@crates/uselesskey-ed25519/tests/jwk_private.rs`:
- Around line 65-71: The test kid_is_deterministic currently only exercises a
cache hit because k1 and k2 come from the same Factory; change it to instantiate
two independent factories using the same Seed::from_env_value("ed25519-kid")
(e.g., let fx1 = Factory::deterministic(...); let fx2 =
Factory::deterministic(...)), call fx1.ed25519("issuer", Ed25519Spec::new()) and
fx2.ed25519("issuer", Ed25519Spec::new()), then assert_eq!(k1.kid(), k2.kid())
to verify cross-factory deterministic derivation of kid().

In `@crates/uselesskey-hmac/README.md`:
- Around line 1-6: The README for the uselesskey-hmac crate is sparse compared
to sibling crates; update it to match workspace conventions by adding a features
table (list features and cargo feature flags), a short usage example showing how
to import and call the crate’s API for generating HMAC keys/fixtures (mention
HS256/HS384/HS512 usage), and a license section that matches the repository
license; ensure the crate name uselesskey-hmac is referenced and that the
examples mirror the format used in uselesskey-ring so the README is consistent
for crates.io consumption.

In `@crates/uselesskey-hmac/src/spec.rs`:
- Around line 12-51: Public API methods on HmacSpec lack /// doc comments; add
concise one-line Rustdoc comments for hs256(), hs384(), hs512(), alg_name(),
byte_len(), and stable_bytes() describing what each returns/represents (e.g.,
constructor for each variant, algorithm name string, key byte length, and stable
4-byte encoding for derivation), mirroring the style and level of detail used in
RsaSpec so crates.io docs are consistent; ensure comments are placed immediately
above the functions (hs256, hs384, hs512, alg_name, byte_len, stable_bytes) and
mention any important stability/compatibility note for stable_bytes about
bumping the derivation version in uselesskey-core.

In `@crates/uselesskey-jsonwebtoken/README.md`:
- Around line 26-36: The README example uses undefined symbols `claims` and
`Claims`; add a brief placeholder explaining/defining them so the snippet is
self-contained: declare or mention a `Claims` struct (e.g., derive
Serialize/Deserialize) and create a `claims` instance before calling `encode`
and `decode` so readers can copy the example; update the example near the
`encode`/`decode` usage of `encoding_key()`/`decoding_key()` to reference that
`claims` variable and the `Claims` type.

In `@crates/uselesskey-jwk/src/lib.rs`:
- Around line 86-101: Add Debug implementations for the public JWK types so
users can inspect them in tests: add #[derive(Debug)] to the structs
RsaPublicJwk, EcPublicJwk, OkpPublicJwk, PublicJwk and the Jwks collection type;
for AnyJwk, either add #[derive(Debug)] if it’s an enum or provide a manual impl
that matches on the inner variant and delegates to its Debug (taking into
account the existing custom Debug for PrivateJwk) so dbg!()/println!("{:?}",
...) works for all public JWK wrappers.
- Around line 306-311: Display for PrivateJwk currently serializes and prints
full JWK JSON (exposing secret fields like d, k); change the fmt::Display impl
for PrivateJwk to emit a redacted representation that matches the existing Debug
behavior by omitting secret key fields (e.g., d, k, etc.) — either serialize a
redacted/public-only view of the struct or reuse the Debug formatting under
fmt::Display (avoid calling serde_json::to_string(self) directly). Ensure the
new impl uses the same redaction rules as Debug so format!("{}"),
log::info!("{}") and println!("{}") never print secret material.

In `@crates/uselesskey-rsa/src/keypair.rs`:
- Around line 160-181: public_jwk() (and private_key_jwk()) hardcode alg="RS256"
which is wrong for non-2048 RsaSpec keys; modify RsaSpec to expose the intended
JWK alg (e.g. add an enum or field describing the algorithm or add a method like
jwk_alg() that maps bits -> "RS256"/"RS384"/"RS512") and update public_jwk() and
private_key_jwk() to call that new accessor instead of the hardcoded "RS256";
reference RsaSpec, public_jwk, and private_key_jwk when making the change so the
JWK alg value is derived from the spec rather than fixed.

In `@crates/uselesskey-rsa/tests/rsa_prop.rs`:
- Around line 93-120: Rename the misleading test function
all_spec_configs_produce_parseable_keys to reflect it only exercises the RS256
spec (e.g., rs256_spec_produces_parseable_keys); update the test function
identifier and any related doc/comment text to reference RsaSpec::rs256() and
2048-bit/65537 exponent so the name accurately matches the behavior of the test.

In `@crates/uselesskey-rustcrypto/src/lib.rs`:
- Around line 56-67: rsa_public_key currently calls rsa_private_key(),
re-parsing the PKCS#8 DER on every call; change rsa_public_key to avoid
re-deserializing the private key by parsing the public-key SPKI/PKCS#8 DER
directly (e.g. use a public-key DER accessor on uselesskey_rsa::RsaKeyPair such
as public_key_spki_der() and call rsa::RsaPublicKey::from_public_key_der or the
appropriate rsa::pkcs8 decode function) so callers don't incur repeated
private-key deserialization in RsaKeyPair::rsa_public_key(); if no public-key
DER accessor exists, update the API/docs to recommend callers call
rsa_private_key() once and derive the public key themselves.

In `@crates/uselesskey-rustls/Cargo.toml`:
- Around line 31-35: The server-config and client-config features currently
enable dep:rustls but do not pull in any crypto provider, causing a build
failure; fix this by either updating Cargo.toml so server-config and
client-config require one of the provider features (e.g., include "rustls-ring"
or "rustls-aws-lc-rs" in the feature arrays for server-config and client-config)
or add a compile-time check in the crate root (lib.rs) that emits a
compile_error when server-config or client-config is enabled without either
feature rustls-ring or rustls-aws-lc-rs; refer to the feature names
server-config, client-config, rustls-ring, rustls-aws-lc-rs and the dep:rustls
entry when applying the change.

In `@crates/uselesskey-rustls/README.md`:
- Around line 3-5: Update the README opening to reflect that this crate
integrates with both rustls-pki-types and rustls itself: change the first
sentence to mention "rustls" (or "rustls and rustls-pki-types") integration for
the uselesskey test fixtures and add a link to the rustls docs; also ensure the
second sentence still explains conversion to rustls types and the optional
ServerConfig/ClientConfig builders (referencing rustls::ServerConfig and
rustls::ClientConfig) so the broader scope is accurately communicated.

In `@crates/uselesskey-rustls/src/config.rs`:
- Around line 359-527: The three tests tls_handshake_roundtrip,
mtls_with_provider_roundtrip, and mtls_roundtrip duplicate the same
client↔server handshake loop; extract that logic into a helper function (e.g.,
drive_handshake or complete_handshake) that accepts mutable references to
rustls::ClientConnection and rustls::ServerConnection and runs the byte-transfer
loop with the progress flag and safety assert using MAX_HANDSHAKE_ITERATIONS,
then replace the loop in each test with a call to that helper (refer to the test
functions tls_handshake_roundtrip, mtls_with_provider_roundtrip, mtls_roundtrip
and the helper name you create).

In `@crates/uselesskey-rustls/src/lib.rs`:
- Around line 286-293: In test_ecdsa_es384_private_key replace the incorrect
method call keypair.private_key_derustls() with the correct identifier
keypair.private_key_der_rustls() so the assertion uses the actual Rustls-format
private key; update the variable/key reference in that function (key =
keypair.private_key_der_rustls()) to match the corrected method name.

In `@crates/uselesskey-x509/src/cert.rs`:
- Around line 242-255: Extract the duplicated base_time derivation into a shared
helper function (e.g., fn derive_base_time(label: &str, subject_cn: &str,
issuer_cn: &str, rsa_bits: u32) -> DateTime/Type) in util.rs and replace the
inlined blocks in cert.rs and chain.rs to call this helper; the helper should
perform the same steps (create blake3::Hasher, update with lengths and bytes of
label, subject_cn, issuer_cn, update with rsa_bits as u32 in big-endian, then
call deterministic_base_time(hasher)) so callers pass label, spec.subject_cn,
spec.issuer_cn and spec.rsa_bits and get back the base_time.
- Around line 210-212: The trivial wrapper function load_inner simply forwards
to load_inner_with_spec with identical arguments; remove the load_inner function
and replace its single call site with a direct call to
load_inner_with_spec(factory, label, spec, variant), preserving the return type
(Arc<Inner>) and any ownership/borrowing semantics; delete the unused load_inner
definition and run a build to ensure no other references remain.

In `@crates/uselesskey-x509/src/chain_spec.rs`:
- Around line 136-137: The stable_bytes implementation currently does a lossy
cast with (self.rsa_bits as u32) which can truncate on 64-bit platforms; update
the code in stable_bytes (or where rsa_bits is serialized) to perform a checked
conversion using u32::try_from(self.rsa_bits) and propagate or handle the Result
(return an error or panic with a clear message) instead of silently truncating,
or alternatively change the rsa_bits field type to u32 in the struct so no
conversion is needed; ensure you update matching uses of rsa_bits elsewhere
(constructors/decoders) if you change the field type.
- Around line 49-52: Change the default leaf certificate lifetime from 3650 to
365 by updating the default value for leaf_validity_days in the ChainSpec/chain
spec struct initialization (symbol: leaf_validity_days) so leaf defaults align
with util.rs expectations for "good leaf certs"; also update the corresponding
test that asserts the default leaf validity (the test referencing the ChainSpec
default) to expect 365 instead of 3650. Ensure any documented comments or
constants referencing 10-year leaf validity are amended to reflect the new
365-day default.

In `@crates/uselesskey-x509/src/negative.rs`:
- Around line 85-97: The two functions corrupt_cert_pem and truncate_cert_der
are thin wrappers that simply call uselesskey_core::negative::corrupt_pem and
uselesskey_core::negative::truncate_der with identical signatures; replace the
wrappers with direct re-exports so callers can use the domain-specific path
without indirection: remove the corrupt_cert_pem and truncate_cert_der functions
and add pub use uselesskey_core::negative::corrupt_pem as corrupt_cert_pem and
pub use uselesskey_core::negative::truncate_der as truncate_cert_der (or
equivalent re-export names) so the same identifiers remain available but now
reference the core implementations directly.
- Around line 47-54: The inline comment for X509Negative::SelfSignedButClaimsCA
is misleading: although spec.key_usage is set to KeyUsage::ca() (so the cert can
structurally sign via key_cert_sign), the real negative condition is that the
cert is self-signed while claiming CA authority without a proper chain; update
the comment next to X509Negative::SelfSignedButClaimsCA (and references to
spec.is_ca / spec.key_usage) to match the description() text by noting that the
certificate is self-signed and claims CA status despite lacking a valid chain,
rather than saying it "can't actually sign anything valid."

In `@crates/uselesskey-x509/src/spec.rs`:
- Around line 206-221: The public methods not_before_duration() and
not_after_duration() expose a lossy unsigned Duration that hides direction from
NotBeforeOffset; fix by either (A) removing these public helpers entirely and
making any remaining helpers private (so callers must use not_before_offset
directly), or (B) redesigning their signatures to return a signed offset or
concrete time (e.g., return a chrono::Duration or an i64/i128 seconds offset, or
compute and return a SystemTime) so the caller can detect future vs past; update
all callers of not_before_duration / not_after_duration accordingly and adjust
docs/tests to reflect the new API; keep the not_before_offset enum usage intact.

In `@crates/uselesskey/examples/jwt_signing.rs`:
- Around line 28-35: The ECDSA and HMAC example blocks call methods that require
the `jwk` feature (e.g., ecdsa_key.public_jwk() and hmac_key.jwk()), so wrap
those blocks with combined cfg guards to require `jwk` in addition to
`ecdsa`/`hmac`; replace #[cfg(feature = "ecdsa")] with #[cfg(all(feature =
"ecdsa", feature = "jwk"))] for the block that uses EcdsaSpec::es256() and
ecdsa_key.public_jwk(), and replace #[cfg(feature = "hmac")] with
#[cfg(all(feature = "hmac", feature = "jwk"))] for the block that uses
HmacSpec::hs256() and hmac_key.jwk().

In `@crates/uselesskey/examples/negative_fixtures.rs`:
- Around line 90-94: Replace the manual open/read_to_string pattern that creates
cert_contents by calling std::fs::File::open(...).read_to_string(...) with a
single call to std::fs::read_to_string(temp_cert.path()).unwrap() and assign its
result to cert_contents; also remove the unnecessary std::io::Read import (the
one on line 11) since read_to_string eliminates that need. This change affects
the code that references cert_contents and temp_cert.path() in
negative_fixtures.rs and should use std::fs::read_to_string for simplicity and
clarity.

In `@crates/uselesskey/examples/tls_server.rs`:
- Around line 59-63: The example creates an expired certificate via
self_signed.expired() but assigns it to a variable (_expired) that's never used;
either drop the binding or print a meaningful field to demonstrate the result.
Update the snippet that calls self_signed.expired() to either remove the unused
_expired binding or to inspect and print a property such as _expired.not_after
(or another relevant field) so the example shows the certificate's expiry and is
informative for readers.
- Around line 24-26: The current println lines slice PEM strings like
&chain.root_cert_pem()[..50], which can panic if the PEM is shorter; replace
those byte-index slices with a safe truncation approach for
chain.root_cert_pem(), chain.intermediate_cert_pem(), and chain.leaf_cert_pem():
either build a preview via chars().take(50).collect::<String>() (preserves
Unicode boundaries) or use .get(..50).unwrap_or(&full_string) to avoid panics,
and then print the resulting safe preview instead of direct byte slicing.
- Around line 49-53: The current prints use fragile byte-slicing
(&self_signed.cert_pem()[..50] and &self_signed.private_key_pkcs8_pem()[..50])
which can panic on multibyte UTF-8; fix by binding the PEM strings to locals
(e.g., let cert = self_signed.cert_pem(); let key =
self_signed.private_key_pkcs8_pem();) and then safely take the first N
characters for display via .chars().take(50).collect::<String>() when formatting
the println! calls for Certificate and Private key so you never index into the
string by bytes.

In `@deny.toml`:
- Line 16: Add an inline comment on the allow = [...] line in deny.toml
explaining why "OpenSSL" is permitted: note that the transitive dependency
uselesskey-aws-lc-rs (and its aws-lc-sys) requires OpenSSL via its license
expression ("ISC AND (Apache-2.0 OR ISC) AND OpenSSL"), so removing "OpenSSL"
would block that dependency; reference those crate names (uselesskey-aws-lc-rs,
aws-lc-sys) and the license string in the comment for future maintainers.

In `@docs/architecture.md`:
- Around line 83-91: Replace the informal phrase "a bunch of" in the "Why
\"variant\"" section (the sentence starting "Variant strings solve a bunch of
test cases cleanly:") with a more formal word such as "several" or "many" so the
sentence reads e.g. "Variant strings solve several test cases cleanly:"; keep
the rest of the paragraph and examples unchanged.
- Around line 121-126: Update the "Planned adapters" section to reflect that
`uselesskey-rustls` and `uselesskey-ring` are included in this PR (their
Cargo.toml and src/lib.rs exist) by either moving them into the main adapter
list or removing the "Planned" heading; additionally add the new adapters
introduced here (`uselesskey-rustcrypto`, `uselesskey-aws-lc-rs`) and reformat
entries to use backticks and short descriptions (e.g., `uselesskey-rustls` —
returns `PrivateKeyDer` / `CertificateDer` directly, `uselesskey-ring` — returns
ring's native key types) so the documentation matches the actual crates added in
this change.

In `@docs/release.md`:
- Around line 7-36: The release docs missing the new adapter crates: add
`uselesskey-ring`, `uselesskey-rustcrypto`, `uselesskey-rustls`, and
`uselesskey-aws-lc-rs` to both the numbered publish-order list and the `cargo
publish -p ...` commands in docs/release.md, placing them in the proper
dependency order relative to existing crates (after core/key-type crates and
alongside other adapters such as `uselesskey-jsonwebtoken`), so the final list
and publish commands include all nine original crates plus these four adapters
in correct build/publish order.

In `@docs/roadmap.md`:
- Around line 42-52: The roadmap's "X.509 — cert chains and negative fixtures"
section is still under Planned but the PR claims "three-level chains, CRLs, and
negative fixtures" are implemented; update the document by moving the entire
"X.509 — cert chains and negative fixtures" heading and its two checklist items
("Cert chain fixtures" and "X.509 negative fixtures") into the Implemented
section (or mark each checkbox as completed), ensuring the sub-items (Root CA →
Intermediate → Leaf, Deterministic serial numbers, Expired certificates, Wrong
SAN, Unknown CA, Revoked cert) are either checked off or removed from Planned so
the roadmap and the PR summary are consistent.
- Around line 54-61: The roadmap is missing the new adapter crate; add a checked
Implemented entry for uselesskey-rustcrypto in the Implemented section: include
a bullet "- [x] **`uselesskey-rustcrypto`**" with two sub-bullets "RustCrypto
key conversions" and "Native key types for RustCrypto ecosystem users" so the
crate is listed alongside the other adapter crates (refer to the existing
adapter entries like `uselesskey-rustls` / `uselesskey-ring` for formatting).
- Around line 54-61: The roadmap incorrectly leaves the adapter crates under
"Planned"; move the three entries for `uselesskey-rustls`, `uselesskey-ring`,
and `uselesskey-aws-lc-rs` from the "Adapter crates"/planned list into the
"Implemented" section and mark their checkboxes complete (change "- [ ]" to "-
[x]") and keep their existing descriptions (including "Returns
`rustls::pki_types::PrivateKeyDer`, `CertificateDer`" and "Native key types for
`ring` and `aws-lc-rs` users") so the docs reflect that these new adapter crates
were added in this PR.
- Around line 32-38: The roadmap's X.509 entry is out of date: update the "X.509
leaf certificates" section for the uselesskey-x509 crate to reflect that
three-level chains, CRLs, and negative fixtures are implemented in this PR (in
addition to the existing self-signed certs and X509FactoryExt), so mark those
items as completed and remove them from the "Planned" list (ensure references to
"three-level chains", "CRLs", "negative fixtures", and "X509FactoryExt" are
moved to the checked/implemented items).

In `@xtask/src/main.rs`:
- Line 152: Extract the duplicated hardcoded path "target/xtask/receipt.json"
into a single constant (e.g., const RECEIPT_PATH: &str =
"target/xtask/receipt.json") and replace all direct uses with that constant
where receipt::Runner::new(...) is called; update the five call sites named
ci(), feature_matrix_cmd(), coverage(), publish_preflight(), and pr() so each
uses RECEIPT_PATH instead of the literal to keep them in sync.
- Around line 709-784: walk_for_blobs currently recurses directories unbounded
and will follow symlink loops; fix by preventing infinite recursion: add either
a depth limit parameter to walk_for_blobs (e.g., max depth 50) and return early
when exceeded, or detect and skip symlinked directories by checking
entry.file_type().is_symlink() (and/or entry.metadata().is_dir()) before
recursing; you can also maintain a visited set of canonicalized paths
(HashSet<PathBuf>) to skip already-seen directories. Update walk_for_blobs
signature (and callers like find_secret_blobs) to accept the new depth/visited
parameter(s), and ensure is_ignored_dir still applies after resolving whether an
entry is a real directory (use entry.path()). Keep error contexts (with_context)
intact.
- Around line 895-1057: Tests mutate environment with unsafe
env::set_var/remove_var and manually call restore_env, which can leak env
changes on panic; implement an RAII EnvGuard (referencing ENV_LOCK and
restore_env behavior) that captures previous XTASK_BASE_REF and GITHUB_BASE_REF
on creation and restores them in Drop, then replace the manual
prev_*/restore_env pairs in resolve_base_ref_* tests with EnvGuard instances so
env is always restored even if the test panics; keep the existing ENV_LOCK usage
to serialize tests.
- Around line 846-893: The dep_guard function currently bails whenever any
unique rand_core version string is collected from `cargo tree --duplicates`;
change the check so it only errors when multiple distinct versions are present
(i.e., require versions.len() >= 2 to bail). Concretely, in dep_guard, keep the
existing parsing that fills the versions Vec<String>, but replace the final
if/else that bails on non-empty with a check on versions.len(): if 0 -> print ok
and return Ok(()), if 1 -> treat as ok (optionally log the single version) and
return Ok(()), and only construct the error message and bail when versions.len()
>= 2; reference symbols: dep_guard, versions, and the final bail block that
formats and joins versions.
- Around line 598-626: In run_impacted_tests remove the dead second check that
tests for "uselesskey-bdd": the targets Vec is already filtered for that name,
so delete the if name == "uselesskey-bdd" { continue; } guard inside the for
name in targets.drain(..) loop and keep the step creation (step_name and
runner.step closure) as-is; this removes unreachable code and avoids redundant
checks.
- Around line 151-193: run_ci_plan currently always runs the mutants and fuzz_pr
steps which will bail if their tools are missing; add availability checks like
the existing is_llvm_cov_installed() check and skip those steps when tools
aren't installed. Concretely, in run_ci_plan wrap runner.step("mutants", None,
mutants)? and runner.step("fuzz", None, fuzz_pr)? with if
is_cargo_mutants_installed() { runner.step(...)? } else { runner.skip("mutants",
Some("cargo-mutants not installed".into())); } (and similarly for fuzz_pr using
an is_cargo_fuzz_installed() check and runner.skip("fuzz", ...)). Ensure the
skip messages match the coverage pattern and that no bail! from mutants/fuzz_pr
can escape when the check indicates the tool is missing.

In `@xtask/src/plan.rs`:
- Around line 105-114: The boolean expression in is_rust_code_change relies on
&& binding tighter than || which hurts readability; parenthesize the crates
branch to make precedence explicit: wrap (path.starts_with("crates/") &&
(path.contains("/src/") || path.contains("/tests/"))) so it's obvious that the
crates check and its src/tests conditions form one unit, leaving the other
starts_with checks as separate alternatives.
- Around line 38-43: The compound conditional that sets bdd_feature_changed is
correct but unclear; update the if in plan.rs (the block that references
variable bdd_feature_changed and uses path) to add explicit parentheses around
the last conjunct — e.g., wrap (path.ends_with(".feature") &&
path.contains("features/")) — and optionally group the OR terms with parentheses
so the intent is obvious when scanning the condition.
- Around line 148-206: In the dependents function the mapping for
"uselesskey-bdd" is self-referential ("uselesskey-bdd" => &["uselesskey-bdd"])
which is redundant and misleading; update the dependents function to return an
empty slice for the "uselesskey-bdd" key (i.e., replace the self-reference with
&[]) so the dependents table accurately reflects no dependents for
uselesskey-bdd while leaving other entries (e.g., "uselesskey-jwk") unchanged.

In `@xtask/src/receipt.rs`:
- Around line 9-31: StepReceipt::status and FeatureMatrixEntry::status are plain
Strings; replace them with a serde-friendly enum (e.g., enum Status { Ok,
Failed, Skipped } with #[derive(Serialize, Deserialize)] and #[serde(rename_all
= "lowercase")]) to enforce allowed values; update the structs Receipt,
StepReceipt, and FeatureMatrixEntry to use this Status enum, adjust any
construction sites and serialization usage, and update the summary() logic to
match on Status variants instead of string comparisons so exhaustiveness is
enforced by the compiler.
- Around line 63-96: The timing uses start.elapsed() twice causing inconsistent
durations; capture the elapsed Duration once at the start of each match arm and
reuse it for both the printed seconds and the StepReceipt duration_ms. In both
the Ok and Err branches of the function (the block that prints "==> {name}" and
pushes StepReceipt onto self.receipt.steps), call let elapsed = start.elapsed();
then compute secs from elapsed.as_secs_f64() and duration_ms from
elapsed.as_millis() as u64, replacing the duplicate start.elapsed() calls so the
log and StepReceipt use the same measured duration.

Comment thread AGENTS.md
@@ -0,0 +1,128 @@
# CLAUDE.md

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

File heading doesn't match filename.

This file is AGENTS.md but the heading reads # CLAUDE.md. Either rename the file or update the heading to # AGENTS.md.

🤖 Prompt for AI Agents
In `@AGENTS.md` at line 1, The markdown heading inside the file is incorrect:
change the top-level heading text from "# CLAUDE.md" to "# AGENTS.md" (update
the heading string at the top of the file) so the file heading matches the
filename and project conventions.

Comment thread AGENTS.md
Comment on lines +77 to +87
- **`crates/uselesskey`** - Public facade crate, re-exports stable API
- **`crates/uselesskey-core`** - Core factory, derivation, caching, negative fixtures
- **`crates/uselesskey-jwk`** - Typed JWK/JWKS helpers and `JwksBuilder`
- **`crates/uselesskey-rsa`** - RSA fixtures via `RsaFactoryExt` trait
- **`crates/uselesskey-ecdsa`** - ECDSA (P-256/P-384) fixtures via `EcdsaFactoryExt` trait
- **`crates/uselesskey-ed25519`** - Ed25519 fixtures via `Ed25519FactoryExt` trait
- **`crates/uselesskey-hmac`** - HMAC (HS256/HS384/HS512) fixtures via `HmacFactoryExt` trait
- **`crates/uselesskey-x509`** - X.509 self-signed certificate fixtures via `X509FactoryExt` trait
- **`crates/uselesskey-jsonwebtoken`** - Adapter: returns `jsonwebtoken` `EncodingKey`/`DecodingKey` directly
- **`crates/uselesskey-bdd`** - Cucumber BDD tests
- **`xtask`** - Build automation commands

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Workspace structure is out of sync with CLAUDE.md.

CLAUDE.md (Lines 88–94) lists additional adapter crates (uselesskey-rustls, uselesskey-ring, uselesskey-rustcrypto, uselesskey-aws-lc-rs) that are absent here. Similarly, the cargo xtask ci description on Line 52 differs from the one in CLAUDE.md Line 52 (which includes matrix + guard + bdd + no-blob + mutants + fuzz). These two files will drift quickly if maintained separately.

Consider either generating one from the other, or consolidating into a single file.

🧰 Tools
🪛 LanguageTool

[typographical] ~77-~77: To join two clauses or introduce examples, consider using an em dash.
Context: ...ace Structure - crates/uselesskey - Public facade crate, re-exports stable A...

(DASH_RULE)


[typographical] ~78-~78: To join two clauses or introduce examples, consider using an em dash.
Context: ...table API - crates/uselesskey-core - Core factory, derivation, caching, negat...

(DASH_RULE)


[typographical] ~79-~79: To join two clauses or introduce examples, consider using an em dash.
Context: ...e fixtures - crates/uselesskey-jwk - Typed JWK/JWKS helpers and JwksBuilder...

(DASH_RULE)


[typographical] ~80-~80: To join two clauses or introduce examples, consider using an em dash.
Context: ...ksBuilder - **crates/uselesskey-rsa** - RSA fixtures via RsaFactoryExt` trait -...

(DASH_RULE)


[typographical] ~81-~81: To join two clauses or introduce examples, consider using an em dash.
Context: ...t trait - **crates/uselesskey-ecdsa** - ECDSA (P-256/P-384) fixtures via EcdsaF...

(DASH_RULE)


[typographical] ~82-~82: To join two clauses or introduce examples, consider using an em dash.
Context: ... trait - crates/uselesskey-ed25519 - Ed25519 fixtures via Ed25519FactoryExt...

(DASH_RULE)


[typographical] ~83-~83: To join two clauses or introduce examples, consider using an em dash.
Context: ...xt trait - **crates/uselesskey-hmac** - HMAC (HS256/HS384/HS512) fixtures via H...

(DASH_RULE)


[typographical] ~84-~84: To join two clauses or introduce examples, consider using an em dash.
Context: ...xt trait - **crates/uselesskey-x509`** - X.509 self-signed certificate fixtures v...

(DASH_RULE)


[typographical] ~85-~85: To join two clauses or introduce examples, consider using an em dash.
Context: ...t - crates/uselesskey-jsonwebtoken - Adapter: returns jsonwebtoken `Encodin...

(DASH_RULE)


[typographical] ~86-~86: To join two clauses or introduce examples, consider using an em dash.
Context: ... directly - **crates/uselesskey-bdd** - Cucumber BDD tests - **xtask`** - Build...

(DASH_RULE)


[typographical] ~87-~87: To join two clauses or introduce examples, consider using an em dash.
Context: ...dd** - Cucumber BDD tests - **xtask`** - Build automation commands ### Key Conce...

(DASH_RULE)

🤖 Prompt for AI Agents
In `@AGENTS.md` around lines 77 - 87, AGENTS.md's workspace list and the "cargo
xtask ci" description are out of sync with CLAUDE.md; update the AGENTS.md
entries to include the missing adapter crates (uselesskey-rustls,
uselesskey-ring, uselesskey-rustcrypto, uselesskey-aws-lc-rs) and align the
"cargo xtask ci" description to match CLAUDE.md (include "matrix + guard + bdd +
no-blob + mutants + fuzz"), or choose one canonical source and regenerate the
other from it so the lists (the crate lines like `crates/uselesskey-rustls`,
etc.) and the `cargo xtask ci` description remain consistent across both
documents.

Comment thread AGENTS.md
## Configuration Files

- `rustfmt.toml` - Formatting: Unix newlines, crate-level imports
- `clippy.toml` - MSRV 1.88

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check actual MSRV in clippy.toml and rust-toolchain.toml
echo "=== clippy.toml ==="
cat clippy.toml 2>/dev/null || echo "not found"
echo ""
echo "=== rust-toolchain.toml ==="
cat rust-toolchain.toml 2>/dev/null || echo "not found"
echo ""
echo "=== workspace Cargo.toml rust-version ==="
grep -i 'rust-version' Cargo.toml 2>/dev/null || echo "not found"

Repository: EffortlessMetrics/uselesskey

Length of output: 317


Update MSRV documentation from 1.88 to 1.92.

The actual MSRV in clippy.toml is 1.92, not 1.88. This is consistent across rust-toolchain.toml and Cargo.toml, all specifying version 1.92. Line 126 of AGENTS.md should be updated to reflect the correct MSRV.

🤖 Prompt for AI Agents
In `@AGENTS.md` at line 126, Update the MSRV mention in AGENTS.md so the line
currently reading "`clippy.toml` - MSRV 1.88" matches the actual toolchain
version; change it to "`clippy.toml` - MSRV 1.92" to be consistent with
rust-toolchain.toml and Cargo.toml (search for the exact string "`clippy.toml` -
MSRV 1.88" and replace it with the updated version).

Comment thread Cargo.toml
Comment on lines 71 to +76
[profile.dev]
# RSA keygen can be slow in debug; modest opt helps without full release build.
opt-level = 1
# RSA keygen can be slow in debug; opt-level 2 keeps BDD/RSA tests from looking hung.
opt-level = 2

[profile.test]
opt-level = 1
opt-level = 2

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

opt-level = 2 for both dev and test profiles is a meaningful compilation trade-off.

This will noticeably increase incremental compile times for the entire workspace, not just RSA-related crates. Consider whether a per-crate [profile.dev.package.rsa] override would be more surgical, avoiding the compile-time penalty for non-crypto crates.

♻️ Targeted opt-level override (alternative)
 [profile.dev]
-# RSA keygen can be slow in debug; opt-level 2 keeps BDD/RSA tests from looking hung.
-opt-level = 2
+opt-level = 0
+
+# Only optimize the heavy crypto crates in dev/test profiles
+[profile.dev.package.rsa]
+opt-level = 2
+
+[profile.dev.package.ed25519-dalek]
+opt-level = 2
+
+[profile.dev.package.num-bigint-dig]
+opt-level = 2

 [profile.test]
-opt-level = 2
+opt-level = 0
+
+[profile.test.package.rsa]
+opt-level = 2
+
+[profile.test.package.ed25519-dalek]
+opt-level = 2
+
+[profile.test.package.num-bigint-dig]
+opt-level = 2
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[profile.dev]
# RSA keygen can be slow in debug; modest opt helps without full release build.
opt-level = 1
# RSA keygen can be slow in debug; opt-level 2 keeps BDD/RSA tests from looking hung.
opt-level = 2
[profile.test]
opt-level = 1
opt-level = 2
[profile.dev]
opt-level = 0
# Only optimize the heavy crypto crates in dev/test profiles
[profile.dev.package.rsa]
opt-level = 2
[profile.dev.package.ed25519-dalek]
opt-level = 2
[profile.dev.package.num-bigint-dig]
opt-level = 2
[profile.test]
opt-level = 0
[profile.test.package.rsa]
opt-level = 2
[profile.test.package.ed25519-dalek]
opt-level = 2
[profile.test.package.num-bigint-dig]
opt-level = 2
🤖 Prompt for AI Agents
In `@Cargo.toml` around lines 71 - 76, The change raised opt-level = 2 for both
[profile.dev] and [profile.test], which increases incremental compile times
workspace-wide; instead revert those global opt-levels to a lower level and add
a per-crate profile override for only the RSA/crypto crate(s) that need higher
optimization (use the Cargo profile override header syntax e.g.
[profile.dev.package."your_rsa_crate_name"] and
[profile.test.package."your_rsa_crate_name"] with opt-level = 2), so only the
crate(s) requiring faster release-like codegen pay the compile cost while other
crates keep fast incremental builds.

Comment thread CLAUDE.md
Comment on lines +69 to +74

Run a single test:
```bash
cargo test -p uselesskey-core test_name
cargo test -p uselesskey-rsa test_name
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing blank line before fenced code block.

Per markdownlint MD031, add a blank line before the fenced code block on Line 71.

Proposed fix
 Run a single test:
+
 ```bash
 cargo test -p uselesskey-core test_name
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Run a single test:
```bash
cargo test -p uselesskey-core test_name
cargo test -p uselesskey-rsa test_name
```
Run a single test:
🧰 Tools
🪛 markdownlint-cli2 (0.20.0)

[warning] 71-71: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

🤖 Prompt for AI Agents
In `@CLAUDE.md` around lines 69 - 74, Add a blank line immediately before the
fenced code block that begins with "```bash" (the code block containing "cargo
test -p uselesskey-core test_name" and "cargo test -p uselesskey-rsa test_name")
in CLAUDE.md so the markdown conforms to MD031; ensure there is an empty line
separating the preceding paragraph and the code fence.

Comment thread deny.toml

[licenses]
allow = ["MIT", "Apache-2.0", "BSD-3-Clause", "ISC", "CC0-1.0"]
allow = ["MIT", "Apache-2.0", "BSD-3-Clause", "ISC", "CC0-1.0", "OpenSSL"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for openssl and aws-lc related dependencies in TOML files
rg -i 'openssl|aws-lc' --include='*.toml' -B 2 -A 2

Repository: EffortlessMetrics/uselesskey

Length of output: 513


🏁 Script executed:

#!/bin/bash
# Search for openssl and aws-lc related dependencies
echo "=== Searching Cargo.toml files ==="
find . -name 'Cargo.toml' -type f | head -20 | xargs grep -i 'openssl\|aws-lc' 2>/dev/null || echo "No matches in Cargo.toml files"

echo -e "\n=== Searching Cargo.lock for openssl/aws-lc ==="
if [ -f Cargo.lock ]; then
  grep -i 'name = ".*openssl\|name = ".*aws-lc' Cargo.lock | head -20
else
  echo "No Cargo.lock found"
fi

echo -e "\n=== Listing all Cargo.toml files ==="
find . -name 'Cargo.toml' -type f

Repository: EffortlessMetrics/uselesskey

Length of output: 1393


🏁 Script executed:

#!/bin/bash
# Read the deny.toml file to see full context
echo "=== deny.toml content ==="
cat deny.toml

echo -e "\n=== Checking aws-lc-sys and aws-lc-rs in Cargo.lock ==="
grep -A 5 'name = "aws-lc-sys"' Cargo.lock | head -20
grep -A 5 'name = "aws-lc-rs"' Cargo.lock | head -20

echo -e "\n=== Checking ring in Cargo.lock ==="
grep -A 5 'name = "ring"' Cargo.lock | head -20

Repository: EffortlessMetrics/uselesskey

Length of output: 1214


🏁 Script executed:

#!/bin/bash
# Look for license metadata in Cargo.lock for the relevant crates
echo "=== Full Cargo.lock entries for aws-lc-sys, aws-lc-rs, and ring ==="
rg -A 20 '^name = "aws-lc-sys"' Cargo.lock
echo -e "\n---\n"
rg -A 20 '^name = "aws-lc-rs"' Cargo.lock
echo -e "\n---\n"
rg -A 20 '^name = "ring"' Cargo.lock

# Check git history of deny.toml to see when OpenSSL was added
echo -e "\n=== Git log for deny.toml ==="
git log --oneline deny.toml 2>/dev/null | head -10

# Check for any comments in deny.toml or related files
echo -e "\n=== Search for OpenSSL references in repo ==="
rg -i 'openssl' --type-list | head -5
rg -i 'openssl' . 2>/dev/null | head -20

Repository: EffortlessMetrics/uselesskey

Length of output: 2896


🌐 Web query:

aws-lc-sys crate license crates.io

💡 Result:

On crates.io, the aws-lc-sys crate’s declared license (SPDX expression) is:

ISC AND (Apache-2.0 OR ISC) AND OpenSSL [1][2]

This is visible in the crate’s published Cargo.toml (e.g., for version 0.37.0). [1]

Sources: [1] [2]


🌐 Web query:

aws-lc-rs crate license

💡 Result:

The aws-lc-rs Rust crate is dual-licensed: you may use it under either the Apache License 2.0 (Apache-2.0) or the ISC License (ISC). [1][2]

Sources: GitHub repository README/license section [1] and docs.rs crate “License” section [2].

[1] (github.com)
[2] (docs.rs)

Citations:


🌐 Web query:

ring crate 0.17 license openssl

💡 Result:

ring 0.17.x is not OpenSSL and does not use the OpenSSL license. Instead, it vendors/derives crypto code largely from BoringSSL and its own code, under these licenses:

  • Crate (SPDX) license for ring 0.17.x: Apache-2.0 AND ISC (as declared in Cargo.toml). [1]
  • Bundled/attributed code: includes code sourced from BoringSSL under Apache-2.0 (and some BoringSSL-sourced bits under ISC), plus some code sourced from once_cell under MIT or Apache-2.0 (per the LICENSE files). [2]
  • A downstream packaging summary (Fedora) reflects this mix as Apache-2.0 AND ISC AND (MIT OR Apache-2.0). [3]

So, if your concern is “is ring 0.17 under OpenSSL / does it link to OpenSSL?”: no—its licensing is Apache-2.0 + ISC (with some MIT/Apache-2.0 components), not OpenSSL. [1][2]

Sources: [1] [2] [3]


🏁 Script executed:

#!/bin/bash
# Check if OpenSSL was recently added or if it's been there longer
echo "=== Recent commits mentioning deny.toml or licenses ==="
git log --all --oneline --grep='deny\|license\|openssl\|aws-lc' 2>/dev/null | head -20

echo -e "\n=== Show the actual change to deny.toml ==="
git show 648076a:deny.toml 2>/dev/null | grep -A 1 'allow =' || echo "OpenSSL present in current version"

Repository: EffortlessMetrics/uselesskey

Length of output: 274


Consider adding an inline comment documenting why the OpenSSL license is allowed.

The OpenSSL license is required by aws-lc-sys (transitive dependency via uselesskey-aws-lc-rs), which is licensed under ISC AND (Apache-2.0 OR ISC) AND OpenSSL. A comment here would help future contributors understand the rationale and avoid accidentally removing it.

📝 Suggested documentation
+# "OpenSSL" is required by aws-lc-sys (transitive dep of uselesskey-aws-lc-rs)
 allow = ["MIT", "Apache-2.0", "BSD-3-Clause", "ISC", "CC0-1.0", "OpenSSL"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
allow = ["MIT", "Apache-2.0", "BSD-3-Clause", "ISC", "CC0-1.0", "OpenSSL"]
# "OpenSSL" is required by aws-lc-sys (transitive dep of uselesskey-aws-lc-rs)
allow = ["MIT", "Apache-2.0", "BSD-3-Clause", "ISC", "CC0-1.0", "OpenSSL"]
🤖 Prompt for AI Agents
In `@deny.toml` at line 16, Add an inline comment on the allow = [...] line in
deny.toml explaining why "OpenSSL" is permitted: note that the transitive
dependency uselesskey-aws-lc-rs (and its aws-lc-sys) requires OpenSSL via its
license expression ("ISC AND (Apache-2.0 OR ISC) AND OpenSSL"), so removing
"OpenSSL" would block that dependency; reference those crate names
(uselesskey-aws-lc-rs, aws-lc-sys) and the license string in the comment for
future maintainers.

Comment thread docs/release.md
Comment on lines +7 to +36
1. `uselesskey-core`
2. `uselesskey-jwk`
3. `uselesskey-rsa`
4. `uselesskey-ecdsa`
5. `uselesskey-ed25519`
6. `uselesskey-hmac`
7. `uselesskey-x509`
8. `uselesskey`
9. `uselesskey-jsonwebtoken` (adapter, depends on key type crates)

This list matches the `cargo xtask publish-check` order.

## Dry run

```bash
cargo xtask publish-check
```

## Publish

```bash
cargo publish -p uselesskey-core
cargo publish -p uselesskey-jwk
cargo publish -p uselesskey-rsa
cargo publish -p uselesskey-ecdsa
cargo publish -p uselesskey-ed25519
cargo publish -p uselesskey-hmac
cargo publish -p uselesskey-x509
cargo publish -p uselesskey
cargo publish -p uselesskey-jsonwebtoken

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's see what crates exist in the workspace
echo "=== Workspace members in crates/ ==="
find crates -maxdepth 2 -name "Cargo.toml" -type f | sort | while read f; do
  dir=$(dirname "$f")
  name=$(grep '^name = ' "$f" | head -1 | sed 's/.*"\(.*\)"/\1/')
  publish=$(grep 'publish' "$f" 2>/dev/null | head -1 || echo "publish not specified")
  echo "$dir: name=$name, $publish"
done

Repository: EffortlessMetrics/uselesskey

Length of output: 815


🏁 Script executed:

#!/bin/bash
# Let's also check the root Cargo.toml for workspace members
echo "=== Workspace members from root Cargo.toml ==="
cat Cargo.toml | grep -A 100 '\[workspace\]' | grep -E '^\s*members|^[^[]' | head -50

Repository: EffortlessMetrics/uselesskey

Length of output: 1552


🏁 Script executed:

#!/bin/bash
# Check what's currently in docs/release.md
echo "=== Current publish order in docs/release.md ==="
sed -n '7,36p' docs/release.md

Repository: EffortlessMetrics/uselesskey

Length of output: 786


Add missing adapter crates to the publish order.

The PR adds four new adapter crates (uselesskey-ring, uselesskey-rustcrypto, uselesskey-rustls, and uselesskey-aws-lc-rs) to the workspace, but they are not included in the publish order or cargo publish commands in docs/release.md. These crates need to be added in the appropriate dependency order.

🤖 Prompt for AI Agents
In `@docs/release.md` around lines 7 - 36, The release docs missing the new
adapter crates: add `uselesskey-ring`, `uselesskey-rustcrypto`,
`uselesskey-rustls`, and `uselesskey-aws-lc-rs` to both the numbered
publish-order list and the `cargo publish -p ...` commands in docs/release.md,
placing them in the proper dependency order relative to existing crates (after
core/key-type crates and alongside other adapters such as
`uselesskey-jsonwebtoken`), so the final list and publish commands include all
nine original crates plus these four adapters in correct build/publish order.

Comment thread docs/roadmap.md
Comment on lines +32 to +38
- [x] **X.509 leaf certificates** (`uselesskey-x509`)
- Self-signed certs via `rcgen`
- Configurable: CN, SANs, validity period, key usage
- `X509FactoryExt` trait
- [x] **`uselesskey-jsonwebtoken`**
- Returns `jsonwebtoken::EncodingKey` / `DecodingKey` directly
- Reduces boilerplate in JWT tests

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Update the X.509 entry to reflect additional implemented features.

The roadmap states only "X.509 leaf certificates" with "Self-signed certs" are implemented, but the PR summary explicitly documents that this PR includes "three-level chains, CRLs, and negative fixtures" for uselesskey-x509. Lines 44-52 list cert chains and negative fixtures as "Planned", creating an inconsistency with the actual implementation status.

📝 Suggested update to reflect complete implementation
 - [x] **X.509 leaf certificates** (`uselesskey-x509`)
   - Self-signed certs via `rcgen`
   - Configurable: CN, SANs, validity period, key usage
+  - Three-level cert chains (Root CA → Intermediate → Leaf)
+  - CRL generation
+  - Negative fixtures (expired certs, wrong SAN, unknown CA, revoked cert)
   - `X509FactoryExt` trait
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- [x] **X.509 leaf certificates** (`uselesskey-x509`)
- Self-signed certs via `rcgen`
- Configurable: CN, SANs, validity period, key usage
- `X509FactoryExt` trait
- [x] **`uselesskey-jsonwebtoken`**
- Returns `jsonwebtoken::EncodingKey` / `DecodingKey` directly
- Reduces boilerplate in JWT tests
- [x] **X.509 leaf certificates** (`uselesskey-x509`)
- Self-signed certs via `rcgen`
- Configurable: CN, SANs, validity period, key usage
- Three-level cert chains (Root CA → Intermediate → Leaf)
- CRL generation
- Negative fixtures (expired certs, wrong SAN, unknown CA, revoked cert)
- `X509FactoryExt` trait
- [x] **`uselesskey-jsonwebtoken`**
- Returns `jsonwebtoken::EncodingKey` / `DecodingKey` directly
- Reduces boilerplate in JWT tests
🤖 Prompt for AI Agents
In `@docs/roadmap.md` around lines 32 - 38, The roadmap's X.509 entry is out of
date: update the "X.509 leaf certificates" section for the uselesskey-x509 crate
to reflect that three-level chains, CRLs, and negative fixtures are implemented
in this PR (in addition to the existing self-signed certs and X509FactoryExt),
so mark those items as completed and remove them from the "Planned" list (ensure
references to "three-level chains", "CRLs", "negative fixtures", and
"X509FactoryExt" are moved to the checked/implemented items).

Comment thread docs/roadmap.md
Comment on lines +42 to +52
### X.509 — cert chains and negative fixtures

- [ ] **Cert chain fixtures**
- Root CA → Intermediate → Leaf
- Deterministic serial numbers

- [ ] **X.509 negative fixtures**
- Expired certificates
- Wrong SAN (hostname mismatch)
- Unknown CA (untrusted root)
- Revoked cert (with CRL/OCSP stub)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Move X.509 cert chains and negative fixtures to the Implemented section.

These features are listed as "Planned" but the PR summary documents that "three-level chains, CRLs, and negative fixtures" are implemented in this PR. This section should either be removed or the checkboxes should be marked as completed and moved to the Implemented section.

🤖 Prompt for AI Agents
In `@docs/roadmap.md` around lines 42 - 52, The roadmap's "X.509 — cert chains and
negative fixtures" section is still under Planned but the PR claims "three-level
chains, CRLs, and negative fixtures" are implemented; update the document by
moving the entire "X.509 — cert chains and negative fixtures" heading and its
two checklist items ("Cert chain fixtures" and "X.509 negative fixtures") into
the Implemented section (or mark each checkbox as completed), ensuring the
sub-items (Root CA → Intermediate → Leaf, Deterministic serial numbers, Expired
certificates, Wrong SAN, Unknown CA, Revoked cert) are either checked off or
removed from Planned so the roadmap and the PR summary are consistent.

Comment thread docs/roadmap.md
Comment on lines +54 to +61
### Adapter crates

- [ ] **`uselesskey-rustls`**
- Returns `rustls::pki_types::PrivateKeyDer`, `CertificateDer`
- Server/client config builders for TLS tests

- [ ] **`uselesskey-ring`** / **`uselesskey-aws-lc-rs`**
- Native key types for `ring` and `aws-lc-rs` users

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add the missing uselesskey-rustcrypto adapter.

The PR summary documents that uselesskey-rustcrypto (RustCrypto conversions) is a new adapter crate added in this PR, but it's not mentioned anywhere in the roadmap. This adapter should be added to the Implemented section for completeness.

📝 Suggested addition

Add to the Implemented section after line 38:

- [x] **`uselesskey-rustcrypto`**
  - RustCrypto key conversions
  - Native key types for RustCrypto ecosystem users

Or include it in an expanded adapter section within Implemented.

🤖 Prompt for AI Agents
In `@docs/roadmap.md` around lines 54 - 61, The roadmap is missing the new adapter
crate; add a checked Implemented entry for uselesskey-rustcrypto in the
Implemented section: include a bullet "- [x] **`uselesskey-rustcrypto`**" with
two sub-bullets "RustCrypto key conversions" and "Native key types for
RustCrypto ecosystem users" so the crate is listed alongside the other adapter
crates (refer to the existing adapter entries like `uselesskey-rustls` /
`uselesskey-ring` for formatting).

⚠️ Potential issue | 🟡 Minor

Move adapter crates to the Implemented section.

The adapter crates uselesskey-rustls, uselesskey-ring, and uselesskey-aws-lc-rs are listed as "Planned", but the PR summary explicitly states these are NEW adapter crates being added in this PR. The descriptions in lines 57-61 should be moved to the Implemented section with checkboxes marked complete.

🤖 Prompt for AI Agents
In `@docs/roadmap.md` around lines 54 - 61, The roadmap incorrectly leaves the
adapter crates under "Planned"; move the three entries for `uselesskey-rustls`,
`uselesskey-ring`, and `uselesskey-aws-lc-rs` from the "Adapter crates"/planned
list into the "Implemented" section and mark their checkboxes complete (change
"- [ ]" to "- [x]") and keep their existing descriptions (including "Returns
`rustls::pki_types::PrivateKeyDer`, `CertificateDer`" and "Native key types for
`ring` and `aws-lc-rs` users") so the docs reflect that these new adapter crates
were added in this PR.

@coderabbitai coderabbitai 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.

Review continued from previous batch...

Comment thread .github/workflows/ci.yml
Comment on lines +10 to 12
pr:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

No timeout-minutes set on either job.

Both jobs delegate to xtask commands that may include fuzz testing, mutation testing, and RSA key generation (noted as slow in the PR description). Without a timeout, a hung or pathological run could consume CI minutes indefinitely. Consider adding a reasonable timeout:

  pr:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
+   timeout-minutes: 30

Also applies to: 45-47

🤖 Prompt for AI Agents
In @.github/workflows/ci.yml around lines 10 - 12, The CI workflow jobs lack
timeout-minutes which can allow hung xtask runs to consume minutes indefinitely;
add a reasonable timeout-minutes (e.g., 30 or 60) to each job
definition—specifically update the job keyed as pr and the other CI job
referenced around the same block (the second job at lines ~45-47) to include
timeout-minutes: <minutes> so the GitHub Actions runner will cancel long-running
or stuck builds.

Comment thread .github/workflows/ci.yml
Comment on lines +27 to +31
- name: Install cargo-fuzz
run: cargo install cargo-fuzz

- name: Install cargo-mutants
run: cargo install cargo-mutants

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

cargo install from source on every run can be slow — consider caching or pre-built binaries.

cargo install cargo-fuzz and cargo install cargo-mutants compile from source. While Swatinem/rust-cache@v2 caches ~/.cargo/bin, the initial install (or after cache eviction) can add significant time. Consider using --locked to improve reproducibility, or binstall for pre-built binaries:

-      - name: Install cargo-fuzz
-        run: cargo install cargo-fuzz
-
-      - name: Install cargo-mutants
-        run: cargo install cargo-mutants
+      - name: Install tools
+        run: |
+          cargo install cargo-binstall
+          cargo binstall -y cargo-fuzz cargo-mutants

Also applies to: 60-64

🤖 Prompt for AI Agents
In @.github/workflows/ci.yml around lines 27 - 31, Update the GitHub Actions
steps named "Install cargo-fuzz" and "Install cargo-mutants" to avoid compiling
from source on every run: add the --locked flag to cargo install for
reproducible installs, and prefer installing prebuilt binaries (e.g., use
binstall or download a release tarball) or rely on the existing Rust cache
(~/.cargo/bin) to restore those tools rather than running cargo install
unconditionally; ensure the workflow checks the cache before installing and only
falls back to install when the binary is missing.

Comment thread .github/workflows/ci.yml
Comment on lines +37 to +43
- name: xtask pr
run: cargo xtask pr
- name: Upload receipt
uses: actions/upload-artifact@v4
with:
name: receipt-pr
path: target/xtask/receipt.json

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Receipt upload is skipped when xtask pr fails — consider if: always().

If xtask pr fails, the upload step won't run, losing the receipt artifact that could aid debugging. If the receipt is useful for diagnosing failures, add if: always():

      - name: Upload receipt
+       if: always()
        uses: actions/upload-artifact@v4
        with:
          name: receipt-pr
          path: target/xtask/receipt.json

The same applies to the main job receipt upload at Lines 69-73.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: xtask pr
run: cargo xtask pr
- name: Upload receipt
uses: actions/upload-artifact@v4
with:
name: receipt-pr
path: target/xtask/receipt.json
- name: xtask pr
run: cargo xtask pr
- name: Upload receipt
if: always()
uses: actions/upload-artifact@v4
with:
name: receipt-pr
path: target/xtask/receipt.json
🤖 Prompt for AI Agents
In @.github/workflows/ci.yml around lines 37 - 43, The "Upload receipt" artifact
step currently runs only on success and is skipped if the preceding "xtask pr"
step fails; update the Upload receipt steps (the step with name "Upload receipt"
that uses actions/upload-artifact@v4 and uploads name: receipt-pr path:
target/xtask/receipt.json) to include if: always() so the receipt artifact is
uploaded regardless of job success; apply the same change to the equivalent
"Upload receipt" in the main job.

Comment on lines +22 to +28
[features]
default = ["native"]
native = ["dep:aws-lc-rs"]
rsa = ["dep:uselesskey-rsa"]
ecdsa = ["dep:uselesskey-ecdsa"]
ed25519 = ["dep:uselesskey-ed25519"]
all = ["rsa", "ecdsa", "ed25519"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

fd -e rs . crates/uselesskey-aws-lc-rs/src/ -type f

Repository: EffortlessMetrics/uselesskey

Length of output: 242


🏁 Script executed:

cat crates/uselesskey-aws-lc-rs/Cargo.toml

Repository: EffortlessMetrics/uselesskey

Length of output: 1096


🏁 Script executed:

find crates/uselesskey-aws-lc-rs/src/ -name "*.rs" -type f | head -20 | xargs -I {} sh -c 'echo "=== {} ===" && cat -n {}'

Repository: EffortlessMetrics/uselesskey

Length of output: 9677


all feature doesn't include native — users with default-features = false won't get aws-lc-rs.

The all feature enables rsa, ecdsa, and ed25519 but not native. A consumer using default-features = false, features = ["all"] would get the key-type crate dependencies but not aws-lc-rs. Since all conversion traits (AwsLcRsRsaKeyPairExt, AwsLcRsEcdsaKeyPairExt, AwsLcRsEd25519KeyPairExt) are gated on #[cfg(all(feature = "native", feature = "xxx"))], this configuration would compile as a no-op with no traits available—matching the documented behavior that the crate "compiles as a no-op" when native is disabled.

Add native to the all feature to ensure complete, usable functionality:

Suggested fix
-all = ["rsa", "ecdsa", "ed25519"]
+all = ["native", "rsa", "ecdsa", "ed25519"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[features]
default = ["native"]
native = ["dep:aws-lc-rs"]
rsa = ["dep:uselesskey-rsa"]
ecdsa = ["dep:uselesskey-ecdsa"]
ed25519 = ["dep:uselesskey-ed25519"]
all = ["rsa", "ecdsa", "ed25519"]
[features]
default = ["native"]
native = ["dep:aws-lc-rs"]
rsa = ["dep:uselesskey-rsa"]
ecdsa = ["dep:uselesskey-ecdsa"]
ed25519 = ["dep:uselesskey-ed25519"]
all = ["native", "rsa", "ecdsa", "ed25519"]
🤖 Prompt for AI Agents
In `@crates/uselesskey-aws-lc-rs/Cargo.toml` around lines 22 - 28, The "all"
feature currently enables rsa, ecdsa, and ed25519 but not native, so consumers
using default-features = false with features = ["all"] won't enable aws-lc-rs
and the conversion traits (AwsLcRsRsaKeyPairExt, AwsLcRsEcdsaKeyPairExt,
AwsLcRsEd25519KeyPairExt) remain unavailable; update the Cargo.toml [features]
section by adding "native" to the "all" feature so it becomes all = ["native",
"rsa", "ecdsa", "ed25519"] to ensure aws-lc-rs and the gated conversion traits
are enabled when users opt into "all".

Comment on lines +30 to +34
//! let ring_kp = keypair.rsa_key_pair_aws_lc_rs();
//!
//! let rng = aws_lc_rs::rand::SystemRandom::new();
//! let msg = b"hello world";
//! let mut sig = vec![0u8; ring_kp.public_modulus_len()];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Doc example uses ring_kp variable name — should be aws_kp or similar.

The example code appears copied from the ring adapter. The variable name ring_kp is misleading in the aws-lc-rs context.

Suggested fix
-//! let ring_kp = keypair.rsa_key_pair_aws_lc_rs();
+//! let kp = keypair.rsa_key_pair_aws_lc_rs();
 //!
 //! let rng = aws_lc_rs::rand::SystemRandom::new();
 //! let msg = b"hello world";
-//! let mut sig = vec![0u8; ring_kp.public_modulus_len()];
-//! ring_kp.sign(&signature::RSA_PKCS1_SHA256, &rng, msg, &mut sig).unwrap();
+//! let mut sig = vec![0u8; kp.public_modulus_len()];
+//! kp.sign(&signature::RSA_PKCS1_SHA256, &rng, msg, &mut sig).unwrap();
🤖 Prompt for AI Agents
In `@crates/uselesskey-aws-lc-rs/src/lib.rs` around lines 30 - 34, The doc example
uses the misleading variable name `ring_kp`; update the example to use a name
that reflects this crate (e.g., `aws_kp`) wherever `ring_kp` appears in the
rsa_key_pair_aws_lc_rs example block, and adjust any subsequent references like
`ring_kp.public_modulus_len()` to `aws_kp.public_modulus_len()` so the example
matches the aws-lc-rs adapter naming.

Comment thread xtask/src/plan.rs
Comment on lines +38 to +43
if path.starts_with("crates/uselesskey-bdd/")
|| path.starts_with("features/")
|| path.ends_with(".feature") && path.contains("features/")
{
bdd_feature_changed = true;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Operator precedence is correct but benefits from explicit parentheses.

The || / && precedence in this condition works as intended (the && binds tighter), but adding parentheses would make the intent explicit and prevent future misreads.

Suggested clarification
         if path.starts_with("crates/uselesskey-bdd/")
             || path.starts_with("features/")
-            || path.ends_with(".feature") && path.contains("features/")
+            || (path.ends_with(".feature") && path.contains("features/"))
🤖 Prompt for AI Agents
In `@xtask/src/plan.rs` around lines 38 - 43, The compound conditional that sets
bdd_feature_changed is correct but unclear; update the if in plan.rs (the block
that references variable bdd_feature_changed and uses path) to add explicit
parentheses around the last conjunct — e.g., wrap (path.ends_with(".feature") &&
path.contains("features/")) — and optionally group the OR terms with parentheses
so the intent is obvious when scanning the condition.

Comment thread xtask/src/plan.rs
Comment on lines +105 to +114
fn is_rust_code_change(path: &str) -> bool {
if !path.ends_with(".rs") {
return false;
}

path.starts_with("crates/") && (path.contains("/src/") || path.contains("/tests/"))
|| path.starts_with("xtask/")
|| path.starts_with("fuzz/")
|| path.starts_with("examples/")
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Same precedence readability concern in is_rust_code_change.

The multi-line expression relies on && binding tighter than ||. Adding parentheses makes it unambiguous at a glance:

Suggested clarification
 fn is_rust_code_change(path: &str) -> bool {
     if !path.ends_with(".rs") {
         return false;
     }
 
-    path.starts_with("crates/") && (path.contains("/src/") || path.contains("/tests/"))
+    (path.starts_with("crates/") && (path.contains("/src/") || path.contains("/tests/")))
         || path.starts_with("xtask/")
         || path.starts_with("fuzz/")
         || path.starts_with("examples/")
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fn is_rust_code_change(path: &str) -> bool {
if !path.ends_with(".rs") {
return false;
}
path.starts_with("crates/") && (path.contains("/src/") || path.contains("/tests/"))
|| path.starts_with("xtask/")
|| path.starts_with("fuzz/")
|| path.starts_with("examples/")
}
fn is_rust_code_change(path: &str) -> bool {
if !path.ends_with(".rs") {
return false;
}
(path.starts_with("crates/") && (path.contains("/src/") || path.contains("/tests/")))
|| path.starts_with("xtask/")
|| path.starts_with("fuzz/")
|| path.starts_with("examples/")
}
🤖 Prompt for AI Agents
In `@xtask/src/plan.rs` around lines 105 - 114, The boolean expression in
is_rust_code_change relies on && binding tighter than || which hurts
readability; parenthesize the crates branch to make precedence explicit: wrap
(path.starts_with("crates/") && (path.contains("/src/") ||
path.contains("/tests/"))) so it's obvious that the crates check and its
src/tests conditions form one unit, leaving the other starts_with checks as
separate alternatives.

Comment thread xtask/src/plan.rs
Comment on lines +148 to +206
fn dependents(crate_name: &str) -> &'static [&'static str] {
match crate_name {
"uselesskey-core" => &[
"uselesskey-rsa",
"uselesskey-ecdsa",
"uselesskey-ed25519",
"uselesskey-hmac",
"uselesskey-x509",
"uselesskey",
"uselesskey-bdd",
],
"uselesskey-rsa" => &[
"uselesskey-x509",
"uselesskey",
"uselesskey-jsonwebtoken",
"uselesskey-rustls",
"uselesskey-ring",
"uselesskey-rustcrypto",
"uselesskey-aws-lc-rs",
],
"uselesskey-ecdsa" => &[
"uselesskey",
"uselesskey-jsonwebtoken",
"uselesskey-rustls",
"uselesskey-ring",
"uselesskey-rustcrypto",
"uselesskey-aws-lc-rs",
],
"uselesskey-ed25519" => &[
"uselesskey",
"uselesskey-jsonwebtoken",
"uselesskey-rustls",
"uselesskey-ring",
"uselesskey-rustcrypto",
"uselesskey-aws-lc-rs",
],
"uselesskey-x509" => &["uselesskey", "uselesskey-rustls"],
"uselesskey-jwk" => &[
"uselesskey-rsa",
"uselesskey-ecdsa",
"uselesskey-ed25519",
"uselesskey-hmac",
"uselesskey",
],
"uselesskey-hmac" => &[
"uselesskey",
"uselesskey-jsonwebtoken",
"uselesskey-rustcrypto",
],
"uselesskey" => &[],
"uselesskey-jsonwebtoken" => &[],
"uselesskey-rustls" => &[],
"uselesskey-ring" => &[],
"uselesskey-rustcrypto" => &[],
"uselesskey-aws-lc-rs" => &[],
"uselesskey-bdd" => &["uselesskey-bdd"],
_ => &[],
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's find the structure of the crates directory
fd -t d 'uselesskey-jwk' --full-path 'crates'

Repository: EffortlessMetrics/uselesskey

Length of output: 121


🏁 Script executed:

# Check the Cargo.toml file for uselesskey-jwk
find . -path '*/crates/uselesskey-jwk/Cargo.toml' -exec cat {} \;

Repository: EffortlessMetrics/uselesskey

Length of output: 493


🏁 Script executed:

# Also check uselesskey-core's Cargo.toml for reference
find . -path '*/crates/uselesskey-core/Cargo.toml' -exec cat {} \;

Repository: EffortlessMetrics/uselesskey

Length of output: 727


🏁 Script executed:

# Let's look at the plan.rs file to understand the context better
sed -n '1,50p' xtask/src/plan.rs

Repository: EffortlessMetrics/uselesskey

Length of output: 1490


🏁 Script executed:

# Search for the expansion algorithm or where dependents is used
rg -n 'expand|dependents' xtask/src/plan.rs | head -20

Repository: EffortlessMetrics/uselesskey

Length of output: 584


🏁 Script executed:

# Look at the expand_impacted_crates function
sed -n '131,147p' xtask/src/plan.rs

Repository: EffortlessMetrics/uselesskey

Length of output: 560


🏁 Script executed:

# Check the tests for dependents expansion
sed -n '231,310p' xtask/src/plan.rs

Repository: EffortlessMetrics/uselesskey

Length of output: 2955


🏁 Script executed:

# Search all Cargo.toml files to see if jwk is mentioned as a dependency anywhere
rg -l 'uselesskey-jwk' --type toml

Repository: EffortlessMetrics/uselesskey

Length of output: 294


🏁 Script executed:

# Check if any crate depends on uselesskey-jwk
rg 'uselesskey-jwk' crates/*/Cargo.toml

Repository: EffortlessMetrics/uselesskey

Length of output: 1136


uselesskey-bdd self-referential dependent entry is unnecessary.

Line 203: "uselesskey-bdd" => &["uselesskey-bdd"] has no practical effect. The expansion algorithm inserts a crate into the impacted set and only processes its dependents if the insertion succeeds (meaning it wasn't already present). Since uselesskey-bdd is already in the impacted set from the initial change, the self-reference here is never queued for processing. Use &[] instead to avoid confusion.

Note: uselesskey-jwk does not depend on uselesskey-core (it only depends on serde and serde_json). The opposite is true—key algorithm crates have optional dependencies on uselesskey-jwk for JWK serialization support. The current dependents structure is correct.

🤖 Prompt for AI Agents
In `@xtask/src/plan.rs` around lines 148 - 206, In the dependents function the
mapping for "uselesskey-bdd" is self-referential ("uselesskey-bdd" =>
&["uselesskey-bdd"]) which is redundant and misleading; update the dependents
function to return an empty slice for the "uselesskey-bdd" key (i.e., replace
the self-reference with &[]) so the dependents table accurately reflects no
dependents for uselesskey-bdd while leaving other entries (e.g.,
"uselesskey-jwk") unchanged.

Comment thread xtask/src/receipt.rs
Comment on lines +9 to +31
#[derive(Debug, Serialize)]
pub struct Receipt {
pub timestamp: u64,
pub steps: Vec<StepReceipt>,
pub feature_matrix: Vec<FeatureMatrixEntry>,
pub bdd_counts: BTreeMap<String, usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub coverage_lcov_path: Option<String>,
}

#[derive(Debug, Serialize)]
pub struct StepReceipt {
pub name: String,
pub status: String,
pub duration_ms: u64,
pub details: Option<String>,
}

#[derive(Debug, Serialize)]
pub struct FeatureMatrixEntry {
pub features: String,
pub status: 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.

🧹 Nitpick | 🔵 Trivial

Consider using an enum for status instead of a raw String.

StepReceipt::status and FeatureMatrixEntry::status use String for status values ("ok", "failed", "skipped"). An enum with #[serde(rename_all = "lowercase")] would provide compile-time safety and make the summary() match exhaustive. This is a low-priority suggestion given this is internal tooling.

🤖 Prompt for AI Agents
In `@xtask/src/receipt.rs` around lines 9 - 31, StepReceipt::status and
FeatureMatrixEntry::status are plain Strings; replace them with a serde-friendly
enum (e.g., enum Status { Ok, Failed, Skipped } with #[derive(Serialize,
Deserialize)] and #[serde(rename_all = "lowercase")]) to enforce allowed values;
update the structs Receipt, StepReceipt, and FeatureMatrixEntry to use this
Status enum, adjust any construction sites and serialization usage, and update
the summary() logic to match on Status variants instead of string comparisons so
exhaustiveness is enforced by the compiler.

Comment thread xtask/src/receipt.rs
Comment on lines +63 to +96
eprintln!("==> {name}");
let start = Instant::now();
match f() {
Ok(()) => {
let secs = start.elapsed().as_secs_f64();
eprintln!("==> {name} [ok, {secs:.1}s]");
self.receipt.steps.push(StepReceipt {
name: name.to_string(),
status: "ok".to_string(),
duration_ms: start.elapsed().as_millis() as u64,
details,
});
Ok(())
}
Err(err) => {
let secs = start.elapsed().as_secs_f64();
eprintln!("==> {name} [FAILED, {secs:.1}s]");
eprintln!(" {err}");
let mut detail = details.unwrap_or_default();
if !detail.is_empty() {
detail.push_str("; ");
}
detail.push_str(&err.to_string());

self.receipt.steps.push(StepReceipt {
name: name.to_string(),
status: "failed".to_string(),
duration_ms: start.elapsed().as_millis() as u64,
details: Some(detail),
});
Err(err)
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

start.elapsed() called twice per step — minor timing inconsistency.

In both the Ok and Err branches, start.elapsed() is called once for the log message and again for duration_ms. The second call will yield a slightly later measurement. Capture the elapsed duration once.

♻️ Proposed fix (Ok branch shown; apply same pattern to Err)
         match f() {
             Ok(()) => {
-                let secs = start.elapsed().as_secs_f64();
+                let elapsed = start.elapsed();
+                let secs = elapsed.as_secs_f64();
                 eprintln!("==> {name} [ok, {secs:.1}s]");
                 self.receipt.steps.push(StepReceipt {
                     name: name.to_string(),
                     status: "ok".to_string(),
-                    duration_ms: start.elapsed().as_millis() as u64,
+                    duration_ms: elapsed.as_millis() as u64,
                     details,
                 });
🤖 Prompt for AI Agents
In `@xtask/src/receipt.rs` around lines 63 - 96, The timing uses start.elapsed()
twice causing inconsistent durations; capture the elapsed Duration once at the
start of each match arm and reuse it for both the printed seconds and the
StepReceipt duration_ms. In both the Ok and Err branches of the function (the
block that prints "==> {name}" and pushes StepReceipt onto self.receipt.steps),
call let elapsed = start.elapsed(); then compute secs from elapsed.as_secs_f64()
and duration_ms from elapsed.as_millis() as u64, replacing the duplicate
start.elapsed() calls so the log and StepReceipt use the same measured duration.

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.

2 participants