Skip to content

M12.5+M12.6: AnvilConfig 0.3.0 — multi-IdentityFile, StrictHostKeyChecking, parse-and-warn#2

Merged
UnbreakableMJ merged 2 commits into
mainfrom
feature/m12-5-anvilconfig-api-break
May 3, 2026
Merged

M12.5+M12.6: AnvilConfig 0.3.0 — multi-IdentityFile, StrictHostKeyChecking, parse-and-warn#2
UnbreakableMJ merged 2 commits into
mainfrom
feature/m12-5-anvilconfig-api-break

Conversation

@UnbreakableMJ

@UnbreakableMJ UnbreakableMJ commented May 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Two M12 sub-milestones combined in one PR (single file diff between them — keeping them separate would have been more friction than value):

  • M12.5 — the semver-minor API break: identity_fileidentity_files: Vec<PathBuf>, skip_host_check: boolstrict_host_key_checking: StrictHostKeyChecking. Cuts anvil-ssh 0.3.0.
  • M12.6log::warn fires once per apply_ssh_config() call when the resolved config contains directives Anvil parses but does not yet honor (HostKeyAlgorithms, KexAlgorithms, Ciphers, MACs → M17; ConnectTimeout, ConnectionAttempts → M18).

This is the second of the M12 PRs. The first (M12.1-4: ssh_config parser + resolver, additive types) merged as #1. Subsequent work (gitway config show, NFR-24 diag wiring, NFR-15 bench, matrix completion) follows in M12.7-9 across this repo and Steelbore/Gitway.

M12.5 — Breaking changes (with deprecated shims)

0.2.x 0.3.0
AnvilConfig.identity_file: Option<PathBuf> AnvilConfig.identity_files: Vec<PathBuf>
AnvilConfig.skip_host_check: bool AnvilConfig.strict_host_key_checking: StrictHostKeyChecking
AnvilConfigBuilder::identity_file(p) add_identity_file(p) / identity_files(vec)
AnvilConfigBuilder::skip_host_check(b) strict_host_key_checking(StrictHostKeyChecking)

The deprecated builder methods + accessors stay as #[deprecated] shims so 0.2.x code keeps compiling (with warnings). Removal target: 1.0.

M12.5 — New additions

  • AnvilConfigBuilder::apply_ssh_config(&ResolvedSshConfig) — layers ssh_config-derived defaults into the builder (HostName/Port/User override, IdentityFile-extend, StrictHostKeyChecking override, first UserKnownHostsFile fills custom_known_hosts if unset). Call before CLI overrides if you want CLI to win.
  • StrictHostKeyChecking::AcceptNew minimal write path — when custom_known_hosts is set, the first-seen fingerprint of an unknown host is recorded; without that path set, the connect downgrades to Yes semantics with a warning. Full TOFU UX (interactive prompt + display) is post-M12.
  • add_identity_file() / identity_files() / strict_host_key_checking() builder methods.

M12.5 — auth.rs semantics

find_identity now iterates config.identity_files in order. When the explicit list is non-empty, the well-known default search (~/.ssh/id_*) is suppressed — this matches OpenSSH: an explicit IdentityFile / --identity silences the default fallback. The default search runs only when no explicit identities were configured.

M12.6 — Warn on parsed-but-not-yet-honored directives

apply_ssh_config() calls a small helper that emits one log::warn! per pending milestone when the resolved config contains directives Anvil knows about but doesn't yet plumb through:

ssh_config: directive(s) HostKeyAlgorithms, KexAlgorithms parsed but not
yet honored (landing in M17 — Gitway PRD §8); current connections use
Anvil's hardcoded algorithm preferences

The warn fires at the bridge between ssh_config and AnvilConfiggitway config show and similar inspection paths (which call resolve() directly without applying it to a builder) don't trigger it.

Test surface

  • 154 lib tests (up from 143) including 9 new config tests (default policy, multi-add, replace-list, deprecated shims for both replaced fields, all three policy variants, apply_ssh_config layering + identity_files extension + custom-known-hosts non-overwrite) and 1 new auth test (multi-identity probe).
  • 6 integration tests still green (test_connection + test_clone). Updated to the new strict_host_key_checking() builder method.
  • The 1 ignored test is the pre-existing RSA SSHSIG limitation in ssh-key 0.6.7.

Test plan

  • CI passes (Linux/macOS/Windows + MSRV 1.88).
  • cargo fmt --all -- --check clean.
  • cargo clippy --all-targets --all-features -- -D warnings clean.
  • cargo test --lib --tests --locked green on all platforms.
  • After merge: tag v0.3.0, publish to crates.io.

