Implement SSH receive-pack for push and complete synchronization #116

Closed
opened 2026-08-24 20:17:07 +00:00 by hugo · 1 comment
Owner

Goal

Complete Git-over-SSH with safe push and combined synchronization through the server-side git-receive-pack service.

Scope

  • Open a verified, authenticated russh exec channel for git-receive-pack using the same endpoint, identity, host-key, quoting, cancellation, and diagnostic rules as upload-pack.
  • Reuse the existing storage-owned receive-pack advertisement validation, ancestry/non-fast-forward check, pack construction, capability negotiation, report-status parsing, and remote-tracking update logic without HTTP framing assumptions.
  • Support new-branch and fast-forward push plus the push half of sync for both SSH URL spellings.
  • Keep pull-before-push ordering for sync; never push after a failed/cancelled/conflicted pull.
  • Half-close stdin only after the complete request, drain protocol output and bounded stderr, require an unambiguous successful service status, and do not claim success solely because the socket closed.
  • Map server rejection, unpack failure, ref rejection, non-fast-forward, channel exit/signal, disconnect, cancellation, host/auth failure, and ambiguous post-send failure to typed recoverable states.
  • On an ambiguous failure after bytes may have reached the server, leave the local repository valid and require a fresh fetch/status before retrying; never blindly replay a push.
  • Preserve dirty-worktree refusal and prevent remote tracking refs from advancing until the server has confirmed the update.

Tests and verification

  • Extend the pure-Rust in-process SSH Git fixture with receive-pack behavior; no installed Git/OpenSSH binaries may be required.
  • Cover empty-remote push, ordinary fast-forward push, already-current push, non-fast-forward rejection, server ref policy rejection, unpack failure, malformed status, early EOF, signal/non-zero exit, cancellation before and after send, ambiguous disconnect, and sync pull failure.
  • Inspect emitted pkt-lines, object closure, pack checksum, selected ref, service command, and remote-tracking update.
  • Prove failures do not corrupt objects, refs, index, worktree, or unrelated staged state and do not leak credentials/key material.
  • Run all repository-required Rust gates.

Acceptance criteria

  • Push and full pull-then-push synchronization work over both requested SSH URL forms.
  • A remote ref is updated only through confirmed receive-pack success; non-fast-forward and ambiguous outcomes fail safely.
  • Upload-pack and receive-pack share one storage-owned SSH trust/auth/session policy.
  • No local Git, SSH, shell, helper, or transport process is launched.

Depends on SSH upload-pack plus the authentication/host-verification issue in this milestone.

## Goal Complete Git-over-SSH with safe push and combined synchronization through the server-side `git-receive-pack` service. ## Scope - Open a verified, authenticated `russh` exec channel for `git-receive-pack` using the same endpoint, identity, host-key, quoting, cancellation, and diagnostic rules as upload-pack. - Reuse the existing storage-owned receive-pack advertisement validation, ancestry/non-fast-forward check, pack construction, capability negotiation, report-status parsing, and remote-tracking update logic without HTTP framing assumptions. - Support new-branch and fast-forward push plus the push half of `sync` for both SSH URL spellings. - Keep pull-before-push ordering for `sync`; never push after a failed/cancelled/conflicted pull. - Half-close stdin only after the complete request, drain protocol output and bounded stderr, require an unambiguous successful service status, and do not claim success solely because the socket closed. - Map server rejection, unpack failure, ref rejection, non-fast-forward, channel exit/signal, disconnect, cancellation, host/auth failure, and ambiguous post-send failure to typed recoverable states. - On an ambiguous failure after bytes may have reached the server, leave the local repository valid and require a fresh fetch/status before retrying; never blindly replay a push. - Preserve dirty-worktree refusal and prevent remote tracking refs from advancing until the server has confirmed the update. ## Tests and verification - Extend the pure-Rust in-process SSH Git fixture with receive-pack behavior; no installed Git/OpenSSH binaries may be required. - Cover empty-remote push, ordinary fast-forward push, already-current push, non-fast-forward rejection, server ref policy rejection, unpack failure, malformed status, early EOF, signal/non-zero exit, cancellation before and after send, ambiguous disconnect, and sync pull failure. - Inspect emitted pkt-lines, object closure, pack checksum, selected ref, service command, and remote-tracking update. - Prove failures do not corrupt objects, refs, index, worktree, or unrelated staged state and do not leak credentials/key material. - Run all repository-required Rust gates. ## Acceptance criteria - Push and full pull-then-push synchronization work over both requested SSH URL forms. - A remote ref is updated only through confirmed receive-pack success; non-fast-forward and ambiguous outcomes fail safely. - Upload-pack and receive-pack share one storage-owned SSH trust/auth/session policy. - No local Git, SSH, shell, helper, or transport process is launched. Depends on SSH upload-pack plus the authentication/host-verification issue in this milestone.
hugo added this to the 07 - Pure-Rust Git-over-SSH remotes milestone 2026-08-24 20:17:07 +00:00
hugo added the enhancement label 2026-08-24 20:17:07 +00:00
Author
Owner

