M19.2: hashed-emit + revoke writers (FR-84 write, FR-86)#23
Merged
Conversation
Adds the write-side counterparts to M19.1's read-side `HashedHost`,
plus the `@revoked` prepend helper FR-86 needs. Also promotes two
previously-private helpers to `pub` so the upcoming `gitway hosts`
verb family (M19.4) can drive them without re-export shims.
src/hostkey.rs:
- `pub fn append_known_host(path, host, fp)` — promoted from
`pub(crate)`. Plaintext append; creates parent dir; no locking
(M19 explicit non-goal).
- `pub fn append_known_host_hashed(path, host, fp)` (new) — emits
`|1|<base64-salt>|<base64-hmac-sha1> <fp>` with a fresh 20-byte
OS-RNG salt per call. Round-trippable through
`cert_authority::parse_known_hosts` + `HashedHost::matches(host)`,
pinned by `tests/test_hostkey_writes.rs::append_known_host_hashed_writes_round_trippable_entry`.
- `pub fn prepend_revoked(path, host_pattern, fp)` (new) — atomic
prepend via tempfile + `std::fs::rename`. 1 MiB cap on input
file (refused with a clear `tips-thinking`-style message
pointing at `--known-hosts` for splitting). Trust-merger
treats `@revoked` as a hard reject regardless of position; the
prepend is purely a readability convention.
- `pub fn all_embedded()` (new) — returns `(host, fingerprint, alg)`
triples for the `gitway hosts list` embedded section. 3 hosts
× 3 algorithms = 9 entries, alg in {"ed25519", "ecdsa", "rsa"}.
- `pub enum HashMode { Empty, Plaintext, Hashed }` and
`pub fn detect_hash_mode(path) -> Result<HashMode, AnvilError>`
(new) — inspect-existing-file decision for `gitway hosts add`'s
default hashed/plaintext choice. Short-circuits on the first
hashed token seen.
- `pub fn default_known_hosts_path()` — promoted from private.
`~/.config/gitway/known_hosts` via `dirs::config_dir()`.
- New private `ensure_parent_exists` helper extracted from the
existing `mkdir -p` block so all four writers share one error
message shape.
Cargo.toml:
- Adds `rand_core = { version = "0.6", features = ["std", "getrandom"] }`
is already a runtime dep; M19.2 just uses it directly via
`OsRng.fill_bytes`.
tests/test_hostkey_writes.rs (new, ~13 tests):
- Plaintext append: file creation, append-to-existing.
- Hashed append: round-trip parse + match, distinct-salt-per-call
(privacy property of `HashKnownHosts yes`).
- Revoke prepend: file creation, prepend-position assertion,
oversize refusal.
- detect_hash_mode: missing file, comments-only, plaintext-only,
short-circuit on first hashed token.
- all_embedded: count + algorithm-tag completeness.
- default_known_hosts_path: ends-with assertion.
Stacks on M19.1 (#22, merged).
Plan: M19.2 of anvil-gitway-milestone-plan.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tasks
UnbreakableMJ
added a commit
that referenced
this pull request
May 4, 2026
Final Anvil-side slice of M19. Bumps anvil-ssh from 0.6.0 to 0.7.0 to publish the M19.1 + M19.2 work as a single crates.io release. The Gitway-side `gitway hosts` verb family (M19.4 + M19.5) lands against this 0.7.0; the M19.X PRD doc PR closes the milestone with a Gitway v1.0.0-rc.7 tag. Cargo.toml: - version "0.6.0" -> "0.7.0" Cargo.lock: - regenerated locally; reflects the 0.7.0 version. CHANGELOG.md: - 0.7.0 entry covering the new HashedHost type, the append_known_host_hashed / prepend_revoked / all_embedded / HashMode + detect_hash_mode public surface, the default_known_hosts_path + append_known_host visibility promotions, and the new direct deps (hmac/sha1/base64). - HMAC-SHA1 privacy-vs-security caveat documented inline. Stacked after PRs #22 (M19.1, merged) and #23 (M19.2, merged). Plan: M19.3 of anvil-gitway-milestone-plan.md. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Second slice of M19 (PRD §5.8.8). Adds the write-side counterparts to M19.1's read-side
HashedHost, plus the@revokedprepend helper FR-86 needs. Also promotes two previously-private helpers topubso the upcominggitway hostsverbs (M19.4) drive them without re-export shims.New public surface (
hostkey)pub fn append_known_host(path, host, fp)— promoted frompub(crate). Plaintext append.pub fn append_known_host_hashed(path, host, fp)— emits|1|<base64-salt>|<base64-hmac-sha1> <fp>with a fresh 20-byte OS-RNG salt per call. Round-trippable throughparse_known_hosts+HashedHost::matches(host).pub fn prepend_revoked(path, host_pattern, fp)— atomic prepend via tempfile +std::fs::rename. 1 MiB input cap; refused with a clear hint pointing at--known-hostsfor splitting.pub fn all_embedded() -> Vec<(String, String, &'static str)>— embedded fingerprint catalogue tagged by algorithm (ed25519/ecdsa/rsa) forgitway hosts list.pub enum HashMode { Empty, Plaintext, Hashed }+pub fn detect_hash_mode(path)— inspect-existing-file decision for thegitway hosts adddefault.pub fn default_known_hosts_path()— promoted from private.Tests
tests/test_hostkey_writes.rs(new, 13 tests): plaintext + hashed append round-trips, distinct-salt-per-call (privacy ofHashKnownHosts yes), revoke prepend position + oversize refusal, hash-mode detection across empty / comments-only / plaintext / hashed files, embedded-catalogue count + algorithm completeness, default-path platform check.Stacked on #22 (M19.1, merged). Rebased onto fresh
main.Test plan
cargo fmt --all -- --checkcargo clippy --all-targets --all-features --locked -- -D warningscargo test --lib --tests --locked— all green (13 new tests pass)🤖 Generated with Claude Code