Short-lived signing certificates for Git, issued down a chain of authority

Files
README.md

warrant #

Short-lived signing certificates for Git, issued down a chain of authority.

warrant issues OpenSSH user certificates to the people, seats and lanes that sign Git commits. Each certificate names its holder down a chain of authority, thomas then thomas:apollo, and its key id names the UCAN delegation from the root's did:key to the holder's key. Git verifies every commit a holder signs against one cert-authority line, so neither Git nor a merge queue changes. The holder's key lives in an ssh-agent for the certificate's window and is never written to disk; revocation is a record in warrant's log and a key revocation list Git reads, derived from that log.

A session #

warrant init thomas --dir store draws the root key, writes the allowed-signers line thomas:* cert-authority,namespaces="git", an empty revocation list and an empty log, and sets gpg.format, gpg.ssh.allowedSignersFile, gpg.ssh.revocationFile, gpg.ssh.defaultKeyCommand, gpg.ssh.program and commit.gpgSign in the global Git configuration. The root key is a file on disk, the weakest custody, and init says so. warrant init thomas --root ca.pub --dir store takes instead the root an ssh-agent already holds, a security key's or a token's, writes its public key alone, and has the agent sign every certificate and delegation. The root is Ed25519 or P-256, the two curves UCAN's delegation signature takes; P-256 is the one a Mac's Secure Enclave holds, through an agent such as Secretive.

warrant issue apollo --for 8h --repo repo --dir store draws a key into the agent for eight hours, signs the delegation and the certificate, and records both in the log. The delegation grants /git/ref/update on one repository, named by the id of the root commit its HEAD's first parents lead to, which no clone or forge changes, and on the refs a like pattern matches, refs/heads/* unless --ref PATTERN says otherwise; --repo takes the repository's path or that id, and a seat is never issued without one. Git runs warrant key --dir store as its default key command and signs with the certificate; git log --format='%G? %GS' prints G thomas:apollo. When several seats hold a certificate, WARRANT_SEAT names the one Git signs with.

warrant verify HEAD -C repo --dir store reads the certificate out of the commit's signature and prints its window at the committer date, the chain its key id names with what the delegation grants, and the log record that holds it; --at TIME judges the window at a time the reader trusts more than the committer's. The commit is judged as an update of the branch HEAD points at, or of the ref --ref REF names, in the repository it is read from, and a ref or a repository the delegation does not cover is refused. warrant revoke 1 records the revocation and rewrites the revocation list, after which Git reports the commit as B. warrant show lists the log.

A seat is rotated by issuing it again before its certificate's window ends: warrant key then hands Git the new certificate, and the old one, superseded, still verifies the commits it signed inside its window. warrant show apollo --dir store lists the seat's certificates, each with its state at the clock, derived from its window, the log and the agent: active, superseded, pending (no key in the agent), expired or revoked. A seat with no active certificate is refused by warrant key, which names the warrant issue command that issues it a new one.

The fleet's agent #

A sandboxed session reaches Unix sockets under its own temporary directory alone, and on macOS launchd binds the login's ssh-agent under /var/run, at a path that changes at every login. warrant agent runs one ssh-agent at a fixed socket every session and the queue runner reach, /tmp/claude-UID/warrant.sock unless --socket or WARRANT_SOCKET names another, as a child it supervises in a process group of its own; it refuses a path longer than a socket address holds and a socket another agent already answers at, and ends with its child's status. warrant agent install writes the launchd job that keeps it across logins and reboots and prints the launchctl bootstrap command that loads it, which the operator runs.

warrant issue, key and show use that agent whenever its socket is there, and the one at SSH_AUTH_SOCK otherwise or with --agent; a root an agent holds still signs through SSH_AUTH_SOCK. gpg.ssh.program is store/ssh-keygen, which runs ssh-keygen with SSH_AUTH_SOCK set to the same socket by the same rule, so Git signs through the fleet's agent whatever the caller's SSH_AUTH_SOCK says.

warrant issue prints the socket of the agent that received the key, and says so when --agent puts it in an agent other than the fleet's, which Git does not sign through. A restart of the fleet's agent drops every key while the certificates stay inside their windows; warrant key then refuses, naming the seat, the agent's socket and the warrant issue command, and Git prints that refusal after gpg.ssh.defaultKeyCommand failed. Git signs with user.signingKey whenever a configuration sets it and never runs the default key command, so a certificate pinned there outlives its key and every rotation: warrant issue names each configuration that sets it for the repository, the global one or the repository's own, with the git config --unset command that removes it.

The log #

The log is a transparency log the root signs. Each issue and revocation is a SCITT signed statement of the root's did:key, whose subject is the certificate's key id, registered in an RFC 9162 Merkle tree kept as C2SP tlog-tiles under store/log: a checkpoint the root signs, the tiles, a receipt per record proving its inclusion, and the delegations by CID. Every file is named by its place in the tree or by its digest and none is edited, so the directory can be served as it is or kept in Git, and warrant verify checks a record's receipt under the checkpoint with nothing from a server.

  • gitsign issues short-lived certificates from Sigstore's Fulcio after an OIDC sign-in and records each signature in the public Rekor log. It needs an identity provider and a public log at signing time, and Git verifies its signatures only through gitsign. warrant works offline, keeps its log as tiles a reader checks offline, and is verified by Git's own SSH signature check.
  • OpenSSH certificates and ssh-keygen -s are what warrant issues; ssh-keygen signs one certificate at a time, with no record of why it was issued and no derived revocation list. warrant adds the chain of authority, the log and the revocation list, and keeps the holder's key out of every file.
  • gittuf adds TUF-style policy on which keys may write which refs, stored in the repository. It is a policy layer over signatures and does not issue them; warrant issues the certificates such a policy could name.

Licence #

ISC. See LICENSE.md.