Repository navigation
Epic 1: a second person can verify - #367
Merged
Merged
Conversation
`auths init` wrote `<repo>/.auths/roots` and never staged it. That file's mere
existence then made the hook's `[ ! -e ]` guard false forever, so the hook never
staged it either. The pin stayed untracked, never reached a cloner, and every
third-party verification path dead-ended on it:
git clone && auths verify HEAD -> KEL not found
... --remote origin -> not a pinned trusted root
... --identity-bundle b.json -> bundle is not a source of its own pin
The pin is the only trust anchor the delegation model permits (an Auths-Id
trailer may *select* among pinned roots, never *establish* one), so nothing
downstream could work. Placing the file by hand made every path return exit 0 —
the engine was never the problem.
- New `RepoIndex` port (stage/is_tracked), mirroring `GitConfigProvider`, with a
subprocess-backed `SystemRepoIndex` adapter in the CLI.
- New `roots::pin_root_in_repo` workflow owns pin+stage and reports what it did.
Staging, not committing: init must not author a commit on the user's behalf —
the pin rides their next one, which they are about to make anyway.
- Hook keys the repair off tracked-ness, not existence, and no longer claims the
pin is "committed with this commit". A `git add` in prepare-commit-msg lands in
commit N+1: git has already snapshotted the index. Hook bumped to v2.
Verified end-to-end: `auths init` now reports `A .auths/roots`, and the pin
lands in the user's next commit.
`auths registry push` worked and had zero callers outside its own command and test — logged in the blockers doc as "4D: nothing pushes the registry". So the signer's KEL never reached the remote their code went to, and a cloner had nothing to resolve `Auths-Id` against. Adds a managed `pre-push` hook installed alongside `prepare-commit-msg`. Three properties are deliberate: - Non-fatal: every failure path exits 0. No write access to the remote is the common, expected case and must never block a code push. - Opt-out via `git config auths.autopush false`. A KEL is public by construction, but pushing to a remote you do not own should stay refusable. - Chains to the repo's own pre-push. `core.hooksPath` *replaces* `.git/hooks`, so a managed hook that did not chain would silently disable every repo-local hook on the machine (husky, pre-commit, prek) — the same class of bug this repo is currently living with. `hook_is_current` now covers both hooks, so `auths doctor` surfaces a stale pre-push instead of leaving a silent gap. Write+chmod factored into one `write_executable` helper. Verified live: a plain `git push` to a bare origin now leaves `refs/auths/registry` on it, and `auths.autopush=false` suppresses the mirror.
A repo now carries its signer's KEL on the same remote it carries its code (the
managed pre-push hook mirrors refs/auths/registry there) — but `git clone`
fetches only refs/heads/* and refs/tags/*. So in a fresh clone the KEL sits one
fetch away on the remote it was cloned from, and `auths verify` failed with "KEL
not found" without ever looking.
`resolve_signer_kel` now falls back to `git remote get-url origin` when neither
--remote nor --oobi is given. This is free in the common case: KelResolverChain
is local-first, so nothing is fetched when the KEL is already local, and the
local registry remains the trusted floor (a remote can only advance key-state,
never roll it back). The fetch fires exactly where verification would otherwise
have failed.
Both --remote docs updated: they claimed "without this flag, resolution is
local-only (no network)", which this makes false.
Epic 1's acceptance criterion now holds end-to-end. On a machine with no
~/.auths, having never met the signer:
git clone <repo> && auths verify HEAD
-> Commit 200a9c0… verified: signed by did:keri:EO0bMMH4… (exit 0)
no flags, no identity, no setup.
Every existing verification test runs as the signer, with a populated ~/.auths. That is precisely why a total failure of the product's core promise shipped undetected: the suite was green and a fresh clone could not verify anything. A test that always plays the signer can never catch it. Adds tests that play the *second person* — no auths identity, a plain `git clone`, no flags: - a_second_person_verifies_a_plain_clone_with_no_identity_and_no_flags (headline) - the_trust_pin_travels_with_the_repository (.auths/roots is in the clone) - a_plain_push_mirrors_the_registry_to_the_remote (pre-push, not a manual push) - an_unpinned_root_still_fails_closed (the TOFU-on-the-root guard holds) The fixture pushes with a plain `git push` and commits with a plain `git commit` — never `auths registry push` or `auths sign` — because a fixture that reaches for the manual verbs would prove nothing about the default path. Mutation-checked: restoring the old write-but-never-stage behaviour in `auths init` fails 3 of the 4. These are tests that would have blocked the regression, not tests that merely pass alongside it.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Auths Commit Verification
Result: ❌ 0/4 commits verified How to fixCommit 1. Install auths macOS: 2. One-time setup (creates your identity and configures Git) auths init3. Sign this branch and push auths sign origin/main..HEAD
git push --force-with-leaseFor CI to verify the signer, commit an identity bundle: auths id export-bundle --alias main --output .auths/ci-bundle.json --max-age-secs 31536000 |
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
Only the signer could verify anything. Established by running the product, not reading it:
That third error is correct — a bundle that bootstrapped its own trust would be circular. The design was right. Two small bugs and one missing default meant nothing could reach a second person.
Placing
.auths/rootsby hand made every path return exit 0. The engine was never the problem.Root cause
init/mod.rswrote<repo>/.auths/rootsand never staged it. That file's mere existence then made the hook's[ ! -e ]guard atcommit_hooks.rs:50false forever, so the hook never staged it either. The two mechanisms were written to cooperate (see the comment atinit/mod.rs:426) and instead cancelled out. The pin stayed untracked, never reached a cloner, and every third-party path dead-ended on it.Separately,
auths registry pushhad zero callers outside its own command — logged indocs/plans/blockers/fixes.mdas "4D: nothing pushes the registry" — so the KEL never reached the remote either. And a plaingit clonefetches onlyrefs/heads/*andrefs/tags/*, so even once mirrored,auths verifynever looked.The four commits
47e4a8dcRepoIndexport +roots::pin_root_in_repoworkflow: pin and stage. Hook keys off tracked-ness, not existence, and stops claiming the pin is "committed with this commit" (it lands in N+1 — git snapshots the index beforeprepare-commit-msg). Hook → v2.8d57d020pre-pushhook mirrorsrefs/auths/registryto the code remote. Non-fatal, opt-out viaauths.autopush=false, and chains to the repo's own pre-push —core.hooksPathreplaces.git/hooks, so a non-chaining hook would silently disable husky/pre-commit/prek machine-wide.dc12ee66auths verifydefaults its KEL transport to the repo's ownorigin. Free in the common case (KelResolverChainis local-first; nothing is fetched when the KEL is local) and fires exactly where verification would otherwise fail.6a8d8f14Result — the acceptance criterion
On a machine with no
~/.auths, having never met the signer:No flags. No identity. No setup.
On the tests
Every existing verification test runs as the signer with a populated
~/.auths— which is exactly why a total failure of the core promise shipped with a green suite. The new tests play the second person, and the fixture uses plaingit commit/git push(neverauths sign/auths registry push), since a fixture reaching for the manual verbs would prove nothing about the default path.Mutation-checked: restoring the old write-but-never-stage behaviour fails 3 of the 4.
Verification
cargo test -p auths-cli --test integration→ 67 passed, 0 failedcargo test -p auths-sdk --lib→ 176 passed, 0 failedcargo clippy -p auths-sdk -p auths-cli --all-targets→ cleancargo fmt --all→ appliedHonesty note
Committing
.auths/rootsmakes the pin travel; it does not make first-clone trust sound. A cloner trusting the repo's own pin is doing TOFU-on-the-root, whichroots.rs:6-8correctly warns is unbounded. What this buys is continuity — the same root signed throughout, rotations chained, later substitution detectable. That is what the flight-recorder use case needs, and it should be documented that way rather than sold as authenticity. Flagged for a second opinion.