git-ents.gitmain
⌘K
foforge
commit ba8637f
refactor: extract git-signed-push crate for push-certificate verification

Signed push now has both halves the plan called for: git-member holds the trust data model, git-signed-push holds the verification engine (cert parsing, nonce check, ssh-keygen -Y verify, role gating, the bootstrap-window rule). git-ents-server’s pre-receive hook shrinks to one call into the new crate.

feat: add git-signed-push crate (pre_receive, verify_certificate, identify_signer) docs: point conformance.adoc at git-signed-push/src/lib.rs Assisted-by: Claude:claude-sonnet-5

Joseph D. Carpinelli · 1 month ago

Reviews

No reviews of this commit yet — record a verdict below.

Start a review

verdict

Cargo.lock @@ -1511,6 +1511,7 @@ "git-comment", "git-ents-core", "git-member", + "git-signed-push", "git-store", "git-toolchain", "gix-actor", @@ -1538,6 +1539,16 @@ "thiserror 2.0.18", ] +[[package]] +name = "git-signed-push" +version = "0.0.0" +dependencies = [ + "git-ents-core", + "git-member", + "git-store", + "tempfile", +] + [[package]] name = "git-store" version = "0.0.0"
Cargo.toml @@ -7,6 +7,7 @@ "crates/git-ents-core", "crates/git-ents-server", "crates/git-member", + "crates/git-signed-push", "crates/git-store", "crates/git-toolchain", ] @@ -57,6 +58,7 @@ git-ents-core = { path = "crates/git-ents-core" } git-ents-server = { path = "crates/git-ents-server" } git-member = { path = "crates/git-member" } +git-signed-push = { path = "crates/git-signed-push" } git-store = { path = "crates/git-store" } git-toolchain = { path = "crates/git-toolchain" } gix = "0.84"
crates/git-ents-server/Cargo.toml @@ -21,6 +21,7 @@ git-anchor = { workspace = true } git-comment = { workspace = true } git-member = { workspace = true } +git-signed-push = { workspace = true } git-store = { workspace = true } git-toolchain = { workspace = true } gix-actor = { workspace = true }
docs/spec/conformance.adoc @@ -27,8 +27,8 @@ |`members.allowed-signers` |`git-member/src/members.rs` (`allowed_signers`, `member_lines`) |`members::renders_a_wildcard_allowed_signers_file`, `renders_the_validity_window_as_comma_joined_options` |`revocations.ref` |`git-member/src/revocations.rs` |`revocations::store_then_load_round_trips_the_revocations` |`revocations.ca` |`git-member/src/members.rs` (`without_revoked`, CA branch untouched) |`members::without_revoked_leaves_ca_members_untouched` -|`auth.bootstrap` |`git-ents-server/src/verify.rs` (`pre_receive`, empty-members early return) |`pre_receive` integration tests (bootstrap case) -|`auth.signed-push` |`git-ents-server/src/verify.rs` (`verify_certificate`) |`pre_receive::rejects_an_unsigned_push_when_signers_exist`, `rejects_a_push_signed_by_an_unknown_key` +|`auth.bootstrap` |`git-signed-push/src/lib.rs` (`pre_receive`, empty-members early return) |`pre_receive` integration tests (bootstrap case) +|`auth.signed-push` |`git-signed-push/src/lib.rs` (`verify_certificate`) |`pre_receive::rejects_an_unsigned_push_when_signers_exist`, `rejects_a_push_signed_by_an_unknown_key` |`auth.nonce` |`git-ents-server/src/http.rs` (`backend_config_injects_nonce_seed_and_hooks_path`) |`http::tests::backend_config_injects_nonce_seed_and_hooks_path` |`auth.client-setup` |`git-ents/src/main.rs` (`setup`, `ensure_key`) | _(manual/CLI UX, no automated test)_ |`cli.remote-admin` |`git-ents/src/main.rs` (`sync`, `push_signed`) | _(manual/CLI UX, no automated test)_ @@ -71,8 +71,8 @@ |`nonfunctional.no-unsafe` |workspace-wide (`#![forbid(unsafe_code)]`) |`cargo clippy --workspace --all-targets` |`nonfunctional.object-store` |`git-store/src/lib.rs` (`Store::open`, common-dir odb) | _(see the module's own doc comment; exercised by hook integration tests)_ |`compat.git` |`git-ents-server/src/http.rs`, `web/git.rs` (subprocess invocations) | _(implicit in all integration tests)_ -|`compat.ssh-keygen` |`git-ents-server/src/verify.rs` (`verify_certificate`), `web/write.rs` (`verify_login_signature`) |`pre_receive::*`, `web_edit::*` -|`compat.openssh-signed-push` |`git-ents-server/src/verify.rs` (`GIT_PUSH_CERT`, `GIT_PUSH_CERT_NONCE_STATUS`) |`pre_receive::rejects_a_push_signed_by_an_expired_key` +|`compat.ssh-keygen` |`git-signed-push/src/lib.rs` (`verify_certificate`), `web/write.rs` (`verify_login_signature`) |`pre_receive::*`, `web_edit::*` +|`compat.openssh-signed-push` |`git-signed-push/src/lib.rs` (`GIT_PUSH_CERT`, `GIT_PUSH_CERT_NONCE_STATUS`) |`pre_receive::rejects_a_push_signed_by_an_expired_key` |`compat.sprite` |`git-ents-server/src/checks.rs` (`ensure_auth`, `ensure_sprite`) | _(requires a live Sprite; not covered by unit tests)_ |`compat.cgi` |`git-ents-server/src/http.rs` (`backend`, CGI env vars) |`server::push_then_clone_round_trip` |`compat.edition` |workspace `Cargo.toml` (`edition = "2024"`, `publish = false`) | _(build-time)_
crates/git-ents-server/src/lib.rs @@ -10,7 +10,6 @@ /// MIME-keyed document rendering (HTML and plain-text), shared by the web /// UI and the `git-ents` CLI, which embeds this crate as a library. pub mod render; -mod verify; mod web; use std::net::SocketAddr; @@ -119,7 +118,7 @@ /// wins over the hardcoded default. pub fn run(args: Args) -> ExitCode { if let Some(Command::PreReceive) = args.command { - return match verify::pre_receive() { + return match git_signed_push::pre_receive() { Ok(()) => ExitCode::SUCCESS, Err(reason) => { eprintln!("error: {reason}");
crates/git-ents-server/src/verify.rs → crates/git-signed-push/src/lib.rs
crates/git-signed-push/Cargo.toml @@ -1,0 +1,18 @@ +[package] +name = "git-signed-push" +version = "0.0.0" +edition.workspace = true +publish.workspace = true +license.workspace = true + +[dependencies] +git-ents-core = { workspace = true } +git-member = { workspace = true } +git-store = { workspace = true } +tempfile = { workspace = true } + +[dev-dependencies] +git-store = { workspace = true, features = ["test-support"] } + +[lints] +workspace = true