Skip to content

Extract checkpoint signed-note format into a new tlog_checkpoint crate#233

Draft
lukevalenta wants to merge 1 commit intolvalenta/tlog-crate-splitfrom
lvalenta/tlog-checkpoint-crate
Draft

Extract checkpoint signed-note format into a new tlog_checkpoint crate#233
lukevalenta wants to merge 1 commit intolvalenta/tlog-crate-splitfrom
lvalenta/tlog-checkpoint-crate

Conversation

@lukevalenta
Copy link
Copy Markdown
Contributor

@lukevalenta lukevalenta commented May 1, 2026

Second slice of #230. Move the c2sp.org/tlog-checkpoint signed-note format out of tlog_tiles into its own tlog_checkpoint crate, so each crate now matches the C2SP spec slug it implements.

Stacked on PR #231 (slice 1: tlog_core extraction). Diff against #231 is the new content here; once #231 lands this PR's base will be retargeted to main.

What moves

Symbol Old home New home
CheckpointText, MalformedCheckpointTextError tlog_tiles::checkpoint tlog_checkpoint
CheckpointSigner trait, Ed25519CheckpointSigner tlog_tiles::checkpoint tlog_checkpoint
TreeWithTimestamp tlog_tiles::checkpoint tlog_checkpoint
open_checkpoint tlog_tiles::checkpoint tlog_checkpoint
CheckpointError (introduced in slice 1) tlog_tiles::checkpoint tlog_checkpoint
UnixTimestamp tlog_tiles::entries tlog_checkpoint

What stays in tlog_tiles

tile.rs (the c2sp.org/tlog-tiles HTTP wire format itself) and entries.rs (the LogEntry/PendingLogEntry abstractions). Slice 3 of #230 will move entries.rs into generic_log_worker.

Bonus: rename UnixTimestampUnixTimestampMillis

Every callsite in the workspace uses milliseconds (constructed via now_millis(), named timestamp_unix_millis: UnixTimestamp to clarify the unit). The subtree_v1 / cosignature_v1 cosigners that produce POSIX-seconds in the wire format already do / 1000 at the spec boundary.

Putting the unit in the type name makes the contract explicit and lets parameter names drop the redundant _unix_millis suffix:

- fn sign(timestamp_unix_millis: UnixTimestamp, …) -> …
+ fn sign(timestamp: UnixTimestampMillis, …) -> …

timestamp_unix_secs local variable names are preserved at the cosigner spec boundaries since those are in seconds.

Cleanups in tlog_tiles

  • Dropped now-unused workspace deps: ed25519-dalek, rand, signed_note, thiserror.
  • Description in Cargo.toml shortened to mention only c2sp.org/tlog-tiles.
  • README updated to reflect the split.

Consumers no longer depending on tlog_tiles at all

After the migration:

Each had been transitively pulling in tile-encoding types and dependencies (length_prefixed, url, etc.) only to get at Hash or CheckpointSigner.

Stats

43 files changed, +355 / −141 (vs slice-1 head). The checkpoint.rs move is git mv with 92% similarity. The bulk of the +355 is tlog_checkpoint's Cargo.toml/LICENSE/README plus the mechanical rename across the workspace.

Pre-push checks

