Skip to content

Ticket 0007a: Anchorer core API in cog-anchor - #317

Merged
noctem-o merged 2 commits into
mainfrom
seam/0007a-anchor-core
Jul 4, 2026
Merged

noctem-o merged 2 commits into
mainfrom
seam/0007a-anchor-core

Conversation

@noctem-o

@noctem-o noctem-o commented Jul 4, 2026

Copy link
Copy Markdown
Owner

What

Ticket 0007a — the anchoring core inside cog-anchor (split A of parent ticket 0007). Adds the real API the emitter will use:

  • Anchorer / AnchorRequest / AnchorError and generate_key_file.
  • Anchorer::open is now the sole LogWriter construction site in the deadbolt estate (CLAUDE.md law 3).
  • Dev-grade key custody: a 64-lowercase-hex Ed25519 seed file, created create_new (refuses to overwrite), never auto-generated.
  • Deps added: serde + serde_json only. OS randomness via BCryptGenRandom (Windows) / /dev/urandom (Unix) FFI — no new cargo deps.
  • tests/smoke.rs refactored to route through Anchorer so LogWriter::open has exactly one call site.

Nothing outside cog-anchor imports it yet; emission into cog-cli is 0007b (parked until this merges — it imports this API).

Review (independently verified, not from the worker's summary)

Check Result
cargo test -p cog-anchor ✅ 7/7 green (5 anchorer.rs + 2 smoke.rs)
cargo clippy -p cog-anchor --all-targets -- -D warnings ✅ clean
cargo tree -i magpie-log ✅ exactly one dependent (cog-anchor)
LogWriter::open grep ✅ single site, in Anchorer::open
cog-anchor imported outside crate ✅ none
Refuse-overwrite test asserts error and unchanged bytes ✅
Non-idempotence documented (2× → len 3) ✅
Hostile: malformed root → AnchorError::Log, len unchanged ✅
Hostile: wrong key on existing log → open fails ✅

Reviewer notes (non-blocking)

  1. smoke.rs modified — outside 0007a's enumerated file list but within crates/cog-anchor/** and necessary: it was a second LogWriter::open site, so routing it through Anchorer is what makes the "sole construction site" criterion true.
  2. RNG via unsafe FFI — chosen to honor "serde-only, no new deps." Small and correct-looking; flagging the platform unsafe as a design trade vs. an explicit getrandom dep. Minor nit: decode_seed_hex strips \n but not \r\n (irrelevant to the newline-free file it generates).

Provenance

Implemented by Codex (gpt-5.5) via the delegation wrapper; ticket authored and diff reviewed by Claude. Human is the merge authority.

🤖 Generated with Claude Code

Add the anchoring API — Anchorer, AnchorRequest, AnchorError, and
generate_key_file — inside cog-anchor. Anchorer::open becomes the sole
LogWriter construction site in the deadbolt estate (CLAUDE.md law 3).

Dev-grade key custody: a 64-lowercase-hex Ed25519 seed file, created
create-new (refuses overwrite) and never auto-generated. smoke.rs is
refactored to route through Anchorer so LogWriter::open has exactly one
call site. Deps: serde/serde_json only (OS RNG via bcrypt/urandom FFI,
no new cargo deps).

Split A of parent ticket 0007; 0007b wires emission into cog-cli.
Implemented by Codex (gpt-5.5) via delegation; reviewed by Claude —
7 tests green, clippy -D warnings clean, cargo tree -i magpie-log shows
one dependent (cog-anchor).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

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

ℹ️ 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 on lines +117 to +120
let mut file = OpenOptions::new()
.write(true)
.create_new(true)
.open(key_path)

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 Restrict generated seed file permissions

On Unix hosts with a permissive umask such as 022, this OpenOptions create path uses the platform default create mode, so the generated Ed25519 seed file can end up group/world-readable (for example 0644). Because that seed is sufficient to append anchors as deadbolt, the generator should create it with restrictive permissions such as 0600 on Unix instead of relying on the caller's umask.

Useful? React with 👍 / 👎.

Architect ruling on the 0007a review: replace the hand-rolled OS RNG FFI
(BCryptGenRandom / /dev/urandom) with getrandom, the audited ecosystem
answer for entropy. Weak-entropy bugs are silent and permanent
(Debian-OpenSSL-shaped), and this is the crate's most security-critical op.

- Add getrandom 0.4 (dedupes with the version already in the tree).
- generate_key_file fills the seed via getrandom::fill; all cfg(windows)/
  cfg(unix) FFI deleted. Public signature, refuse-to-overwrite, and hex
  output format unchanged.
- Add #![forbid(unsafe_code)]: the identity-creation crate now carries a
  compile-time no-unsafe guarantee (compiles clean — the FFI was the only
  unsafe in the crate).
- decode_seed_hex: trim_end() so \r\n is handled, not just \n.

All five 0007a tests still green, unmodified. clippy -D warnings clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@noctem-o

noctem-o commented Jul 4, 2026

Copy link
Copy Markdown
Owner Author

Revise pass applied (architect ruling) — re-recommend ACCEPT

Force of the ruling: the "no new deps" constraint was meant to stop scope creep, not to force hand-rolling the crate's most security-critical operation. Hand-rolled entropy fails silent-and-permanent (Debian-OpenSSL-shaped); getrandom is the audited answer.

Four changes (commit 8d84409):

  • getrandom = "0.4" added — dedupes with the 0.4.2 already in the tree, no second major version.
  • generate_key_file now fills the seed via getrandom::fill; all cfg(windows)/cfg(unix) FFI deleted. Public signature, refuse-to-overwrite, and hex output format unchanged.
  • #![forbid(unsafe_code)] added to the crate root — compiles clean, confirming the FFI was the crate's only unsafe. The invariant the FFI was trading away is now compiler-enforced.
  • decode_seed_hex uses trim_end() so \r\n is handled, not just \n.

The smoke.rs refactor from the first pass is retained (endorsed — it's what makes the sole-LogWriter::open-site criterion true rather than merely asserted).

Validation (my runs, in the PR worktree):

Check Result
cargo test -p cog-anchor ✅ 7/7 green, unmodified
#![forbid(unsafe_code)] compiles ✅ clean (no other unsafe in crate)
cargo clippy -p cog-anchor --all-targets -- -D warnings ✅ clean
cargo fmt --all --check ✅
cargo tree -i magpie-log ✅ one dependent
Diff scope ✅ cog-anchor/** + Cargo.lock only

Behaviour is identical to the first pass — only the entropy source and the crate's unsafe-ness changed. Recommend merge.

@noctem-o
noctem-o merged commit f8bbe85 into main Jul 4, 2026
1 check passed
@noctem-o
noctem-o deleted the seam/0007a-anchor-core branch July 4, 2026 19:58
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