Implemented and pushed in 76e707f6457f3a0d8b46f3a49beb9b5d16162046.

  • Added pure-Rust git-receive-pack over the same verified/authenticated Russh session policy as upload-pack, including shared command/path quoting.
  • Reused the storage-owned receive advertisement, fast-forward, pack construction, capability, report-status, and tracking logic for HTTPS and SSH. SSH sends the complete request, half-closes input, drains bounded output/stderr, and requires explicit protocol plus channel success.
  • Added typed unpack/ref rejection and ambiguous post-send outcomes; remote tracking advances only after confirmed success. Synchronization remains pull-first and never opens receive-pack after a failed pull.
  • Added an in-process pure-Rust SSH fixture covering new branch, fast-forward, already current, non-fast-forward, policy rejection, unpack failure, malformed status, EOF, nonzero/signal, disconnect, cancellation before/after send, host/auth failure, both SSH URL spellings, and failed-pull ordering. Tests inspect the service command, ref command, pack v2/object count/checksum, credential non-disclosure, and local/tracking state preservation.

Verification:

  • cargo fmt --all -- --check
  • RUSTFLAGS="-D warnings" cargo check --workspace --all-targets
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo test --workspace
  • SSH-feature all-target check, clippy, and full test suite
  • Release macOS bundle built and validated in the actual IronStorage desktop UI: existing vault loaded; Git status showed main, HTTPS origin, 0 ahead / 0 behind, clean worktree. No Apple app sources changed.
  • cargo audit: existing RUSTSEC-2023-0071 on rsa 0.9.10 and 0.10.0-rc.18 (medium, no fixed upgrade), plus 15 allowed transitive warnings; this issue added no dependencies.
Implemented and pushed in `76e707f6457f3a0d8b46f3a49beb9b5d16162046`. - Added pure-Rust `git-receive-pack` over the same verified/authenticated Russh session policy as upload-pack, including shared command/path quoting. - Reused the storage-owned receive advertisement, fast-forward, pack construction, capability, report-status, and tracking logic for HTTPS and SSH. SSH sends the complete request, half-closes input, drains bounded output/stderr, and requires explicit protocol plus channel success. - Added typed unpack/ref rejection and ambiguous post-send outcomes; remote tracking advances only after confirmed success. Synchronization remains pull-first and never opens receive-pack after a failed pull. - Added an in-process pure-Rust SSH fixture covering new branch, fast-forward, already current, non-fast-forward, policy rejection, unpack failure, malformed status, EOF, nonzero/signal, disconnect, cancellation before/after send, host/auth failure, both SSH URL spellings, and failed-pull ordering. Tests inspect the service command, ref command, pack v2/object count/checksum, credential non-disclosure, and local/tracking state preservation. Verification: - `cargo fmt --all -- --check` - `RUSTFLAGS="-D warnings" cargo check --workspace --all-targets` - `cargo clippy --workspace --all-targets -- -D warnings` - `cargo test --workspace` - SSH-feature all-target check, clippy, and full test suite - Release macOS bundle built and validated in the actual IronStorage desktop UI: existing vault loaded; Git status showed `main`, HTTPS `origin`, 0 ahead / 0 behind, clean worktree. No Apple app sources changed. - `cargo audit`: existing RUSTSEC-2023-0071 on `rsa` 0.9.10 and 0.10.0-rc.18 (medium, no fixed upgrade), plus 15 allowed transitive warnings; this issue added no dependencies.
hugo closed this issue 2026-08-25 18:56:24 +00:00
Sign in to join this conversation.