from3f7635a940b16714f9808bda72903a0167e0e984fdd3218ce493c5f18f9a9f2a → 3f7635a9
to0d284658037d700c9f020230cde14b104e0f82b2c96103f3c91141fbce494675 → 0d284658

+12 −8

11 # loot
22
33 A from-scratch source-control system.
44
55 **Thesis:** visibility and permissions belong to *content and changes*, not to
66 the *repository*. Commit your `.env`. Keep files private inside a shared repo.
77 Embargo a security fix: merge it, cut the release, reveal the source later.
88
99 This is the unsolved problem in modern version control. Ergonomics (jj already
1010 nails them) are a layer for later.
1111
1212 ## What works today
1313
14−The full loop from first init to relay-based collaboration is functional:
14+The full loop from first init to relay-based collaboration is functional. This
15+block is the CLI's **full verb list** — all 61 verbs, regenerated from
16+`loot --help` rather than curated (a guard test pins it to the dispatch table,
17+so it cannot drift again; #1107):
1518
1619 ```text
17−local: init → status → describe → new → log → surface
18−docks: dock → docks (isolated working trees over one store, ADR 0022)
19−file: bundle → apply
20−relay: serve → push → pull
21−grants: grant → grant --relay → grants → pull-grants
22−identity: keygen → whoami → peer add → id export → id import
23−setup: config → clone
20+abandon absorb adopt apply archive attest bisect blame bundle buoy burn
21+cherry-pick clone completions config conflicts describe diff doctor duplicate
22+edit embargo-status evolog ferry gc grant grant-status grants grep id init
23+keygen lane lanes log manifest maroon migrate new op peer pull pull-grants
24+purges push rehome relay remote resolve revert serve shortlog split squash
25+status surface tutorial undo verify view whoami
2426 ```
27+
28+`loot <verb> --help` prints any verb's own usage block without running it.
2529
2630 ### Try it: private `.env` in a shared repo
2731
2832 ```bash
2933 cargo build --release
3034 export PATH="$PWD/target/release:$PATH"
3135
3236 cd $(mktemp -d)
3337 printf 'TOKEN=supersecret\n' > .env
3438 printf '# My Project\n' > README.md
3539 printf '.env restricted=alice\n*.md public\n' > .lootattributes
3640
3741 loot init --identity alice
3842 loot status -m "initial work"
3943 loot surface # alice: restores both README.md and .env
4044
4145 # switch to a non-keyholder to prove it
4246 printf mallory > .loot/identity
4347 rm -f .env README.md
4448 loot surface # mallory: README.md appears; .env stays sealed
4549 ```
4650
4751 The `.env` ciphertext lives in `.loot/` the whole time. Mallory cannot decrypt
4852 it, and if she snapshots and re-syncs, the sealed file is carried forward
4953 untouched — snapshot is visibility-aware.
5054
5155 ### Sync over a relay
5256
5357 A relay stores and forwards ciphertext it cannot read. Restricted keys never
5458 travel in a sync bundle (ADR 0003), so the relay's zero-knowledge property is
5559 enforced at the wire level, not by policy.
5660
5761 ```bash
5862 # Terminal 1: run a relay
5963 loot serve --dir /tmp/relay --addr 127.0.0.1:4000
6064
6165 # Terminal 2: alice pushes
6266 loot remote add origin http://127.0.0.1:4000
6367 loot push
6468
6569 # Terminal 3: bob pulls (bob only sees public content)
6670 loot clone http://127.0.0.1:4000 ./bob-repo --identity bob
6771 ```
6872
6973 ### Grants: sharing a content key
7074
7175 ```bash
7276 # alice knows bob's public key (from `loot whoami` on bob's machine)
7377 loot peer add bob "ssh-ed25519 AAAA..."
7478
7579 # deliver a sealed grant via the relay
7680 loot grant --relay origin .env bob
7781
7882 # bob fetches and applies it
7983 loot pull-grants # verifies alice's signature, checks peer registry
8084 loot surface # now bob can read .env
8185 ```
8286
8387 ### Embargo: timed reveals
8488
8589 ```bash
8690 # mark a file as embargoed until unix timestamp 1800000000
8791 echo "VULN_DETAILS=CVE-2025-XXXX" > security-fix.txt
8892 printf 'security-fix.txt embargoed=1800000000\n' >> .lootattributes
8993
9094 loot status -m "patch for CVE-2025-XXXX"
9195 loot push # relay holds the ciphertext; key withheld until reveal_at
9296 ```
9397
9498 At `reveal_at`, `flush_escrow` promotes the key so anyone who pulls can read it.
9599 The seam for a third-party key custodian (network escrow) is designed and ready.
96100
97101 ## Architecture
98102
99103 ```text
100104 crates/
101105 loot-core canonical engine: encrypted DAG, per-content visibility, convergence
102106 loot-identity ed25519 keypairs, x25519 ECIES, signed push envelopes, peer registry
103107 loot-net relay HTTP server + sync client (stow/negotiate/grant mailbox)
104108 loot-cli the `loot` binary — commands are thin verbs over Workspace
105109 loot-bench shared 50k-file benchmark workload
106110 spike-dag thin shim re-exporting loot-core (bake-off compat)
107111 spike-crdt non-canonical CRDT model (retained so the bake-off is reproducible)
108112 ```
109113
110114 ### Key modules
111115
112116 | Module | What it owns |
113117 | --- | --- |
114118 | `loot-core::sealed` | Per-content encryption, key custody, embargo, public-content compression (ADR 0003, 0007, 0020) |
115119 | `loot-core::converge` | Merger/relay convergence rule — decrypt-then-merge (ADR 0001) |
116120 | `loot-core::engine` | Encrypted content-addressed DAG: put/get/record/surface/bundle/apply |
117121 | `loot-core::manifest` | Grant audit trail: grantee, grantor pubkeys, timestamps |
118122 | `loot-identity` | ed25519 sign/verify, x25519 derive, ECIES seal/unseal, push envelope |
119123 | `loot-net::mailbox` | Relay grant mailbox: pubkey-addressed, content-addressed loose blobs |
120124 | `loot-cli::workspace` | Ambient repo: identity, clock, persistence, idempotent snapshot |
121125
122126 ### ADRs (docs/adr/)
123127
124128 | # | Decision |
125129 | --- | --- |
126130 | 0001 | Per-content decrypt-then-merge convergence |
127131 | 0002 | Encrypted DAG as the canonical foundation (bake-off winner) |
128132 | 0003 | Sealed content module + keyring custody (restricted keys never travel) |
129133 | 0004 | Drop plaintext dedup equality oracle |
130134 | 0005 | CLI slice, persistence, .lootattributes |
131135 | 0006 | JJ-style workspace auto-snapshot |
132136 | 0007 | Embargo escrow module |
133137 | 0008 | Grant log and targeted key bundles |
134138 | 0009 | Two-level revocation |
135139 | 0010 | Forward-maroon implementation |
136140 | 0011 | Relay stow append-only |
137141 | 0012 | Per-object loose storage |
138142 | 0013 | Named remotes and grant bundle delivery |
139143 | 0014 | Identity keypairs: ed25519 OpenSSH, signed push envelopes |
140144 | 0015 | Grant authentication and trust (grantor signs, peer-registry gate) |
141145 | 0016 | Identity portability: export/import with passphrase wrapping |
142146 | 0017 | RepoStore: one home for the `.loot/` layout |
143147 | 0018 | Signed changes: author in id + validity enforcement |
144148 | 0019 | Format versioning + compatibility gate (newer reads older) |
145149 | 0020 | Compress public content (Zstd); format major → 2 |
146150 | 0021 | Object-level "wants" negotiation on push/pull |
147151 | 0022 | Concurrent-agent model: docks, harbor, optimistic convergence |
148152 | 0023 | Agent-facing machine output: porcelain-first, reconciliation verbs |
149153 | 0024 | Resumable transfer via batched, negotiated sync |
150154
151155 See [CONTEXT.md](CONTEXT.md) for the full domain glossary.
152156
153157 ## Build & test
154158
155159 ```bash
156160 cargo build
157161 cargo test # ~25s — includes HTTP relay integration tests
158162 cargo test -p loot-core # fast, no I/O, 67 tests
159163 ```
160164
161165 ## Command reference
162166
163167 ```text
164168 loot init [--identity <name>] initialize a repo (identity from global config if omitted)
165169 loot clone <url> <dir> clone a relay into <dir>; ends with a materialized working tree
166170 loot config set <key> <val> set a global config value (~/.config/loot/config)
167171 loot status [-m <message>] snapshot the working tree into the working change (idempotent)
168172 loot describe -m <message> name the working change
169173 loot new finalize the working change; start a fresh one
170174 loot surface materialize what the current identity may see
171175 loot lane new [--name <n>] spawn a sealed lane (isolated tree + tip) over the shared store
172176 loot lanes list lanes with their tip, in-flight PR, and status
173177 loot log show change history with visibility hints
174178 loot gc [--dry-run] prune loose objects no change references
175179 loot verify [--accept-loss] integrity-check the object store (exits 1 on corrupt/missing; --accept-loss records unrecoverable losses)
176180 loot bundle <file> write a sync bundle (ciphertext, no keys)
177181 loot apply <file> merge a peer's bundle (idempotent)
178182 loot grant <path> <identity> <file> write a targeted grant bundle (file delivery)
179183 loot grant --relay <remote> <path> <id> seal and deliver a grant via relay mailbox
180184 loot grants [<url>] peek pending grant count (no download)
181185 loot pull-grants [<url>] fetch, verify, and apply sealed grants from relay
182186 loot maroon [--hard] <path> <identity> cut off <identity> from future access
183187 loot migrate <path> <vis-spec> change a path's visibility
184188 loot manifest show the grant audit trail
185189 loot conflicts list paths needing resolution
186190 loot resolve <path> <file> resolve a conflict
187191 loot remote add <name> <url> register a relay URL
188192 loot push [<url>] publish changes to a relay
189193 loot pull [<url>] fetch and merge changes from a relay
190194 loot serve [--addr <host:port>] run a relay
191195 loot keygen generate an identity keypair
192196 loot whoami show identity and public key
193197 loot id export <file> export keypair, passphrase-encrypted
194198 loot id import <file> import keypair from passphrase-encrypted file
195199 loot peer add <name> <pubkey> register a peer's public key
196200 loot peer list list known peers
197201 ```
198202