🤖 Generated with Claude Code

…ecking)

The semver-minor API break that brings AnvilConfig in line with
ssh_config(5) semantics. Lands as anvil-ssh 0.3.0 per Gitway PRD §5.8.1
and the M12 plan.

config.rs (breaking):
- `identity_file: Option<PathBuf>` -> `identity_files: Vec<PathBuf>`.
  OpenSSH allows multiple IdentityFile directives; the resolver and
  the auth path now honour the full list in source order.
- `skip_host_check: bool` -> `strict_host_key_checking:
  StrictHostKeyChecking`. The new enum encodes Yes / No / AcceptNew,
  matching ssh_config(5).
- New `apply_ssh_config(&ResolvedSshConfig)` builder method. Layers
  ssh_config-derived defaults (HostName/Port/User overrides;
  IdentityFile-extends; StrictHostKeyChecking override; first
  UserKnownHostsFile fills custom_known_hosts if not already set).
  Algorithm directives + ConnectTimeout / ConnectionAttempts are
  recorded in ResolvedSshConfig but not yet plumbed to the session
  (M17 / M18).
- New builder methods: add_identity_file (additive),
  identity_files (replace-all), strict_host_key_checking.
- 0.2.x compatibility shims (#[deprecated]):
  * AnvilConfig::identity_file() returns first identity_files entry.
  * AnvilConfig::skip_host_check() returns true iff policy is No.
  * AnvilConfigBuilder::identity_file(path) clears and pushes single.
  * AnvilConfigBuilder::skip_host_check(bool) maps to Yes/No.
  Removal target: 1.0.

auth.rs:
- find_identity now iterates config.identity_files in order. When
  the explicit list is non-empty, the well-known default search at
  ~/.ssh/id_* is suppressed (matches OpenSSH: explicit IdentityFile
  silences the default fallback).

session.rs:
- Handler reworked to carry policy + host + custom_known_hosts.
  StrictHostKeyChecking::No matches the 0.2.x skip-check path.
  StrictHostKeyChecking::AcceptNew with a writable custom_known_hosts
  records the first-seen fingerprint and accepts; without that path
  it warns and falls back to Yes semantics. Existing-but-mismatched
  fingerprints are always rejected regardless of policy.
- connect() tolerates an empty fingerprint set when policy is
  AcceptNew + custom_known_hosts is set; the handler will record on
  first sight.

hostkey.rs:
- New crate-private append_known_host(path, host, fingerprint) —
  the minimum write surface for AcceptNew. Creates parent dirs and
  appends one `host SHA256:<fp>` line. Locking + duplicate detection
  are deferred to the post-M12 TOFU UX polish.

Cargo.toml: 0.2.0 -> 0.3.0.

CHANGELOG.md: 0.3.0 entry covering the API break + migration table
showing the 0.2.x -> 0.3.0 mapping for both replaced fields.

Tests: +9 config tests (default policy, multi-add, replace-list,
deprecated shims for both fields, all three policy variants,
apply_ssh_config layering + identity_files extension + custom-
known-hosts non-overwrite); +1 auth test (multi-identity-files
probe). Updated test_connection.rs integration tests to use the new
strict_host_key_checking() builder method. Total now 153 lib tests,
0 failures, 1 ignored (pre-existing RSA SSHSIG limitation).

Plan: M12.5 of let-us-plan-on-bright-cosmos.md. Cuts anvil-ssh
0.3.0; M12.7 (Gitway-side) will pick it up.
…rectives

apply_ssh_config now emits a single log::warn at apply time when the
ResolvedSshConfig contains algorithm directives (HostKeyAlgorithms,
KexAlgorithms, Ciphers, MACs — landing in M17) or timing directives
(ConnectTimeout, ConnectionAttempts — landing in M18) that Anvil parses
losslessly for gitway config show but does not yet honor in the
connection path. The warn fires at the bridge between ssh_config and
AnvilConfig — gitway config show and similar inspection callers do
not trigger it.

Plan: M12.6 of let-us-plan-on-bright-cosmos.md.
@UnbreakableMJ UnbreakableMJ changed the title M12.5: AnvilConfig 0.3.0 — multi-IdentityFile + StrictHostKeyChecking M12.5+M12.6: AnvilConfig 0.3.0 — multi-IdentityFile, StrictHostKeyChecking, parse-and-warn May 3, 2026
@UnbreakableMJ
UnbreakableMJ merged commit 461e3ad into main May 3, 2026
5 checks passed
@UnbreakableMJ
UnbreakableMJ deleted the feature/m12-5-anvilconfig-api-break branch May 3, 2026 23:45
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