vigo swarm gitback CLI Reference
vigo swarm gitback is the envoy-side surface for the unified-substrate gitback subsystem (ADR-015). The verb tree manages projects: founding new ones, inviting other fleets, joining as a member, listing local state, and queueing leave / revoke envelopes.
Status: ADR-015 cutover in progress
Steps 1–3 (multi-call dispatch, capability tokens, per-user state + CLI) and step 4's first six slices (helper daemon + git-remote-gitback; push-driven fan-out + receive-side endpoint; LAN auto-discovery; envelope gossip with digest catch-up; trait re-emit + mesh schema + UI/CLI rewrite; REST member-roster + reanchor byte migration) have shipped. Verbs that mint or sign envelopes (init, invite, revoke, leave, reanchor) require puddle to be unlocked — see vigo swarm puddle.
What works end-to-end today:
init/invite/join/list/statusare fully functional locally. Run from inside a git work tree,initalso adds theswarmremote andgit push --all && --tags— founding and publishing a repo is one command;--no-pushkeeps it register-only.project pushis the standalone re-publish verb (remote-add-if-missing, push--all/--tags).leaveandrevokesign envelopes inline via the user's puddle session helper, write the signed envelope as a*.pendingsentinel; the agent's root reactor sweeps every 60 s and persists them to/var/lib/vigo/swarm/gitback/envelopes/<project_id>/{revokes,leaves}/.git clone gitback://<project_id>/<handle>andgit push gitback://…against the local envoy: the user-sidegit-remote-gitbackbinary opens/var/run/vigo/gitback.sock(SO_PEERCRED-gated), the agent verifies the user's token chain, and proxies togit upload-pack/git receive-packagainst the bare repo.- Cross-envoy push: after a successful local
git-receive-pack, the helper daemon snapshots refs pre/post and (on delta) builds agit bundle --alland POSTs it to every known peer for the project over swarm mTLS (port 1531). Targets come from two deduplicated sources: peers that have advertised holding the project on the LAN multicast (GITBACKPROJECT:<host>:<pid_hex>:<handle>:<dr_scope>announce lines — ADR-023 widened the line from the original 2-fieldGITBACKPROJECT:<host>:<pid_hex>, which still parses during a rolling agent upgrade — indexed byProjectPeers), and explicit hostnames listed inproject.json::cross_subnet_members. Receivers verify the X-Vigo-Chain header against the URL's project_id, ingest viagit bundle verify+git fetch, and return{accepted, refs_changed}. - DR catch-up (0.46.0): a member that holds local project state but no bare repo (freshly joined, re-joined after
re-found, auto-adopted atpuddle unlockper ADR-023, etc.) pulls one. Every ~45 s the catch-up reactor walks each user's~/.vigo-gitback/<pid>/; for the first project missing its bare repo, itGETs/swarm/gitback/bundle/<pid>from a peer that announces holding it, with the same X-Vigo-Chain envelope a push carries, and ingests the returnedgit bundle --allthrough the push path. One repo backfilled per tick; a no-op once every locally-held project has refs. Removes the old need for a throwaway-tag push or a hand-editedcross_subnet_membersto seed a new envoy. The reactor also flushes the world-readablegitback-announced.jsoncache thatpuddle unlockreads for ADR-023 auto-adoption. - Cross-envoy envelope gossip: after
revokeorleavewrites a signed envelope locally, the reactor fans it out to every known peer viaPOST /swarm/gitback/envelopes/<pid>/{revokes,leaves}. Receivers verify the signature, re-check the project_id, and persist idempotently. Every 5 minutes the reactor reconciles viaGET /swarm/gitback/envelopes/<pid>/digestagainst each peer; on mismatch it pullsGET /swarm/gitback/envelopes/<pid>/alland merges. Peers offline during the original gossip catch up within one reconciliation tick of coming back online.
What's still pending (follow-up slices of step 4):
All step-4 follow-up work has shipped. Cross-envoy push, fetch, envelope gossip, fleet observability, REST member-roster, and reanchor byte migration are all in tree.
ADR-019 (founder-rekey delegations) — complete
ADR-019 shipped across five slices at 0.35.18 → 0.35.22:
- Slice 1 (0.35.18) —
DelegationEnvelopedata layer: Ed25519-signed bridge from a retired founder pubkey to a new one, persisted at~/.vigo-gitback/<pid>/delegations.json. Hard cap atDELEGATION_MAX_LEN = 5, soft warn atDELEGATION_WARN_LEN = 3. - Slice 2 (0.35.19) — chained verification:
tokens::verify_chainwalks the delegation list before the token chain, applying authority windows so retired founders can't sign post-window tokens (the deposed-founder attack). The helper daemon's per-user verify already loads the local delegation chain. - Slice 3 (0.35.20) — rekey-time hooks:
puddle rekey-pairandrekey --fromautomatically sign + append delegations on every founded project and rewrite member entries OLD → NEW.--force-orphan-projectsflag covers the cap case.project statusnow displays delegation chain length, current founder pubkey, and warn-band / at-cap banners. - Slice 4 (0.35.21) — multi-puddle rejoin: new
re-invite-all(founder side) andrejoin(member side) verbs handle cross-puddle members after a founder rekey via fresh out-of-band capability codes, same trust hop as the originalinvite/joinflow. The post-rotation hook now also rebuilds the founder'stokens.jsonwith[bootstrap, fresh_admin_under_NEW]so subsequent re-invites work without manual intervention. The wire chain header (X-Vigo-Chain) gained aPushChainenvelope that carries both the token chain and the founder's delegation list, with backwards-compat fallback for pre-ADR-019 senders that emitted bareTokenChainJSON. - Slice 5 (0.35.22) —
project re-foundescape hatch: mints a freshproject_idfrom the current puddle pubkey, byte-migrates from the at-cap bare repo via the samegit fetch +refs/*:refs/*pathreanchoruses, and tombstones the old project_id on disk via a newproject.json::tombstoned_atfield.--force-orphan-projectsonrekey-pairalso writes the tombstone marker. Tombstoned projects keep their bare repo + URL on disk as an archive but reject helper-daemon git operations and skipcmd_invite/cmd_re_invite_all. Plus this status writeup and the formal ADR-019 document.
Single-puddle personal-DR survives a founder rekey transparently. Multi-puddle projects need a one-time re-invite-all (founder) + rejoin (each cross-puddle member) ceremony after the rekey. After 5 chained rekeys, project re-found is the path forward.
Invocation
vigo swarm gitback status
vigo swarm gitback purge --yes
vigo swarm gitback project <verb> [args]
The command lives inside the vigo agent binary. CLI verbs run as the calling user — root is not required and would discard the user's per-puddle identity. Every verb reads from ~/.vigo-gitback/<project_id>/ (per-user) and the bare-repo dir at /var/lib/vigo/swarm/gitback/projects/ (per-envoy, root-owned, mode 0700 — only the helper daemon proxies non-root reads).
Usercrate authorization (0.41.0+). Every verb on this page (except when invoked as root) refuses unless the invoking user has gitback: true set on the user resource in their usercrate. The agent's user executor writes the resolved flags to /var/lib/vigo/usercrate-policy/<user>.json on each reconcile pass; the CLI reads it as a precondition. Both layers — envoy-level (swarm.gitback.enabled pattern list in server.yaml) and user-level (this flag) — must say yes.
Project identity
Every project is identified by project_id = sha256(founder_puddle_pubkey ‖ handle) rendered as 64-character lowercase hex. Project URLs take the form:
gitback://<project_id>/<handle>
The handle is human-readable; cryptographic identity is the project_id. Squatting on a handle is impossible — there's no central registry. Any fleet can found a project named vigo-web, but the resulting project_ids will differ.
Friendly URLs (ADR-022). If the founder's puddle has claimed a name (vigo swarm puddle name set <name>), gitback://<name>/<handle> works for git operations alongside the hex form — git-remote-gitback resolves <name> to the puddle pubkey via the cached fleet name map and computes sha256(pubkey ‖ handle). On a fleet-name-map miss (or a fresh envoy whose map hasn't bootstrapped yet), the resolver falls back to the requesting user's own ~/.vigo-puddle/name-claim.json, so gitback://<my-name>/<repo> resolves from the envoy that just ran puddle name set without waiting for the claim to propagate through the trait → server → re-fetch round trip. The friendly form is a convenience; the hex gitback://<project_id>/<handle> is the proof (a name is squattable — first claim wins). See vigo swarm puddle name. The <project_id> arguments below take the hex id or a local handle prefix — not a friendly name (that's a URL-resolution feature, not a CLI-argument one).
Verbs that take a <project_id> argument also accept a unique handle prefix among projects this user has joined locally — vigo swarm gitback project status vigo-web resolves the same as the full hex id.
Commands
| Verb | Purpose |
|---|---|
status |
Show envoy-wide gitback posture (publisher + receiver state, helper-daemon socket) |
purge --yes |
Power-user wipe of local gitback state. As a non-root user: ~/.vigo-gitback/. As root: also envoy-wide root-owned state. Distinct from per-project leave --purge; does not gossip. |
gc-orphans [--yes] |
Root-only. List (or delete with --yes) bare repos under /var/lib/vigo/swarm/gitback/projects/ that no user in /etc/passwd still holds a working copy for. Retroactive cleanup for the post-userdel -r case. |
project init <handle> [--scope <kind>] [--repo <path>] [--no-push] |
Found a new project as the local fleet — and, from inside a git work tree, also add the swarm remote and git push --all && --tags to publish it |
project push [<handle>] [--repo <path>] |
Publish (or re-publish) a project from its working repo: add the swarm remote if missing, or re-point it after a re-found/reanchor, then git push swarm --all && --tags. Idempotent |
project invite <project_id> --puddle <fp> --role <role> [--expires <Nd>] |
Mint a child token for another fleet |
project join <project_id> [--invitation <file|->] |
Adopt an invitation issued by an admin |
project leave <project_id> [--purge] [--yes] |
Drop local membership, queue LEAVE envelope |
project list [--json] |
List projects this user has joined locally |
project status <project_id> |
Show one project's local state |
project revoke <project_id> --token-id <hex> [--reason <text>] |
Queue a revocation for a previously-issued token |
project reanchor <project_id> |
Hard-fork the project under a new founder (interactive confirm) |
project re-invite-all [<project_id>] |
(ADR-019) Founder side, post-rekey: mint fresh invitations for every cross-puddle member |
project rejoin <project_id> [--invitation <file|->] |
(ADR-019) Member side: idempotently update local chain in place after a founder rekey |
project re-found <project_id> [--force] |
(ADR-019) Escape hatch when chain is at cap: mint fresh project_id, byte-migrate, tombstone old |
status
Envoy-wide gitback posture, parallel to vigo swarm lockbox status. The bare repos under /var/lib/vigo/swarm/gitback/projects/ are 0700 root:root (non-root callers reach them only through the helper daemon), so status asks the helper daemon — which runs as root — for the authoritative envoy-wide view (per-user ~/.vigo-gitback/ state joined with the bare repos, head refs, and queued envelopes — exactly what the gitback trait carries). If the helper daemon's socket isn't reachable it falls back to a local scan, which sees only the calling user's per-user state and — unless you run it as root — no bare-repo facts; that output is marked partial data:. Distinct from project status <id>, which shows one project in detail.
vigo swarm gitback status
Arguments: none.
Preconditions: none. Read-only; safe for any user; does not require puddle to be unlocked.
Output:
$ vigo swarm gitback status
gitback status
base: /var/lib/vigo/swarm/gitback
projects: 4 (3 with a local bare repo)
bare repos: 511.4 MB on disk
envelopes: 0 revoke(s), 0 leave(s) queued
apractia swarm-wide 1 ref(s), 4.1 MB
tessera swarm-wide 1 ref(s), 812.0 KB
vigo swarm-wide 1 ref(s), 506.5 MB
vigo-web members-only no bare repo yet (awaiting first push or DR catch-up)
helper daemon: socket present (/var/run/vigo/gitback.sock)
On an envoy where no Unix user has founded or joined a project, the projects: line reads none. A project shows no bare repo yet until its first push lands or the DR catch-up reactor pulls it from a peer. The socket line shows socket absent when the helper daemon isn't running — in which case a partial data: line warns that the bare-repo counts above came from a local scan and may be incomplete (run as root, or restart the agent, for the full picture).
purge
Power-user wipe of local gitback state on this envoy. Symmetric with vigo swarm lockbox purge. Distinct from project leave --purge (one project's bare repo) and from the server-driven _apply-blacklist (envoy-wide bare repos via TaskDispatch).
vigo swarm gitback purge --yes
Mode-aware behaviour:
- As a non-root user: wipes
~/.vigo-gitback/for the calling user (every joined project's per-user state —project.json,tokens.json,delegations.json, any pending sentinels). Root-owned bare repos under/var/lib/vigo/swarm/gitback/projects/survive; rerun undersudoto scrub those too. - As root: wipes the envoy-wide root-owned
projects/dir under/var/lib/vigo/swarm/gitback/. The user-side~/.vigo-gitback/of the invoking user is also cleared if it exists; per-user dot-dirs of OTHER users are not touched (each user's home is theirs).
Flags:
--yes— required. Without it the verb prints what would be wiped and exits.
Effects:
- Files are removed via
fs::remove_dir_all/fs::remove_file. No envelope is signed and no gossip is fanned out — peers age this envoy out via the swarm staleness window unless the operator runsproject leave [--yes]per project first or announces out-of-band. - If any
leave.pending/revoke.pendingsentinels existed at purge time, the verb prints a follow-up note so the operator knows that gossip never reached peers.
When to use it. Decommissioning an envoy where you want every gitback byte gone immediately. For a graceful departure where peers learn of it, use project leave per project first.
gc-orphans
Retroactive cleanup for the post-userdel -r case. Lists (and, with --yes, deletes) bare repos under /var/lib/vigo/swarm/gitback/projects/ for which no user in /etc/passwd still holds a ~/<user>/.vigo-gitback/<project_id>/ working-state dir.
vigo swarm gitback gc-orphans # dry-run; print orphan list
vigo swarm gitback gc-orphans --yes # delete the listed bare repos
Root-only. The bare-repo tree at /var/lib/vigo/swarm/gitback/projects/ is root-owned (mode 0700); non-root invocations refuse with a clear error.
Output (dry-run):
2 orphan bare repo(s) under /var/lib/vigo/swarm/gitback/projects/ (no user in /etc/passwd holds the project):
4f0c2…<64 hex> 12.4 MiB last-modified 3d ago /var/lib/vigo/swarm/gitback/projects/4f0c…git
a1b2…<64 hex> 820 B last-modified 47d ago /var/lib/vigo/swarm/gitback/projects/a1b2…git
dry-run — nothing deleted. Re-run with --yes to remove these bare repos.
When to use it. A user got userdel -r <user>'d without first running vigo swarm gitback project leave for each project they were a member of. The departed user's puddle identity went with their home dir, so the agent can no longer auto-emit a LEAVE on their behalf (envelopes must be signed by the subject). The bare repo would otherwise sit forever, absorbing fanout pushes for a member who can't read them.
Caveat. If this envoy was acting as a fanout replicator for a project — bare repo only, no local working copy ever — deleting it drops that replica from the puddle. Only run this when you're sure no other envoy depends on this one for fanout availability.
Preferred workflow (avoid needing gc-orphans):
# as the user, BEFORE userdel -r
for pid in $(vigo swarm gitback project list --json | jq -r '.[].project_id'); do
vigo swarm gitback project leave "$pid" --yes
done
# then it's safe to userdel -r
project init
Founds a new project. The local fleet (per ADR-014) becomes the founder; project_id = sha256(local_puddle_pubkey ‖ handle). Run from inside a git work tree (or pass --repo <path>), init also adds the swarm remote and publishes the repo — founding and publishing are one command.
vigo swarm gitback project init <handle> [--scope swarm-wide|members-only] [--repo <path>] [--no-push]
Arguments:
<handle>— human-readable name. 1-64 ASCII alphanumeric +-_., no leading dot.--scope— distribution scope:swarm-wide(default): bare repo lands on every gitback-authorized envoy in the swarm for DR purposes.members-only: bare repo lands only on envoys whose puddle is in the project'smemberslist.
--repo <path>— the git work tree to publish. Default: the current directory if it's a git work tree.--no-push— register the project (project.json+tokens.json) only; don't touch any working repo. Publish later withproject push.
Preconditions: puddle must be unlocked. Run vigo swarm puddle unlock first.
Effects:
- Writes
project.jsonandtokens.json(with the founder's bootstrap token) under~/.vigo-gitback/<project_id>/. - Unless
--no-push: if the working repo (cwd or--repo) is a git work tree and the helper daemon socket is up, adds aswarmremote pointing atgitback://<project_id>/<handle>(hex form — always resolves) and runsgit push swarm --all && git push swarm --tags, which seeds the bare repo and triggers fan-out. Warns (doesn't block) when<handle>doesn't match the work-tree's directory name. If you're not in a git work tree, or the helper isn't running, or aswarmremote already points elsewhere, it registers the project only and prints how to publish later.
$ cd ~/repos/vigo-web && vigo swarm gitback project init vigo-web
✓ Founded project vigo-web
URL: gitback://e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/vigo-web
Founder: ed25519:f43f6ef3eea29e3780d73aa2cee4a78193419a69f43f6ef3eea29e3780d73aa2
Scope: swarm-wide
State: /home/dan/.vigo-gitback/e3b0c4…/
Remote 'swarm' → gitback://e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/vigo-web
… git push output …
Published. Verify replication with `vigocli swarm gitback show vigo-web`.
When the founder's puddle has claimed a name (vigo swarm puddle name set), the URL: line shows the friendly gitback://<name>/<handle> form with Canonical URL: beneath it.
project push
Publishes (or re-publishes) a project from its working repo. The everyday companion to init — and what you run after a re-found/reanchor changes the project_id. Idempotent; safe to re-run.
vigo swarm gitback project push [<handle>] [--repo <path>]
Arguments:
<handle>— a project handle (orproject_id/ unique prefix) you have local state for. With it,pushensures theswarmremote points at this project — adding it if missing, or re-pointing it (git remote set-url) if it currently addresses a different project. Without it,pushrequires an existingswarmremote and pushes to it as-is.--repo <path>— the git work tree. Default: the current directory.
Preconditions: must be run from inside a git work tree (or with --repo); the helper daemon socket must be up.
Effects: ensures the swarm remote (per the rule above), then git push swarm --all && git push swarm --tags.
$ cd ~/repos/vigo-web && vigo swarm gitback project push vigo-web
✓ Pushed /home/dan/repos/vigo-web to the swarm.
project invite
Mints a child token for another fleet and prints a self-contained JSON invitation the recipient pipes into project join.
vigo swarm gitback project invite <project_id> --puddle <fp> --role admin|member [--expires <Nd>]
Arguments:
<project_id>— full hex or unique handle prefix.--puddle <fp>— recipient's puddle pubkey. Accepts either bare 64-char hex ored25519:<hex>form (the formatvigo swarm puddle pubkeyprints).--role admin|member:admin— recipient can mint further child tokens (invite + revoke).member— recipient can fetch + push but not invite or revoke.
--expires <Nd>— optional.30dor bare30interpreted as days. Default is no expiry.
Preconditions: the local fleet must hold an admin token on the project.
Output: JSON invitation on stdout, instructions on stderr. Pipe stdout to the recipient by any side channel:
$ vigo swarm gitback project invite vigo-web \
--fleet ed25519:abc... --role member > invite.json
$ scp invite.json other-laptop:~
The recipient runs:
$ vigo swarm gitback project join <project_id> --invitation invite.json
project join
Adopts an invitation issued by an admin.
vigo swarm gitback project join <project_id> [--invitation <file|->]
Arguments:
<project_id>— must match the invitation's anchor (sanity check — catches "wrong invitation pasted").--invitation <file|->— path to the JSON invitation, or-to read from stdin (default).
Preconditions: puddle must be initialized for this user (vigo swarm puddle init once, then per-envoy join --from <host>).
Verification: the chain is verified offline against the founder pubkey embedded in the invitation. The leaf token's subject must match the local puddle pubkey — an invitation issued for someone else's fleet is rejected.
project leave
Drops local membership.
vigo swarm gitback project leave <project_id> [--purge] [--yes]
Arguments:
<project_id>--purge— also wipe the bare repo bytes on this envoy. Requires root (the bare repo lives at0700 root:root).--yes— skip the confirmation prompt.
Effects:
- Signs a LEAVE envelope inline via the user's puddle session helper and writes it as a
leave.pendingsentinel under~/.vigo-gitback/<project_id>/. The agent's root reactor sweeps every 60 s and moves the signed envelope into/var/lib/vigo/swarm/gitback/envelopes/<project_id>/leaves/. After the next slice of step 4 lands, the same envelope will gossip to peers; today it persists locally only. - Removes the per-user state directory (
project.json+tokens.json+ sentinels). - With
--purgeand root, removes/var/lib/vigo/swarm/gitback/projects/<project_id>.git.
project list
$ vigo swarm gitback project list
HANDLE SCOPE MEMBERS TOKENS PROJECT_ID
vigo-web swarm-wide 1 1 e3b0c44298fc1c14…
team-notes members-only 3 2 a891c4b27e3431af…
--json emits the full ProjectConfig array as JSON.
project status
Multi-line detail block: handle, URL, project_id, founder, fan-out, member list, token chain depth, leaf role + subject + issued / expires timestamps, ADR-019 delegation chain length and current founder pubkey (with warn-band yellow flag at length ≥ DELEGATION_WARN_LEN and a hard "AT CAP" banner at DELEGATION_MAX_LEN), tombstone marker (when set, with a hint pointing at re-found or project leave), state-dir path, pending-sentinel flags.
vigo swarm gitback project status <project_id>
project revoke
Admin only. Queues a revocation envelope for a previously-issued token.
vigo swarm gitback project revoke <project_id> --token-id <hex> [--reason <text>]
Arguments:
<project_id>--token-id <hex>— 64-char hex token ID. Tokens are content-addressed (token_id = sha256(canonical_bytes)), surfaced viaproject status(chain depth) and the audit log.--reason <text>— optional human-readable reason captured in the envelope.
Effects: signs a REVOKE envelope inline via the user's puddle session helper (the leaf admin token's subject == this user's puddle pubkey, so the user holds an authorized signing key) and writes it as a revoke.pending sentinel under ~/.vigo-gitback/<project_id>/. The agent's root reactor sweeps every 60 s and moves the signed envelope into /var/lib/vigo/swarm/gitback/envelopes/<project_id>/revokes/<token_id_hex>.json. From that point any push presenting a chain that includes the revoked token id is rejected by the helper daemon on this envoy. Cross-envoy gossip lands in the next slice of step 4. 90-day retention before reap, matching lockbox.
project reanchor
Hard-fork the project under a new founder. Used when the original founder puddle (and all admin fleets) are gone — surviving members can adopt a new anchor and continue.
vigo swarm gitback project reanchor <project_id>
Interactive: prints the consequences (old URL becomes dead, members must re-add the new remote, tokens not portable) and refuses to proceed unless the user types the project handle to confirm. Same anti-footgun pattern as git push --force-with-lease.
Effects:
- Refuses if
sha256(this_user_puddle_pubkey ‖ handle) == old_project_id(the calling user is already the founder, so reanchor is a no-op). - Refuses if a project already exists locally under the proposed new project_id.
- Mints a fresh founder bootstrap admin token under this user's puddle and persists
project.json+tokens.jsonunder~/.vigo-gitback/<new_project_id>/. The new members list starts as[this_fleet]; surviving members from the old project must be re-invited viaproject invite <new_project_id> --puddle <fp> --role <r>. - When run as root and the old bare repo exists at
/var/lib/vigo/swarm/gitback/projects/<old_project_id>.git, runsgit -C <new_repo> fetch <old_repo> +refs/*:refs/*to copy every ref + the objects backing them into the new bare repo. Non-root invocations (or envoys that don't hold the old bare repo) print a note and rebuild only the local auth surface; another member with root + the old bare repo can complete the migration later. - The old project state is left in place — drop it via
project leave <old_project_id>once the new project is healthy.
project re-invite-all
(ADR-019) Founder-side flow run after puddle rekey-pair. Walks every multi-puddle project the local fleet founded, mints a fresh invitation for every cross-puddle member, prints the JSON to stdout with stderr separators per recipient. Single-puddle personal-DR projects are skipped (their members are sibling envoys in the user's own puddle, already inheriting trust via ADR-018's pair-claim ceremony).
vigo swarm gitback project re-invite-all [<project_id>]
Arguments:
- Optional positional
<project_id>(or unique handle prefix) — restricts to a single project. Without an argument, walks every founded multi-puddle project.
Preconditions: puddle must be unlocked (the verb signs new leaf tokens via the session helper). The local user must be the founder of at least one multi-puddle project — the founder predicate uses chain.last().new_founder_pubkey (or founder_puddle_pubkey if no delegations) compared against the local puddle pubkey.
Effects:
- For every founded project where
members[]minus the local fleet is non-empty, mints a freshMembertoken chained off the local user's current admin leaf (which slice 3's rekey hook rebuilt under the new identity). - Embeds the project's full delegation chain in each invitation so the recipient's verifier can walk the post-rekey leaf back to the genesis founder pubkey baked into
project_id. - Prints
(member fleet, project handle, invitation JSON)triples to stdout; per-recipient header lines go to stderr so the operator can split the JSON blocks into per-recipient files via simple shell tooling. Pipe each JSON block out-of-band to the matching member puddle — same trust hop as the originalinviteflow.
Threat model: an attacker who has compromised a retired founder key could forge their own delegation envelope and present a chain that mathematically verifies. The OOB capability code (the re-invite-all JSON block being communicated through a trusted channel from a trusted founder) is the trust gate; receivers do not auto-accept gossiped delegations.
$ vigo swarm gitback project re-invite-all
─── re-invite for project 'team-repo' → ed25519:ab12… ───
{
"version": 1,
"project": { … },
"chain": [ … ],
"delegations": [ … ]
}
✓ 1 re-invite invitation(s) printed across founded multi-puddle project(s).
Skipped 2 single-puddle project(s) (no cross-puddle members): vigo-web, dotfiles
Communicate each JSON block out-of-band to the matching member puddle,
then have them run:
vigo swarm gitback project rejoin <project_id> --invitation -
project rejoin
(ADR-019) Member-side flow run after the founder of a multi-puddle project has rekeyed and run re-invite-all. Idempotent extension of project join — does NOT bail when the project already exists locally.
vigo swarm gitback project rejoin <project_id> [--invitation <file|->]
Arguments:
<project_id>(or unique handle prefix) — must match the project_id baked into the invitation JSON.--invitation <file|->— path to the invitation JSON, or-for stdin (default).
Preconditions: local puddle identity must be initialized. Unlike join, rejoin does NOT require the absence of an existing project state.
Effects:
- Verifies the invitation chain via
verify_chainwith the embedded delegations as the authority bridge. Rejects if the chain doesn't anchor to the genesis founder pubkey baked intoproject_id, if the leaf doesn't grant access to the local fleet, or if any delegation signature fails. - Updates
project.json,tokens.json, anddelegations.jsonfrom the invitation. When the invitation has empty delegations (founder rolled back or re-founded under genesis), removes any stale localdelegations.jsonso the verifier doesn't apply bogus authority windows. - Preserves any unrelated local state (revoke / leave pending sentinels). The bare repo bytes on this envoy are untouched —
project_idis stable across the founder's rekey, so no data migration is needed.
$ vigo swarm gitback project rejoin team-repo --invitation re-invite.json
✓ Re-joined project team-repo (chain refreshed)
URL: gitback://e3b0…/team-repo
Role: Member
Members: 3
Scope: members-only
Delegations: 1 hop(s) (chain anchors to genesis founder ed25519:ab12345…)
State updated under /home/dan/.vigo-gitback/e3b0…
project re-found
(ADR-019) Escape hatch when a founded project's delegation chain is at the cap (DELEGATION_MAX_LEN = 5) or has been tombstoned by a --force-orphan-projects rekey. Mints a fresh project_id from the current puddle pubkey, byte-migrates from the at-cap bare repo via the same git fetch +refs/*:refs/* path reanchor uses, and tombstones the OLD project on disk.
Differs from reanchor: reanchor is for surviving members taking over after the original founder puddle is gone (refuses if the calling user IS the current founder); re-found is for the founder themselves resetting their own project chain (REQUIRES that the calling user is the current founder).
vigo swarm gitback project re-found <project_id> [--force]
Arguments:
<project_id>(or unique handle prefix) — the at-cap or tombstoned project to re-found.--force— re-found a project whose chain is below cap and which isn't tombstoned. Manual chain-hygiene reset; use sparingly.
Preconditions:
- Puddle must be unlocked (the verb signs a fresh bootstrap token via the session helper).
- Caller must be the project's current founder. The predicate uses
chain.last().new_founder_pubkey(orfounder_puddle_pubkeyif there are no delegations). - The founder must have rekeyed at least once since founding the project — otherwise the new project_id (
sha256(current_puddle_pubkey ‖ handle)) collides with the old, and re-found refuses with a hint to runpuddle rekey-pairfirst. - A project under the proposed new project_id must not already exist locally.
Interactive: prints the consequences (old URL becomes dead, new URL must be communicated to members, etc.) and refuses to proceed unless the operator types the project handle exactly. Same anti-footgun pattern as git push --force-with-lease.
Effects:
- Mints a fresh founder bootstrap admin token under the calling user's CURRENT puddle identity (the freshly-rekeyed key, not the genesis pubkey baked into the OLD project_id).
- Persists
project.json+tokens.jsonunder~/.vigo-gitback/<new_project_id>/. The new members list starts as[this_fleet]; surviving members from the old project must be re-invited via a follow-upproject re-invite-all <new_project_id>. - When run as root and the old bare repo exists at
/var/lib/vigo/swarm/gitback/projects/<old_project_id>.git, runsgit -C <new_repo> fetch <old_repo> +refs/*:refs/*to copy every ref + the objects backing them into the new bare repo. Non-root invocations (or envoys that don't hold the old bare repo) print a note and rebuild only the local auth surface; another root member with the old bare repo can complete the migration later. - Sets
project.json::tombstoned_aton the OLD project. The bare repo + URL stay on disk as an archive, but the helper daemon refuses git operations and CLI verbs (invite,revoke,re-invite-all) skip the project. Drop the archive entirely viaproject leave <old_project_id>once the new project is healthy.
See also
- ADR-015 — Unified gitback substrate
- ADR-014 — Puddle primitive
- ADR-018 — Puddle rekey
- ADR-019 — Gitback founder-rekey via anchor-preserving delegation chains — the design behind
re-invite-all,rejoin,re-found, the delegation chain primitive, and authority windows. Shipped 0.35.18–0.35.22. - vigo swarm puddle CLI reference
- Capability-token primitives —
tokens.rs