All four pass:

  • cargo clippy --workspace --all-targets -- -Dwarnings -Dclippy::pedantic
  • cargo test (31 test result blocks ok, 2 more than slice 1 from tlog_checkpoint's relocated unit tests)
  • cargo fmt --all --check
  • cargo machete

WASM build also clean (cargo check -p witness_worker --target wasm32-unknown-unknown).

Refs #230.

Slice 2 of #230. The c2sp.org/tlog-checkpoint signed-note format is a
sibling spec to c2sp.org/tlog-tiles, not a part of it: the tlog-tiles
spec defers checkpoint formatting to tlog-checkpoint. Until now the
implementation lived inside the tlog_tiles crate alongside the
tile-encoding wire format. This commit moves it into its own
tlog_checkpoint crate.

New crate `crates/tlog_checkpoint`:

- `CheckpointText` + `MalformedCheckpointTextError` — text parser/
  serializer for the c2sp.org/tlog-checkpoint body.
- `CheckpointSigner` trait + `Ed25519CheckpointSigner` — basic
  Ed25519 signed-note algorithm (0x01).
- `TreeWithTimestamp` — a (size, hash, timestamp) tuple ready to be
  signed.
- `open_checkpoint` — caller-side parse-and-fully-verify entry point.
- `CheckpointError` — error variants for the entry points above.
- `UnixTimestampMillis` — type alias for the timestamp embedded in a
  checkpoint cosignature (see rename below).

Dependencies: tlog_core, signed_note, ed25519-dalek, base64, sha2,
rand, thiserror.

Renamed `UnixTimestamp` to `UnixTimestampMillis`:

The previous name didn't say what unit it was, even though the
workspace uses it exclusively for milliseconds (every callsite reads
`now_millis()` and the parameter names were `timestamp_unix_millis`).
Putting the unit in the type name makes the contract explicit at the
trait boundary and lets parameter names drop the redundant suffix:

  -fn sign(timestamp_unix_millis: UnixTimestamp, ...) -> ...
  +fn sign(timestamp: UnixTimestampMillis, ...) -> ...

The cosignature_v1 / subtree_v1 cosigners that need POSIX seconds
keep their `/ 1000` conversion at the spec boundary, with the local
`timestamp_unix_secs` variable name preserved for the local domain
where the unit changes.

Cleanups in `tlog_tiles`:

- `tlog_tiles` now contains only the tile-encoding wire format (tile.rs)
  and the LogEntry/PendingLogEntry abstractions (entries.rs). Slice 3
  will move entries.rs into generic_log_worker.
- Dropped now-unused deps: ed25519-dalek, rand, signed_note, thiserror.
  url, length_prefixed, base64, sha2, serde, tlog_core, tlog_checkpoint
  remain.
- Description in Cargo.toml shortened to mention only c2sp.org/tlog-tiles.

Migration: every internal consumer that used `tlog_tiles::CheckpointSigner`,
`tlog_tiles::CheckpointText`, `tlog_tiles::open_checkpoint`,
`tlog_tiles::Ed25519CheckpointSigner`, `tlog_tiles::TreeWithTimestamp`,
`tlog_tiles::UnixTimestamp(/Millis)`, `tlog_tiles::CheckpointError`,
or `tlog_tiles::MalformedCheckpointTextError` now imports from
`tlog_checkpoint` directly. Touched: `tlog_witness`, `tlog_cosignature`,
`tlog_tiles_wasm`, `static_ct_api`, `bootstrap_mtc_api`,
`bootstrap_mtc_worker`, `ct_worker`, `generic_log_worker`,
`witness_worker`, `integration_tests`, `x509_util`, and the
`fuzz_parse_checkpoint` target.

Several consumers (`tlog_witness`, `tlog_cosignature`,
`tlog_tiles_wasm`, `witness_worker`) no longer depend on
`tlog_tiles` at all after the migration.

Slice 3 (move entries.rs into generic_log_worker) is deferred to a
follow-up.
@lukevalenta
Copy link
Copy Markdown
Contributor Author

/bonk review

Copy link
Copy Markdown

@ask-bonk ask-bonk Bot left a comment

Choose a reason for hiding this comment

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

Review

Mechanical extraction looks clean. All four pre-push checks pass locally on this branch:

  • cargo clippy --workspace --all-targets -- -Dwarnings -Dclippy::pedantic
  • cargo test (with default-members; integration_tests and fuzz excluded as documented)
  • cargo fmt --all --check
  • cargo machete

Plus cargo check -p witness_worker --target wasm32-unknown-unknown is clean.

Confirmed

  • The new tlog_checkpoint crate's Cargo.toml only pulls deps it actually uses; the corresponding deps drop in tlog_tiles are real (no stale workspace pulls).
  • tlog_tiles no longer depends on ed25519-dalek, rand, signed_note, or thiserror — its only consumers of Hash/checkpoint types now go through tlog_core and tlog_checkpoint.
  • tlog_tiles_wasm (Cargo.toml) really has no tlog_tiles dep anymore — only tlog_core + tlog_checkpoint. The crates/tlog_tiles_wasm directory name is now misleading, but the PR description acknowledges this is intentional pending the slice-3 / WASM rebinding plan in #230.
  • UnixTimestampUnixTimestampMillis rename is workspace-wide: rg '\bUnixTimestamp\b' returns no matches.
  • The seconds boundary in cosignature_v1 / subtree_v1 cosigners still does / 1000 and uses local timestamp_unix_secs names, exactly as the PR description claims (crates/tlog_cosignature/src/cosignature_v1.rs:44, subtree_v1.rs:333 area).
  • The git mv of checkpoint.rstlog_checkpoint/src/lib.rs preserves the existing unit tests (test_parse_checkpoint, test_sign_verify); the +44/-17 in that file is the new module doc-comment header (lines 14-55), the new UnixTimestampMillis doc, and the tlog_core::record_hash import path in tests.
  • CheckpointError carries forward from slice 1 with the same variants; the MalformedCheckpoint(#[from] MalformedCheckpointTextError) and Tlog(#[from] TlogError) conversions are preserved.

Nits (non-blocking)

Inconsistent parameter name in one trait impl. The PR description explicitly justifies dropping the _unix_millis suffix from CheckpointSigner::sign parameter names ("Putting the unit in the type name makes the contract explicit and lets parameter names drop the redundant _unix_millis suffix"). Every sign impl in the workspace uses timestamp (or _timestamp when unused) except crates/bootstrap_mtc_api/src/cosigner.rs:96, which still has _timestamp_unix_millis: UnixTimestampMillis. Cosmetic only — the parameter is unused and prefixed with _, so no caller sees it — but worth aligning before pushing for full consistency:

- _timestamp_unix_millis: UnixTimestampMillis,
+ _timestamp: UnixTimestampMillis,

README typo on MalformedCheckpointError. crates/tlog_checkpoint/src/lib.rs:236 references [MalformedCheckpointError] in the # Errors doc for CheckpointText::new, but the actual type is MalformedCheckpointTextError. Pre-existing in slice 1 (the rustdoc link silently no-ops since it doesn't resolve), but worth fixing while the file is being touched:

-    /// Returns a [`MalformedCheckpointError`] if the arguments do not comply with
+    /// Returns a [`MalformedCheckpointTextError`] if the arguments do not comply with

Stats sanity

PR body says "+355 / −141, 43 files changed" — git diff ae9e983..HEAD --stat confirms exactly 355 insertions / 141 deletions across 43 files. Test count claim ("31 test result blocks ok, 2 more than slice 1 from tlog_checkpoint's relocated unit tests") matches: the tlog_checkpoint crate's two tests (test_parse_checkpoint, test_sign_verify) now appear as their own running 2 tests block in the test runner output.

Boundary check

The crate split keeps Worker-specific concerns out of tlog_checkpoint: no worker / KV / Durable Object types appear, only signed_note, tlog_core, ed25519-dalek, rand, sha2, base64, thiserror. Aligns with the AGENTS.md boundary guidance for spec-level published crates.

LGTM modulo the two nits above.

@ask-bonk
Copy link
Copy Markdown

ask-bonk Bot commented May 1, 2026

Review posted to PR #233: #233

Summary of findings:

All four pre-push checks pass locally (clippy pedantic, cargo test, fmt, machete) plus the WASM build. The crate extraction is mechanically clean and matches the PR description exactly:

  • tlog_tiles correctly drops ed25519-dalek, rand, signed_note, thiserror deps
  • tlog_tiles_wasm no longer depends on tlog_tiles itself (only tlog_core + tlog_checkpoint)
  • UnixTimestampUnixTimestampMillis rename is complete workspace-wide
  • The / 1000 seconds boundary in cosignature_v1/subtree_v1 is preserved as claimed
  • Stats match exactly: +355/−141 across 43 files

Two non-blocking nits flagged:

  1. crates/bootstrap_mtc_api/src/cosigner.rs:96 still has _timestamp_unix_millis parameter name while every other CheckpointSigner::sign impl uses timestamp/_timestamp per the PR's stated naming policy. Cosmetic since the param is unused.

  2. crates/tlog_checkpoint/src/lib.rs:236 rustdoc references nonexistent MalformedCheckpointError type — should be MalformedCheckpointTextError (pre-existing from slice 1, but worth fixing while the file is being touched).

github run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant