Provide secure, storage-owned SSH authentication and server identity verification for the optional SSH transport without invoking OpenSSH, a shell, or a credential helper.
Scope
Build the client on russh and keep its session, algorithm, authentication, and trust policy in crates/storage.
Support public-key authentication from explicitly configured OpenSSH private-key files, including encrypted Ed25519, ECDSA, and RSA keys supported by the selected Rust crates.
Support an already-running SSH agent through the pure-Rust agent protocol when explicitly enabled. IronStorage must never start, probe by executing, or configure an agent process.
Define deterministic, bounded identity selection. Do not spray unrelated keys indefinitely or silently fall back to password, keyboard-interactive, OpenSSH config, ProxyCommand, or another program.
Keep identity paths, public fingerprints, host/port, and algorithm names non-secret. Resolve private-key passphrases through the storage-owned protected secret store and SecretBytes; never place private key bytes or passphrases in TOML, Git config, logs, command history, or view-ready errors.
Verify every server key in the russh::client::Handler::check_server_key path against the configured/default OpenSSH known_hosts location using Rust parsing.
Return typed unknown-host, changed-host-key, unsupported-algorithm, missing-key, denied/cancelled passphrase, agent-unavailable, and authentication-rejected states.
Expose an explicit storage API that can persist one confirmed unknown host key. A changed key must always hard-fail and must never be replaced by trust-on-first-use.
Show SHA-256 host and identity fingerprints from canonical key bytes. Bound banners and diagnostics and redact all sensitive values.
Use modern algorithms by default and require an explicit future policy change for SHA-1 or other legacy algorithms.
Tests and verification
Use synthetic keys and a pure-Rust in-process SSH server/agent fixture.
Private keys and passphrases never enter ordinary configuration, Git data, logs, or frontend-owned state.
Cancellation and authentication failures leave repository, refs, index, worktree, known-hosts state, and secret storage unchanged.
Depends on the typed SSH remote and feature-wiring issue in this milestone.
## Goal
Provide secure, storage-owned SSH authentication and server identity verification for the optional SSH transport without invoking OpenSSH, a shell, or a credential helper.
## Scope
- Build the client on `russh` and keep its session, algorithm, authentication, and trust policy in `crates/storage`.
- Support public-key authentication from explicitly configured OpenSSH private-key files, including encrypted Ed25519, ECDSA, and RSA keys supported by the selected Rust crates.
- Support an already-running SSH agent through the pure-Rust agent protocol when explicitly enabled. IronStorage must never start, probe by executing, or configure an agent process.
- Define deterministic, bounded identity selection. Do not spray unrelated keys indefinitely or silently fall back to password, keyboard-interactive, OpenSSH config, `ProxyCommand`, or another program.
- Keep identity paths, public fingerprints, host/port, and algorithm names non-secret. Resolve private-key passphrases through the storage-owned protected secret store and `SecretBytes`; never place private key bytes or passphrases in TOML, Git config, logs, command history, or view-ready errors.
- Verify every server key in the `russh::client::Handler::check_server_key` path against the configured/default OpenSSH `known_hosts` location using Rust parsing.
- Return typed unknown-host, changed-host-key, unsupported-algorithm, missing-key, denied/cancelled passphrase, agent-unavailable, and authentication-rejected states.
- Expose an explicit storage API that can persist one confirmed unknown host key. A changed key must always hard-fail and must never be replaced by trust-on-first-use.
- Show SHA-256 host and identity fingerprints from canonical key bytes. Bound banners and diagnostics and redact all sensitive values.
- Use modern algorithms by default and require an explicit future policy change for SHA-1 or other legacy algorithms.
## Tests and verification
- Use synthetic keys and a pure-Rust in-process SSH server/agent fixture.
- Cover unencrypted and encrypted key files, agent selection, wrong user/key/passphrase, unavailable agent, unknown host confirmation, matching host, changed key, hashed known-host entries where supported, non-default ports, cancellation, and redacted Debug/error output.
- Prove host verification occurs before authentication and the Git command channel.
- Prove no production path calls `std::process::Command` or a helper.
- Run all repository-required Rust gates.
## Acceptance criteria
- A trusted host can authenticate with a configured key file or explicitly enabled existing agent using Rust-only code.
- Unknown hosts require an explicit fingerprint confirmation; changed keys fail closed.
- Private keys and passphrases never enter ordinary configuration, Git data, logs, or frontend-owned state.
- Cancellation and authentication failures leave repository, refs, index, worktree, known-hosts state, and secret storage unchanged.
Depends on the typed SSH remote and feature-wiring issue in this milestone.
hugo
added this to the 07 - Pure-Rust Git-over-SSH remotes milestone 2026-08-24 20:17:06 +00:00
Implemented in 5dbda4bbd2ac60937b67eeb683cfb7fca2d4b788 and pushed to main.
Implementation:
Added a storage-owned Russh client with explicit OpenSSH Ed25519, ECDSA, and RSA key-file authentication, encrypted-key passphrases retrieved as SecretBytes, and exact-fingerprint pure-Rust SSH-agent selection with bounded identity enumeration.
Added strict storage-owned known_hosts verification for exact, comma-list, negated, hashed, non-default-port, revoked, and certificate-authority records. Unknown hosts return a typed confirmation payload; only that payload can be atomically persisted. Changed and revoked keys are never replaced.
Disabled RSA/SHA-1 host-key and authentication paths, added SHA-256 fingerprints, cancellation polling, bounded files/diagnostics/timeouts, and typed missing/denied/cancelled/agent/authentication/host-key failures.
Added protected SSH passphrase storage references and documented the configuration, trust, and secret-storage contracts.
Added in-process pure-Rust SSH server and agent tests covering all three key families, encrypted keys, wrong credentials, exact agent selection/no spraying, unavailable agents, unknown/confirmed/changed/hashed/revoked hosts, cancellation, and verification-before-authentication.
SSH feature tests passed with cargo test --manifest-path crates/storage/Cargo.toml --features ssh --no-fail-fast plus the final targeted SSH suite after hardening.
cargo audit ran with no suppressions. It reports RUSTSEC-2023-0071 for the existing rsa 0.9.10 OpenPGP path and Russh RSA support through rsa 0.10.0-rc.18; neither has a fixed release. The exposure and signing/decryption distinction are recorded in DEPENDENCIES.md.
Computer Use on the freshly bundled release desktop app loaded the existing password store and showed its HTTPS origin at 0 ahead / 0 behind with a clean worktree. SSH frontend exposure is intentionally part of milestone issue #117; no Apple source changed in this issue.
Implemented in `5dbda4bbd2ac60937b67eeb683cfb7fca2d4b788` and pushed to `main`.
Implementation:
- Added a storage-owned Russh client with explicit OpenSSH Ed25519, ECDSA, and RSA key-file authentication, encrypted-key passphrases retrieved as `SecretBytes`, and exact-fingerprint pure-Rust SSH-agent selection with bounded identity enumeration.
- Added strict storage-owned `known_hosts` verification for exact, comma-list, negated, hashed, non-default-port, revoked, and certificate-authority records. Unknown hosts return a typed confirmation payload; only that payload can be atomically persisted. Changed and revoked keys are never replaced.
- Disabled RSA/SHA-1 host-key and authentication paths, added SHA-256 fingerprints, cancellation polling, bounded files/diagnostics/timeouts, and typed missing/denied/cancelled/agent/authentication/host-key failures.
- Added protected SSH passphrase storage references and documented the configuration, trust, and secret-storage contracts.
- Added in-process pure-Rust SSH server and agent tests covering all three key families, encrypted keys, wrong credentials, exact agent selection/no spraying, unavailable agents, unknown/confirmed/changed/hashed/revoked hosts, cancellation, and verification-before-authentication.
Verification:
- `cargo fmt --all -- --check`
- `RUSTFLAGS="-D warnings" cargo check --workspace --all-targets`
- `cargo clippy --workspace --all-targets -- -D warnings`
- `cargo test --workspace`
- `cargo clippy --manifest-path crates/storage/Cargo.toml --all-targets --features ssh -- -D warnings`
- SSH feature tests passed with `cargo test --manifest-path crates/storage/Cargo.toml --features ssh --no-fail-fast` plus the final targeted SSH suite after hardening.
- `cargo audit` ran with no suppressions. It reports RUSTSEC-2023-0071 for the existing `rsa 0.9.10` OpenPGP path and Russh RSA support through `rsa 0.10.0-rc.18`; neither has a fixed release. The exposure and signing/decryption distinction are recorded in `DEPENDENCIES.md`.
- Computer Use on the freshly bundled release desktop app loaded the existing password store and showed its HTTPS origin at 0 ahead / 0 behind with a clean worktree. SSH frontend exposure is intentionally part of milestone issue #117; no Apple source changed in this issue.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Goal
Provide secure, storage-owned SSH authentication and server identity verification for the optional SSH transport without invoking OpenSSH, a shell, or a credential helper.
Scope
russhand keep its session, algorithm, authentication, and trust policy incrates/storage.ProxyCommand, or another program.SecretBytes; never place private key bytes or passphrases in TOML, Git config, logs, command history, or view-ready errors.russh::client::Handler::check_server_keypath against the configured/default OpenSSHknown_hostslocation using Rust parsing.Tests and verification
std::process::Commandor a helper.Acceptance criteria
Depends on the typed SSH remote and feature-wiring issue in this milestone.
Implemented in
5dbda4bbd2ac60937b67eeb683cfb7fca2d4b788and pushed tomain.Implementation:
SecretBytes, and exact-fingerprint pure-Rust SSH-agent selection with bounded identity enumeration.known_hostsverification for exact, comma-list, negated, hashed, non-default-port, revoked, and certificate-authority records. Unknown hosts return a typed confirmation payload; only that payload can be atomically persisted. Changed and revoked keys are never replaced.Verification:
cargo fmt --all -- --checkRUSTFLAGS="-D warnings" cargo check --workspace --all-targetscargo clippy --workspace --all-targets -- -D warningscargo test --workspacecargo clippy --manifest-path crates/storage/Cargo.toml --all-targets --features ssh -- -D warningscargo test --manifest-path crates/storage/Cargo.toml --features ssh --no-fail-fastplus the final targeted SSH suite after hardening.cargo auditran with no suppressions. It reports RUSTSEC-2023-0071 for the existingrsa 0.9.10OpenPGP path and Russh RSA support throughrsa 0.10.0-rc.18; neither has a fixed release. The exposure and signing/decryption distinction are recorded inDEPENDENCIES.md.