sgit-ai 0.26.7
┌─ sgit-ai 0.26.7 ── dev build ── 28 Oct 2026, 12:22 UTC ─────────────────────────
│ tag v0.26.7 = 0d486aaf (dev) · PyPI package == tag · a plain install gets this
│ new user signatures verified: check verify · history log/show --show-signature · tag show
│ not nothing makes them: commit -S / sgit.gpgSign are parked until the web UI confirms
│ tests 5,235 unit+security ✓ · 18/18 end-to-end ✓ · 0.26.0/.3/.6 read signed commits ✓
│ open BR-1 (high, every build) · R3-1 · W-1 (a question for the web UI)
│ maturity #######--- good: use it; it changes what you can see, not what you can write
└──────────────────────────────────────────────────────────────────────────────────
$ pip install sgit-ai==0.26.7 # or: sgit update, which takes the newest build
$ sgit version
sgit-ai v0.26.7
What is new
This build is the first half of Git-parity item 2.2, signing with your own key, from design 02. It is the half that reads.
A second signature on a commit or tag
- Every commit already has a per-clone signature: the clone's own key signs the commit, and
sgit check verifychecks it. - A commit or tag can now also carry
user_signature: the signer's own SSH or OpenPGP signature, Git'sgpgsig. - It covers the same bytes as the clone signature, and neither covers the other.
- Either can be checked, or removed, alone.
- An unsigned commit is stored byte for byte as before: the member is left out when it is empty.
Trust is yours, as in Git
- The settings come from your global Git config, never from the vault or a repository's config, so nothing next to a vault can change what is trusted:
gpg.ssh.allowedSignersFile,gpg.ssh.revocationFile,gpg.minTrustLevel,gpg.format,user.signingKey;- your gpg keyring for OpenPGP.
- Who checks what:
- SSH signatures are checked in the process: ed25519, ECDSA, RSA-SHA2 and FIDO
sk-keys; - certificates,
cert-authoritylines and revocation lists go tossh-keygen; - OpenPGP goes to
gpg. - The verdicts are Git's letters (G U B X Y R E N), checked by the CLI team against
git log --format=%G?.
Where you see it
$ sgit check verify
Checked 5 commit(s): 5 verified, 0 bad, 0 unsigned, 0 without a known key, 0 missing
User signatures: 1 good, 1 untrusted, 0 bad, 0 expired, 0 revoked, 0 cannot check, 3 none
$ sgit check verify --require-user-signature --json # every commit must be good
$ sgit history log --show-signature
$ sgit history show HEAD --show-signature
$ sgit vault tag show v1 # names the tag's signer
check verifyfails (exit 1) on a bad user signature. An untrusted one does not fail, asgit verify-commitdoes not.--require-user-signaturefails unless every commit is good.--jsonis new oncheck verify: counts,user_counts,user_by_statusand the first failure.- The wording is Git's:
Good "git" signature for alice@example.com with ED25519 key SHA256:…,No principal matched.
What is not here
- Nothing creates a user signature.
sgit commit -S,sgit.gpgSignand signing inmerge,write,revertandtagwere written (0709aa86) and then parked (96120aa6) until the vault web UI confirms the rule for the signed bytes (W-1 below). - So today
--show-signatureshows signatures that only a library caller can produce. The CLI team's notes say this plainly; this page says it so that nobody looks for the flag.
Compatibility
- Vaults and the host: no new gate, no new minimum client, no change for a vault that never carries a user signature.
- Old vaults: unchanged. Vaults made by 0.20.0, 0.22.0, 0.24.0 and 0.26.0 (normal and pack mode) open in 0.26.8, fsck clean, and their old clients pull what the new build pushed.
- Old clients and a signed commit: checked by creating a vault with 0.26.8 that holds an unsigned, an SSH-signed and an OpenPGP-signed commit, then:
| Client | clone | fsck | clone signatures | push a commit | 0.26.8 pulls it |
|---|---|---|---|---|---|
| 0.20.0 | ✓ | ok | 4 of 4 verified | ✓ | ✓, 5 of 5 verified |
| 0.22.0 | ✓ | ok | 4 of 4 verified | ✓ | ✓ |
| 0.24.0 | ✓ | ok | 4 of 4 verified | ✓ | ✓ |
| 0.26.0 | ✓ | ok | 4 of 4 verified | ✓ | ✓, 5 of 5 verified |
| 0.26.3 | ✓ | ok | 4 of 4 verified | ✓ | ✓ |
| 0.26.6 | ✓ | ok | 4 of 4 verified | ✓ | ✓ |
- Why old clients are not bothered: they load a commit into a typed object that does not know
user_signature, so the member is dropped on load, and the bytes they check are the bytes that were signed. - That is also why the gate matters: a reader that checks the clone signature over the raw JSON and removes only
signaturewould call every signed commit bad. Python clients cannot do that by accident; the web UI is the open question (W-1).
Review by cli.build
Verdict: good.
- Every claim of this build held, including the verdicts, the unchanged bytes of an unsigned commit and the compatibility with older clients.
- The build reads and checks; it never writes a signature, so it cannot damage a vault.
- One high finding is not this build's own: it is in every build tested (BR-1).
┌─ checks on the PyPI package 0.26.7 ───────────────────────────────────────────────
│ ✓ package == tag v0.26.7 (only the version file differs); wheel == sdist
│ ✓ 5,235 unit+security tests pass (38 skipped)
│ ✓ everything from 0.26.1–0.26.6 (G1–G2, M1–M5, L1–L2, X1–X4, C1–C5) still holds
│ ✓ S1 SSH signature (in-process key) and OpenPGP signature (gpg): checked by 0.26.8 as untrusted / good
│ ✓ S2 a signature over other bytes, and one with a flipped bit: both bad; clone signatures unaffected
│ ✓ S3 0.26.0, 0.26.3 and 0.26.6 clone, fsck, verify and push over signed commits
│ ✓ S4 vaults of 0.20.0 · 0.22.0 · 0.24.0 · 0.26.0, plain and pack: open, commit, push, old client pulls
│ ✗ P1 one push of 150 commits: fresh clone's fsck fails ── BR-1, unchanged from 0.26.6
└──────────────────────────────────────────────────────────────────────────────────────
Code read
The diff against 0.26.6 is 14 changed code files and new ones: crypto/signing/ (19 modules), Vault__User_Signatures, four new schemas and eleven new safe types. Read: the runner, the config reader and resolver, both signers, the commit and tag writers, the verification walk and the CLI wiring.
- Running programs is done in one place (
Signing__Command_Runner): argument lists only, never a shell; bytes on stdin; the working folder is the home folder, never the vault; the environment is a short list (agent sockets,GNUPGHOME, Git's own config variables) plusLC_ALL=C; a missing program is code 127 and a timeout code 124. Nothing from the vault reaches a command line. - The key and the program come from your global Git config only. A repository's config and the vault are not read, so a cloned vault cannot choose which program runs.
- A signing key beginning with
-cannot become an option: it is always the value of-for-bsu. - The temporary file for a literal SSH key is made with
O_EXCLand mode 0600 in a fresh folder and removed afterwards. - A signer checks its own output before returning it, and a failure stops before anything is stored.
- The stored bytes:
stored_jsondrops an emptyuser_signature, so an unsigned commit and tag are byte-identical to the format before. - One small thing:
sgit.gpgSignis the switch, not Git'scommit.gpgSign, on purpose (an existing setting must not make agents start signing after an upgrade). Say so in the release notes when signing ships.
W-1 (a question for the web UI, before signing ships)
- The rule: the bytes covered by either signature are the commit's JSON without
signatureand withoutuser_signature, keys sorted, no spaces, UTF-8. - Why it needs the web UI: if it checks the per-clone signature, it must remove
user_signaturetoo. If it removes onlysignature, a vault with signed commits shows bad signatures in the web UI. - What I could not test: the web UI. It is not in this repository, and a vault with signed commits needs a real host to load there.
- A fixture for them:
scripts/xsign.py new-createbuilds a vault with an unsigned, an SSH-signed and an OpenPGP-signed commit; the CLI team can push one to a test host for the web UI to open. - Update: the CLI team un-parked signing in 0.26.10 on that statement. Ask the web UI team for one test with a signed vault.
Also noted
- The CLI team's brief for this review names
check verify --user. The build has no such flag: user signatures are always counted when a commit carries one, and--require-user-signaturemakes them mandatory. The brief and the CHANGELOG should agree with the build. - The BR-1 fix (0.26.9) and signing itself (0.26.10) came after this build; see 0.26.9 and 0.26.10. W-1: the CLI team says the web UI never verifies commit signatures and ignores members it does not know; not tested by me.
- The clone key and the signer's key are different things.
check verifysays "verified" for the clone signature and reports user signatures on their own line. A reader may read "verified" as "signed by a person"; the release notes for 0.27.0 should say it does not. - Not tested here:
ssh-keygenas the signer or verifier (not installed on the test machine; the in-process path was used), certificates, KRLs, FIDO keys, and a passphrase-protected key.
Scripts: xsign.py, sig_tamper.py, legacy_vault.py, push_gap.py. Results: results/check_0.26.7.json, results/suite-v0.26.7.txt, results/xsign_signed_commits_old_clients.txt, results/sig_tamper_0.26.8.txt, results/legacy_vaults_0.20.0_to_0.26.0.txt. All builds: the builds page.
This page as markdown · its source, admin/content/builds/0-26-7.md