refactor: rename the `signers` module to `members`
commit
72c4dbbrefactor: rename the `signers` module to `members`
The push-trust concept is uniformly a Member — the CLI is git ents
members, the type is Member, the refs are refs/meta/member/* — but
the module kept the signers name from the old auth design, so code
read git_ents::signers::Member and held members in signers vars. Make
the name match the concept. The OpenSSH allowed_signers term stays, as
it is OpenSSH’s own file-format name.
refactor: rename module git_ents::signers to git_ents::members
docs: correct git-store MapDoc/Row docs that still cited the signer set
Assisted-by: Claude:claude-opus-4-8
Reviews
No reviews of this commit yet — record a verdict below.
Start a review
crates/git-ents-server/src/verify.rs
@@ -14,8 +14,8 @@
use std::path::Path;
use std::process::{Command, Stdio};
+use git_ents::members::{self, Member};
use git_ents::revocations;
-use git_ents::signers::{self, Member};
/// Verify the push git is about to apply, returning `Ok(())` to accept it or
/// `Err(reason)` to reject it. The push certificate is read from the
@@ -23,7 +23,7 @@
pub fn pre_receive() -> Result<(), String> {
let repo = std::env::current_dir().map_err(|e| format!("cannot resolve repository: {e}"))?;
let members =
- signers::load_all(&repo).map_err(|e| format!("could not read authorized signers: {e}"))?;
+ members::load_all(&repo).map_err(|e| format!("could not read authorized signers: {e}"))?;
if members.is_empty() {
// No trust list pushed yet: stay open so the first signer can be added.
// Revocation is keyed on member refs existing, so revoking every member's
@@ -33,7 +33,7 @@
}
let revoked =
revocations::fingerprints(&repo).map_err(|e| format!("could not read revocations: {e}"))?;
- let authorized = signers::without_revoked(members, &revoked);
+ let authorized = members::without_revoked(members, &revoked);
let cert_oid = env("GIT_PUSH_CERT")
.filter(|oid| !oid.is_empty())
@@ -64,7 +64,7 @@
let signature_path = workdir.path().join("cert.sig");
write_file(
&allowed_path,
- signers::allowed_signers(authorized).as_bytes(),
+ members::allowed_signers(authorized).as_bytes(),
)?;
write_file(&signature_path, signature.as_bytes())?;
crates/git-ents/src/lib.rs
@@ -4,8 +4,8 @@
pub mod checks;
pub mod config;
pub mod issues;
+pub mod members;
pub mod revocations;
-pub mod signers;
#[cfg(test)]
mod testutil;
crates/git-ents/src/main.rs
@@ -6,7 +6,7 @@
//! set, and the client setup that produces the signed pushes the server
//! requires. The member commands read and write a remote's set by fetching the
//! `refs/meta/member/*` refs into the local repository, editing them through
-//! [`git_ents::signers`], and pushing them back.
+//! [`git_ents::members`], and pushing them back.
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
@@ -15,8 +15,8 @@
use clap::{Parser, Subcommand};
use git_ents::account::{self, Account};
use git_ents::checks::{self, CHECKS_REF, Check};
+use git_ents::members::{self, MEMBER_NS, Member, Trust, member_ref};
use git_ents::revocations::{self, REVOKED_REF, Revocation};
-use git_ents::signers::{self, MEMBER_NS, Member, Trust, member_ref};
#[derive(Parser)]
#[command(name = "git-ents", about = "Helpful guardians of your git trees.")]
@@ -523,7 +523,7 @@
let repo = repo()?;
sync_namespace(remote, MEMBER_NS)?;
sync(remote, REVOKED_REF)?;
- let members = signers::load_all(&repo).map_err(|error| error.to_string())?;
+ let members = members::load_all(&repo).map_err(|error| error.to_string())?;
if members.is_empty() {
println!("no members on {remote} (open bootstrap window)");
return Ok(());
@@ -625,7 +625,7 @@
let repo = repo()?;
let refname = member_ref(username);
let expected = sync(remote, &refname)?;
- let mut member = signers::load(&repo, username)
+ let mut member = members::load(&repo, username)
.map_err(|error| error.to_string())?
.unwrap_or_else(|| Member::with_keys(username.to_owned(), BTreeMap::new()));
if valid_after.is_some() {
@@ -640,7 +640,7 @@
if let Some(ca_path) = cert_authority {
let ca = read_public_key(ca_path)?;
member.trust = Trust::CertAuthority(ca);
- signers::store(&repo, &member).map_err(|error| error.to_string())?;
+ members::store(&repo, &member).map_err(|error| error.to_string())?;
push_signed(remote, &refname, expected.as_deref())?;
println!("pinned a certificate authority for {username}");
return Ok(());
@@ -665,7 +665,7 @@
return Ok(());
}
keys.insert(fingerprint.clone(), public_key);
- signers::store(&repo, &member).map_err(|error| error.to_string())?;
+ members::store(&repo, &member).map_err(|error| error.to_string())?;
push_signed(remote, &refname, expected.as_deref())?;
println!("authorized {fingerprint} for {username}");
Ok(())
@@ -743,7 +743,7 @@
let public_key = public_key(key)?;
let fingerprint = fingerprint(&public_key)?;
sync_namespace(remote, MEMBER_NS)?;
- let members = signers::load_all(&repo).map_err(|error| error.to_string())?;
+ let members = members::load_all(&repo).map_err(|error| error.to_string())?;
if members.is_empty() {
println!("{remote}: open bootstrap window (no members yet)");
} else if let Some(member) = members.iter().find(|member| {
crates/git-ents/tests/cert_authority.rs
@@ -7,7 +7,7 @@
)]
//! The Phase 3 CA-pin gate: a certificate the pinned CA issued verifies against
-//! the `allowed_signers` file `git_ents::signers` renders, and a certificate
+//! the `allowed_signers` file `git_ents::members` renders, and a certificate
//! from an unpinned CA does not. The cert-embedded signature is produced the way
//! a real client would — through an `ssh-agent` holding the key and its
//! certificate — since `ssh-keygen -Y sign` only embeds a certificate when the
@@ -17,7 +17,7 @@
use std::process::{Command, Stdio};
use std::sync::atomic::{AtomicUsize, Ordering};
-use git_ents::signers::{Member, Trust, allowed_signers};
+use git_ents::members::{Member, Trust, allowed_signers};
/// The principal the CA certifies and the verifier checks — the pusher identity.
const PRINCIPAL: &str = "tester@example.com";
@@ -60,7 +60,7 @@
std::fs::remove_dir_all(&dir).ok();
}
-/// Write the `allowed_signers` file `git_ents::signers` renders for a member
+/// Write the `allowed_signers` file `git_ents::members` renders for a member
/// whose trust is the CA `ca`, and return its path.
fn render_ca_allowed_signers(dir: &Path, name: &str, ca: &Key) -> PathBuf {
let ca_pubkey = std::fs::read_to_string(&ca.public)
crates/git-store/src/lib.rs
@@ -54,9 +54,9 @@
}
/// A meta-ref document that is a single named map of string keys to string
-/// values — the shape the signer set, the check set, and a run's outcomes all
-/// share. The wrapping struct's one field fixes the on-disk subtree name
-/// (`signers/`, `checks/`, `results/`), so each document stays its own type;
+/// values — the shape the check set, the revocation list, and a run's outcomes
+/// all share. The wrapping struct's one field fixes the on-disk subtree name
+/// (`checks/`, `revoked/`, `results/`), so each document stays its own type;
/// this trait is only the bridge that lets them share the load/store plumbing
/// in [`Store::load_entries`] and [`Store::store_entries`].
pub trait MapDoc: for<'a> Facet<'a> {
@@ -67,7 +67,7 @@
}
/// One `(key, value)` entry of a [`MapDoc`] presented as a named type. The set
-/// documents expose legible structs (`Signer`, `Check`, `RunOutcome`) rather
+/// documents expose legible structs (`Check`, `Revocation`, `RunOutcome`) rather
/// than bare pairs; this trait is the single bridge between such a struct and
/// the `(key, value)` shape stored on disk, so the wrap/unwrap is written once
/// here instead of at every load and store.
crates/git-ents-server/src/web/pages.rs
@@ -819,7 +819,7 @@
/// derived feature and check status. Editing is a members-gated write path that
/// does not exist yet, so the values are presented as the current configuration.
pub(super) async fn settings_page(repo: &Path, meta: &RepoMeta) -> Markup {
- let signers = load_signers(repo).await;
+ let members = load_members(repo).await;
let checks = load_checks(repo).await;
repo_shell(
meta,
@@ -863,21 +863,21 @@
div.card {
div.card-header {
"Members"
- @if let Ok(signers) = &signers { span.count { (signers.len()) } }
+ @if let Ok(members) = &members { span.count { (members.len()) } }
}
p.shell-note {
"People on " code { "refs/meta/member/*" } " whose signed pushes are accepted "
"(" code { "git ents members list" } ")."
}
- @match &signers {
+ @match &members {
Err(err) => div.card-row.muted { "Could not read members: " (err) }
- Ok(signers) if signers.is_empty() => {
+ Ok(members) if members.is_empty() => {
div.card-row.muted {
"No members — pushes are open until the first key is added."
}
}
- Ok(signers) => {
- @for member in signers {
+ Ok(members) => {
+ @for member in members {
(member.render())
}
}
@@ -910,11 +910,11 @@
)
}
-/// Load the member set off the async runtime, since `signers::load_all` shells
+/// Load the member set off the async runtime, since `members::load_all` shells
/// out to git and reads the object database synchronously.
-async fn load_signers(repo: &Path) -> Result<Vec<git_ents::signers::Member>, String> {
+async fn load_members(repo: &Path) -> Result<Vec<git_ents::members::Member>, String> {
let repo = repo.to_owned();
- tokio::task::spawn_blocking(move || git_ents::signers::load_all(&repo))
+ tokio::task::spawn_blocking(move || git_ents::members::load_all(&repo))
.await
.map_err(|err| err.to_string())?
.map_err(|err| err.to_string())
crates/git-ents-server/src/web/render.rs
@@ -14,7 +14,7 @@
use git_ents::checks::{Check, Run};
use git_ents::config::Config;
use git_ents::issues::Issue;
-use git_ents::signers::Member;
+use git_ents::members::Member;
/// HTML rendering for a meta-ref value. The default walks the value's [`Facet`]
/// shape structurally; a type overrides [`render`](Render::render) when its
crates/git-ents/src/signers.rs → crates/git-ents/src/members.rs
@@ -1,5 +1,9 @@
//! The repository's members, sourced from the `refs/meta/member/*` refs.
//!
+//! A *member* is one person whose signed pushes the repository accepts. The
+//! OpenSSH `allowed_signers` file this module renders keeps that name because it
+//! is OpenSSH's own format term, but everything else here is framed as members.
+//!
//! Push authentication trusts exactly one place: the `refs/meta/member/<username>`
//! refs. Each is a [`Member`] document — one person, named by the ref's last
//! segment — recording the keys whose signed pushes are accepted and the window