fromd4753206895646a2b3ce29b17fea3b1190a273fbb48209cd23bef974c8a501bb → d4753206
to3f7635a940b16714f9808bda72903a0167e0e984fdd3218ce493c5f18f9a9f2a → 3f7635a9

+0 −1

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
1414 The full loop from first init to relay-based collaboration is functional:
1515
1616 ```text
1717 local: init → status → describe → new → log → surface
1818 docks: dock → docks (isolated working trees over one store, ADR 0022)
1919 file: bundle → apply
2020 relay: serve → push → pull
2121 grants: grant → grant --relay → grants → pull-grants
2222 identity: keygen → whoami → peer add → id export → id import
2323 setup: config → clone
2424 ```
2525
2626 ### Try it: private `.env` in a shared repo
2727
2828 ```bash
2929 cargo build --release
3030 export PATH="$PWD/target/release:$PATH"
3131
3232 cd $(mktemp -d)
3333 printf 'TOKEN=supersecret\n' > .env
3434 printf '# My Project\n' > README.md
3535 printf '.env restricted=alice\n*.md public\n' > .lootattributes
3636
3737 loot init --identity alice
3838 loot status -m "initial work"
3939 loot surface # alice: restores both README.md and .env
4040
4141 # switch to a non-keyholder to prove it
4242 printf mallory > .loot/identity
4343 rm -f .env README.md
4444 loot surface # mallory: README.md appears; .env stays sealed
4545 ```
4646
4747 The `.env` ciphertext lives in `.loot/` the whole time. Mallory cannot decrypt
4848 it, and if she snapshots and re-syncs, the sealed file is carried forward
4949 untouched — snapshot is visibility-aware.
5050
5151 ### Sync over a relay
5252
5353 A relay stores and forwards ciphertext it cannot read. Restricted keys never
5454 travel in a sync bundle (ADR 0003), so the relay's zero-knowledge property is
5555 enforced at the wire level, not by policy.
5656
5757 ```bash
5858 # Terminal 1: run a relay
5959 loot serve --dir /tmp/relay --addr 127.0.0.1:4000
6060
6161 # Terminal 2: alice pushes
6262 loot remote add origin http://127.0.0.1:4000
6363 loot push
6464
6565 # Terminal 3: bob pulls (bob only sees public content)
6666 loot clone http://127.0.0.1:4000 ./bob-repo --identity bob
6767 ```
6868
6969 ### Grants: sharing a content key
7070
7171 ```bash
7272 # alice knows bob's public key (from `loot whoami` on bob's machine)
7373 loot peer add bob "ssh-ed25519 AAAA..."
7474
7575 # deliver a sealed grant via the relay
7676 loot grant --relay origin .env bob
7777
7878 # bob fetches and applies it
7979 loot pull-grants # verifies alice's signature, checks peer registry
8080 loot surface # now bob can read .env
8181 ```
8282
8383 ### Embargo: timed reveals
8484
8585 ```bash
8686 # mark a file as embargoed until unix timestamp 1800000000
8787 echo "VULN_DETAILS=CVE-2025-XXXX" > security-fix.txt
8888 printf 'security-fix.txt embargoed=1800000000\n' >> .lootattributes
8989
9090 loot status -m "patch for CVE-2025-XXXX"
9191 loot push # relay holds the ciphertext; key withheld until reveal_at
9292 ```
9393
9494 At `reveal_at`, `flush_escrow` promotes the key so anyone who pulls can read it.
9595 The seam for a third-party key custodian (network escrow) is designed and ready.
9696
9797 ## Architecture
9898
9999 ```text
100100 crates/
101101 loot-core canonical engine: encrypted DAG, per-content visibility, convergence
102102 loot-identity ed25519 keypairs, x25519 ECIES, signed push envelopes, peer registry
103103 loot-net relay HTTP server + sync client (stow/negotiate/grant mailbox)
104104 loot-cli the `loot` binary — commands are thin verbs over Workspace
105105 loot-bench shared 50k-file benchmark workload
106106 spike-dag thin shim re-exporting loot-core (bake-off compat)
107107 spike-crdt non-canonical CRDT model (retained so the bake-off is reproducible)
108108 ```
109109
110110 ### Key modules
111111
112112 | Module | What it owns |
113113 | --- | --- |
114114 | `loot-core::sealed` | Per-content encryption, key custody, embargo, public-content compression (ADR 0003, 0007, 0020) |
115115 | `loot-core::converge` | Merger/relay convergence rule — decrypt-then-merge (ADR 0001) |
116116 | `loot-core::engine` | Encrypted content-addressed DAG: put/get/record/surface/bundle/apply |
117117 | `loot-core::manifest` | Grant audit trail: grantee, grantor pubkeys, timestamps |
118118 | `loot-identity` | ed25519 sign/verify, x25519 derive, ECIES seal/unseal, push envelope |
119119 | `loot-net::mailbox` | Relay grant mailbox: pubkey-addressed, content-addressed loose blobs |
120120 | `loot-cli::workspace` | Ambient repo: identity, clock, persistence, idempotent snapshot |
121121
122122 ### ADRs (docs/adr/)
123123
124124 | # | Decision |
125125 | --- | --- |
126126 | 0001 | Per-content decrypt-then-merge convergence |
127127 | 0002 | Encrypted DAG as the canonical foundation (bake-off winner) |
128128 | 0003 | Sealed content module + keyring custody (restricted keys never travel) |
129129 | 0004 | Drop plaintext dedup equality oracle |
130130 | 0005 | CLI slice, persistence, .lootattributes |
131131 | 0006 | JJ-style workspace auto-snapshot |
132132 | 0007 | Embargo escrow module |
133133 | 0008 | Grant log and targeted key bundles |
134134 | 0009 | Two-level revocation |
135135 | 0010 | Forward-maroon implementation |
136136 | 0011 | Relay stow append-only |
137137 | 0012 | Per-object loose storage |
138138 | 0013 | Named remotes and grant bundle delivery |
139139 | 0014 | Identity keypairs: ed25519 OpenSSH, signed push envelopes |
140140 | 0015 | Grant authentication and trust (grantor signs, peer-registry gate) |
141141 | 0016 | Identity portability: export/import with passphrase wrapping |
142142 | 0017 | RepoStore: one home for the `.loot/` layout |
143143 | 0018 | Signed changes: author in id + validity enforcement |
144144 | 0019 | Format versioning + compatibility gate (newer reads older) |
145145 | 0020 | Compress public content (Zstd); format major → 2 |
146146 | 0021 | Object-level "wants" negotiation on push/pull |
147147 | 0022 | Concurrent-agent model: docks, harbor, optimistic convergence |
148148 | 0023 | Agent-facing machine output: porcelain-first, reconciliation verbs |
149149 | 0024 | Resumable transfer via batched, negotiated sync |
150150
151151 See [CONTEXT.md](CONTEXT.md) for the full domain glossary.
152152
153153 ## Build & test
154154
155155 ```bash
156156 cargo build
157157 cargo test # ~25s — includes HTTP relay integration tests
158158 cargo test -p loot-core # fast, no I/O, 67 tests
159159 ```
160160
161161 ## Command reference
162162
163163 ```text
164164 loot init [--identity <name>] initialize a repo (identity from global config if omitted)
165165 loot clone <url> <dir> clone a relay into <dir>; ends with a materialized working tree
166166 loot config set <key> <val> set a global config value (~/.config/loot/config)
167167 loot status [-m <message>] snapshot the working tree into the working change (idempotent)
168168 loot describe -m <message> name the working change
169169 loot new finalize the working change; start a fresh one
170170 loot surface materialize what the current identity may see
171171 loot lane new [--name <n>] spawn a sealed lane (isolated tree + tip) over the shared store
172172 loot lanes list lanes with their tip, in-flight PR, and status
173−loot lane merge <id-or-name> fold a named lane's finalized line into the primary (CA2)
174173 loot log show change history with visibility hints
175174 loot gc [--dry-run] prune loose objects no change references
176175 loot verify [--accept-loss] integrity-check the object store (exits 1 on corrupt/missing; --accept-loss records unrecoverable losses)
177176 loot bundle <file> write a sync bundle (ciphertext, no keys)
178177 loot apply <file> merge a peer's bundle (idempotent)
179178 loot grant <path> <identity> <file> write a targeted grant bundle (file delivery)
180179 loot grant --relay <remote> <path> <id> seal and deliver a grant via relay mailbox
181180 loot grants [<url>] peek pending grant count (no download)
182181 loot pull-grants [<url>] fetch, verify, and apply sealed grants from relay
183182 loot maroon [--hard] <path> <identity> cut off <identity> from future access
184183 loot migrate <path> <vis-spec> change a path's visibility
185184 loot manifest show the grant audit trail
186185 loot conflicts list paths needing resolution
187186 loot resolve <path> <file> resolve a conflict
188187 loot remote add <name> <url> register a relay URL
189188 loot push [<url>] publish changes to a relay
190189 loot pull [<url>] fetch and merge changes from a relay
191190 loot serve [--addr <host:port>] run a relay
192191 loot keygen generate an identity keypair
193192 loot whoami show identity and public key
194193 loot id export <file> export keypair, passphrase-encrypted
195194 loot id import <file> import keypair from passphrase-encrypted file
196195 loot peer add <name> <pubkey> register a peer's public key
197196 loot peer list list known peers
198197 ```
199198