Skip to content

mirror_worker: cosigner key, /metadata, and add-checkpoint#261

Open
lukevalenta wants to merge 2 commits into
mainfrom
lvalenta/mirror-worker-foundation
Open

mirror_worker: cosigner key, /metadata, and add-checkpoint#261
lukevalenta wants to merge 2 commits into
mainfrom
lvalenta/mirror-worker-foundation

Conversation

@lukevalenta

@lukevalenta lukevalenta commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Build on the crate skeleton (#260) with the mirror's key handling and the pending-checkpoint submission path:

  • Parse trusted per-origin log keys (ML-DSA-44 / subtree/v1) from config and verify submitted checkpoints against them.
  • Load the mirror's own ML-DSA-44 cosigner key and publish its SPKI, algorithm, and per-log config at GET /metadata.
  • Handle POST /add-checkpoint via a per-origin MirrorState Durable Object, whose single-threaded fetch handler makes the check-verify-persist update of the pending checkpoint atomic.

The MirrorState DO also defines committed and next-entry state and RPCs beyond what add-checkpoint uses; these are covered by the DO's format/round-trip unit tests and will be used by the add-entries endpoint.

@lukevalenta lukevalenta self-assigned this Jul 20, 2026
@lukevalenta
lukevalenta force-pushed the lvalenta/mirror-worker-foundation branch 4 times, most recently from c2b6fdd to e68a4a6 Compare July 20, 2026 15:40
@lukevalenta

Copy link
Copy Markdown
Contributor Author

/bonk review

@ask-bonk

ask-bonk Bot commented Jul 20, 2026

Copy link
Copy Markdown

PR #261 – Mirror Worker: cosigner key, /metadata, and add‑checkpoint

Summary

The changes introduce full support for the mirror’s cosigner key, a /metadata endpoint, and atomic checkpoint updates via a per‑origin MirrorState Durable Object. The implementation is clean, follows the spec, and includes comprehensive tests for JSON formats, state handling, and key loading.

Findings (ordered by severity)

  1. Correctness & Spec Compliance

    • The atomic read‑verify‑write flow in MirrorState::fetch_inner for /update-pending correctly uses the DO’s input/output gates, preventing race conditions.
    • Consistency‑proof verification correctly handles the three cases (same size, first pending, normal proof) and returns appropriate HTTP codes (409, 422, 400).
    • /metadata builds a deterministic, sorted log list, exposing the required fields (mirror_public_key, mirror_algorithm, prefixes, per‑log config).
    • Cosigner key loading (load_mirror_signer / build_mirror_signer) enforces the ML‑DSA‑44 algorithm and provides clear error messages for unsupported OIDs or malformed PEMs.
  2. Testing Coverage

    • Unit tests pin JSON wire formats for all persisted structs (PendingCheckpoint, CommittedCheckpoint, NextEntry, snapshot, RPC bodies). This protects against accidental migration‑breaking changes.
    • Tests verify the signer loader rejects non‑ML‑DSA‑44 keys and malformed PEMs, and that the dev config matches the embedded SPKI.
    • Edge‑case tests for empty proof serialization and default zero states are present.
  3. Code Quality

    • Use of LazyLock for config and log‑key caches ensures one‑time initialization with no runtime overhead.
    • OnceLock for the mirror signer avoids repeated PKCS#8 parsing.
    • Clear separation between frontend handler (frontend_worker.rs) and DO logic (mirror_state_do.rs).
    • Comprehensive inline documentation explains the state model and RPC semantics.
  4. Minor Recommendations

    • Unused import suppression: The line use ml_dsa::pkcs8::{DecodePrivateKey as _, EncodePublicKey as _}; imports DecodePrivateKey only for the underscore alias, which is fine but could be simplified to just EncodePublicKey if the decode path isn’t needed elsewhere. This is purely stylistic and does not affect functionality.
    • Error message consistency: In dispatch_update_pending, the generic Bad request prefix is used for 400 responses from the DO. Consider mirroring the exact error text from the DO for easier debugging (e.g., forward the DO’s body verbatim). This is optional.
    • Header handling in tlog_size_conflict: The Headers object is built and mutated before being attached to the response; this works, but using Response::from_body(...).with_status(409).with_headers(headers) could be expressed as a single chained call for readability.

Overall Assessment

The implementation fulfills the feature requirements, adheres to the C2SP mirror specification, and includes robust testing and documentation. No functional regressions or lint failures are evident. Minor stylistic tweaks are optional.

Recommendation: Approve and merge.

github run

@lukevalenta
lukevalenta force-pushed the lvalenta/mirror-worker-foundation branch from e68a4a6 to 691037e Compare July 20, 2026 16:34
@lukevalenta

Copy link
Copy Markdown
Contributor Author
  • Unused import suppression: The line use ml_dsa::pkcs8::{DecodePrivateKey as _, EncodePublicKey as _}; imports DecodePrivateKey only for the underscore alias, which is fine but could be simplified to just EncodePublicKey if the decode path isn’t needed elsewhere. This is purely stylistic and does not affect functionality.

DecodePrivateKey is needed for from_pkcs8_pem

  • Error message consistency: In dispatch_update_pending, the generic Bad request prefix is used for 400 responses from the DO. Consider mirroring the exact error text from the DO for easier debugging (e.g., forward the DO’s body verbatim). This is optional.

Addressed.

  • Header handling in tlog_size_conflict: The Headers object is built and mutated before being attached to the response; this works, but using Response::from_body(...).with_status(409).with_headers(headers) could be expressed as a single chained call for readability.

Already in the single-chained form.

@lukevalenta
lukevalenta marked this pull request as ready for review July 20, 2026 16:36
Comment thread crates/mirror_worker/src/frontend_worker.rs Outdated
@lukevalenta
lukevalenta force-pushed the lvalenta/mirror-worker-skeleton branch from 5ef8208 to 8b0f5db Compare July 20, 2026 16:50
Comment thread crates/mirror_worker/src/frontend_worker.rs Outdated
@lukevalenta
lukevalenta force-pushed the lvalenta/mirror-worker-skeleton branch from 8b0f5db to 299d2a1 Compare July 20, 2026 17:11
@lukevalenta
lukevalenta force-pushed the lvalenta/mirror-worker-foundation branch 2 times, most recently from ab6cc24 to ac6a7c0 Compare July 20, 2026 17:18
@lukevalenta

Copy link
Copy Markdown
Contributor Author

/bonk review again

@ask-bonk

ask-bonk Bot commented Jul 20, 2026

Copy link
Copy Markdown

PR #261 – Mirror Worker: cosigner key, /metadata, and add‑checkpoint

Summary

The changes introduce full support for the mirror’s cosigner key, a /metadata endpoint, and atomic checkpoint handling via a per‑origin MirrorState Durable Object. The implementation aligns with the C2SP mirror specification, includes extensive unit tests, and maintains clean separation between frontend routing and DO logic.

Findings

  1. Correctness & Spec Compliance

    • Atomic read‑verify‑write flow in MirrorState::fetch_inner correctly prevents race conditions.
    • Consistency‑proof verification handles all cases (same size, first pending, normal proof) with appropriate HTTP status codes (409, 422, 400).
    • /metadata returns a deterministic, sorted log list with required fields (mirror_public_key, mirror_algorithm, prefixes, per‑log config).
    • Cosigner key loading enforces ML‑DSA‑44, provides clear error messages for unsupported OIDs or malformed PEMs.
  2. Testing

    • Comprehensive tests cover JSON wire formats for persisted structs, signer loading edge cases, and empty‑proof serialization.
    • Tests verify that non‑ML‑DSA‑44 keys are rejected and that the dev config matches the embedded SPKI.
  3. Code Quality

    • Use of LazyLock and OnceLock for config and signer caching is appropriate.
    • Clear separation of concerns: frontend_worker.rs handles HTTP routing; mirror_state_do.rs encapsulates DO state and RPC handling.
    • Inline documentation explains the state model and RPC semantics.
  4. Minor Points

    • The import use ml_dsa::pkcs8::{DecodePrivateKey as _, EncodePublicKey as _}; is needed for from_pkcs8_pem; keep it as‑is.
    • The error handling in dispatch_update_pending now forwards the DO’s exact error body, which improves debugging.
    • Header construction in tlog_size_conflict already uses a single‑chained call, so no change required.

Recommendation

The implementation satisfies the feature requirements, passes all unit tests, and shows no lint or build issues. I recommend approval and merge.

github run

Base automatically changed from lvalenta/mirror-worker-skeleton to main July 21, 2026 15:40
Build on the crate skeleton with the mirror's key handling and the
pending-checkpoint submission path:

- Parse trusted per-origin log keys (ML-DSA-44 / subtree/v1) from config
  and verify submitted checkpoints against them.
- Load the mirror's own ML-DSA-44 cosigner key and publish its SPKI,
  algorithm, and per-log config at GET /metadata.
- Handle POST /add-checkpoint via a per-origin MirrorState Durable
  Object, whose single-threaded fetch handler makes the
  check-verify-persist update of the pending checkpoint atomic.

The MirrorState DO also defines committed and next-entry state and RPCs
beyond what add-checkpoint uses; these are covered by the DO's
format/round-trip unit tests and will be used by the add-entries
endpoint.
@lukevalenta
lukevalenta force-pushed the lvalenta/mirror-worker-foundation branch from ac6a7c0 to 1686137 Compare July 21, 2026 16:12
Fix some additional nits from an offline AI review.

* Bound /commit by the persisted-entry frontier (next_entry) rather than
  pending, enforcing committed <= next_entry.
* Propagate DO storage read errors instead of defaulting to the zero
  state, which could let an old_size == 0 request roll back a real
  checkpoint (same fix applied to witness_worker).
* Build the mirror signer from the already-decoded DER rather than
  re-parsing the PEM.
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.

3 participants