{"_id":"@bombadil/loam","_rev":"9-8aed4c1d3344660bb67163f3fa3af765","name":"@bombadil/loam","dist-tags":{"latest":"0.7.0"},"versions":{"0.0.0":{"name":"@bombadil/loam","version":"0.0.0","keywords":["database","crdt","graphql","content-addressed","capabilities","federation","rhizomatic"],"author":{"name":"Mykola Bilokonsky"},"license":"MIT OR Apache-2.0","_id":"@bombadil/loam@0.0.0","maintainers":[{"name":"mykola","email":"mbilokonsky@gmail.com"}],"homepage":"https://github.com/bombadil-labs/loam#readme","bugs":{"url":"https://github.com/bombadil-labs/loam/issues"},"bin":{"loam":"dist/cli/bin.js"},"dist":{"shasum":"3a6bba90cbb4433064dab527898bf51368b757c5","tarball":"https://registry.npmjs.org/@bombadil/loam/-/loam-0.0.0.tgz","fileCount":119,"integrity":"sha512-5fnbW4dkm2XK0i8sKQRCRwPtx2SsEp7P5ThSkyYIcw0zhU5NlxjHayhELdtDDNGH7wpHuJD+v7fbPb7Gw1Lq5w==","signatures":[{"sig":"MEQCIFqrCBagJEDThvwq16JKr0yJnQxW/vSh6BpmOr85y+YhAiBNGfvL8oHaQ4Jb5htY0xM+39NezSfazCLhFvmq4ApvYw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":580602},"type":"module","engines":{"node":">=22.13"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"7cf05774bb6e8b9e2507568673cacba5d9257455","scripts":{"lint":"eslint .","test":"vitest run","build":"tsc -p tsconfig.build.json","check":"npm run format:check && npm run lint && npm run typecheck && npm run build && npm test","format":"prettier --write .","release":"node scripts/release.mjs","version":"node scripts/sync-version.mjs","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit","format:check":"prettier --check .","prepublishOnly":"npm run check"},"_npmUser":{"name":"mykola","email":"mbilokonsky@gmail.com"},"repository":{"url":"git+https://github.com/bombadil-labs/loam.git","type":"git"},"_npmVersion":"10.9.8","description":"Loam — a general database grown on rhizomatic: signed, content-addressed deltas beneath; GraphQL query, mutation, and subscription above.","directories":{},"_nodeVersion":"22.0.0","dependencies":{"graphql":"^17.0.2","better-sqlite3":"^12.11.1","@bombadil/rhizomatic":"^0.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.15.0","vitest":"^4.1.10","prettier":"^3.3.0","@eslint/js":"^9.15.0","typescript":"^5.7.0","@types/node":"^22.0.0","typescript-eslint":"^8.15.0","@types/better-sqlite3":"^7.6.13","eslint-config-prettier":"^9.1.0"},"_npmOperationalInternal":{"tmp":"tmp/loam_0.0.0_1783653757059_0.7275330234260033","host":"s3://npm-registry-packages-npm-production"}},"0.0.1":{"name":"@bombadil/loam","version":"0.0.1","keywords":["database","crdt","graphql","content-addressed","capabilities","federation","rhizomatic"],"author":{"name":"Mykola Bilokonsky"},"license":"MIT OR Apache-2.0","_id":"@bombadil/loam@0.0.1","maintainers":[{"name":"mykola","email":"mbilokonsky@gmail.com"}],"homepage":"https://github.com/bombadil-labs/loam#readme","bugs":{"url":"https://github.com/bombadil-labs/loam/issues"},"bin":{"loam":"dist/cli/bin.js"},"dist":{"shasum":"7b90f3795af974e70b78b297967c8dbc675cd48a","tarball":"https://registry.npmjs.org/@bombadil/loam/-/loam-0.0.1.tgz","fileCount":119,"integrity":"sha512-kAZ4IvelPbB3m3UF57H9nvZQrnp75dFB3zSmhE4FL8fo44i6oN/9+2HCqEvhEJTolCvwdy5SMbY95FXDAJJgKA==","signatures":[{"sig":"MEUCIQCBucR9kim7mATVwEOs5Eo5uNGAvE5N9/X9rlYlAa35MAIgVK6u5dbB9H2HBZty6eTGh749fIYt6yZvPY/RA+B2v7A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bombadil%2floam@0.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":580691},"type":"module","engines":{"node":">=22.13"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"c58cbf39f2759bff437a859e38b5ce280a94393c","scripts":{"lint":"eslint .","test":"vitest run","build":"tsc -p tsconfig.build.json","check":"npm run format:check && npm run lint && npm run typecheck && npm run build && npm test","format":"prettier --write .","release":"node scripts/release.mjs","version":"node scripts/sync-version.mjs","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit","format:check":"prettier --check .","prepublishOnly":"npm run check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:494caeba-b769-47cb-96e7-256ef18b0374"}},"repository":{"url":"git+https://github.com/bombadil-labs/loam.git","type":"git"},"_npmVersion":"11.16.0","description":"Loam — a general database grown on rhizomatic: signed, content-addressed deltas beneath; GraphQL query, mutation, and subscription above.","directories":{},"_nodeVersion":"24.18.0","allowScripts":{"esbuild@0.25.12":true,"better-sqlite3@12.11.1":true},"dependencies":{"graphql":"^17.0.2","better-sqlite3":"^12.11.1","@bombadil/rhizomatic":"^0.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.15.0","vitest":"^4.1.10","prettier":"^3.3.0","@eslint/js":"^9.15.0","typescript":"^5.7.0","@types/node":"^22.0.0","typescript-eslint":"^8.15.0","@types/better-sqlite3":"^7.6.13","eslint-config-prettier":"^9.1.0"},"_npmOperationalInternal":{"tmp":"tmp/loam_0.0.1_1783655642744_0.34132785328745596","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@bombadil/loam","version":"0.1.0","keywords":["database","crdt","graphql","content-addressed","capabilities","federation","rhizomatic"],"author":{"name":"Mykola Bilokonsky"},"license":"MIT OR Apache-2.0","_id":"@bombadil/loam@0.1.0","maintainers":[{"name":"mykola","email":"mbilokonsky@gmail.com"}],"homepage":"https://github.com/bombadil-labs/loam#readme","bugs":{"url":"https://github.com/bombadil-labs/loam/issues"},"bin":{"loam":"dist/cli/bin.js"},"dist":{"shasum":"624c64535c9bb0ab763098c097e1b3b96c9968f1","tarball":"https://registry.npmjs.org/@bombadil/loam/-/loam-0.1.0.tgz","fileCount":149,"integrity":"sha512-rQr3ou46pYRfTC5NNHlgdXkoXH3ADRN5M8JNol7YqsELC6WNZ/VOP0ZgACZWL70mFZCFQzLAE7VkYaOnVxzKLQ==","signatures":[{"sig":"MEQCIBaiINp02JiUgBD5aK0s+an9O2RCo5J+D5KMbAthQQcUAiB03kso/uS/h1ov2HYcqnZ8z/9mnDNtu04pD8Vu3gy+Mw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bombadil%2floam@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1453258},"type":"module","engines":{"node":">=22.13"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./client":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./browser":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"gitHead":"5891015675d300f228873e1eee5b840e8e0bbf93","scripts":{"lint":"eslint .","test":"vitest run","build":"tsc -p tsconfig.build.json && node scripts/build-bundles.mjs","check":"npm run format:check && npm run lint && npm run typecheck && npm run build && npm test","format":"prettier --write .","release":"node scripts/release.mjs","version":"node scripts/sync-version.mjs","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit","format:check":"prettier --check .","prepublishOnly":"npm run check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:494caeba-b769-47cb-96e7-256ef18b0374"}},"repository":{"url":"git+https://github.com/bombadil-labs/loam.git","type":"git"},"_npmVersion":"11.16.0","description":"Loam — a general database grown on rhizomatic: signed, content-addressed deltas beneath; GraphQL query, mutation, and subscription above.","directories":{},"_nodeVersion":"24.18.0","allowScripts":{"esbuild@0.25.12":true,"better-sqlite3@12.11.1":true},"dependencies":{"graphql":"^17.0.2","@noble/hashes":"^1.8.0","better-sqlite3":"^12.11.1","@bombadil/rhizomatic":"^0.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.15.0","vitest":"^4.1.10","esbuild":"^0.25.12","prettier":"^3.3.0","@eslint/js":"^9.15.0","typescript":"^5.7.0","@types/node":"^22.0.0","typescript-eslint":"^8.15.0","@types/better-sqlite3":"^7.6.13","eslint-config-prettier":"^9.1.0"},"_npmOperationalInternal":{"tmp":"tmp/loam_0.1.0_1783787812808_0.8402837559533449","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@bombadil/loam","version":"0.2.0","keywords":["database","crdt","graphql","content-addressed","capabilities","federation","rhizomatic"],"author":{"name":"Mykola Bilokonsky"},"license":"MIT OR Apache-2.0","_id":"@bombadil/loam@0.2.0","maintainers":[{"name":"mykola","email":"mbilokonsky@gmail.com"}],"homepage":"https://github.com/bombadil-labs/loam#readme","bugs":{"url":"https://github.com/bombadil-labs/loam/issues"},"bin":{"loam":"dist/cli/bin.js"},"dist":{"shasum":"c411449097effc2dd68341af9a718077c08eab77","tarball":"https://registry.npmjs.org/@bombadil/loam/-/loam-0.2.0.tgz","fileCount":365,"integrity":"sha512-Anj+RhzDwKRYj/fj9yF3fAJVncKo6z0rDQ6v/zuhwk18raP17FFC/kzdPkZCPiC1iuRiFMfnUxbJJ1PjkAOmNw==","signatures":[{"sig":"MEUCIHZJzilBenPf+PV1Y65htRmwP+Amony05kXofcbPaEMVAiEAl1rbyqBk8bP57KcMsUEVtdYVdzYAV4baKIaRntiNntM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bombadil%2floam@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":5351866},"type":"module","engines":{"node":">=24"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./client":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./browser":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"gitHead":"cbecae140006d772c681a1e81c286856c300f047","scripts":{"p5":"node scripts/p5-triage.mjs","lint":"eslint .","test":"vitest run","build":"tsc -p tsconfig.build.json && node scripts/build-bundles.mjs","check":"npm run format:check && npm run lint && npm run typecheck && npm run build && npm test","format":"prettier --write .","release":"node scripts/release.mjs","version":"node scripts/sync-version.mjs","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit","adlc:patch":"node scripts/patch-adlc-npx.mjs && node scripts/patch-adlc-init.mjs && node scripts/patch-adlc-hollow-ts.mjs","format:check":"prettier --check .","prepublishOnly":"npm run check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:494caeba-b769-47cb-96e7-256ef18b0374"}},"repository":{"url":"git+https://github.com/bombadil-labs/loam.git","type":"git"},"_npmVersion":"11.17.0","description":"Loam — a general database grown on rhizomatic: signed, content-addressed deltas beneath; GraphQL query, mutation, and subscription above.","directories":{},"_nodeVersion":"24.19.0","allowScripts":{"esbuild@0.25.12":true,"better-sqlite3@12.11.1":true},"dependencies":{"graphql":"^17.0.2","@noble/hashes":"^1.8.0","better-sqlite3":"^12.11.1","@bombadil/rhizomatic":"^0.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.15.0","vitest":"^4.1.10","esbuild":"^0.25.12","prettier":"^3.3.0","happy-dom":"^20.11.1","@eslint/js":"^9.15.0","codemirror":"^6.0.2","typescript":"^5.7.0","@types/node":"^22.0.0","cm6-graphql":"^0.2.1","@codemirror/lint":"^6.9.7","@codemirror/view":"^6.43.6","@codemirror/state":"^6.7.1","typescript-eslint":"^8.15.0","@codemirror/language":"^6.12.4","@types/better-sqlite3":"^7.6.13","eslint-config-prettier":"^9.1.0","@codemirror/autocomplete":"^6.20.3"},"_npmOperationalInternal":{"tmp":"tmp/loam_0.2.0_1786819768918_0.4004953449426145","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@bombadil/loam","version":"0.3.0","keywords":["database","crdt","graphql","content-addressed","capabilities","federation","rhizomatic"],"author":{"name":"Mykola Bilokonsky"},"license":"MIT OR Apache-2.0","_id":"@bombadil/loam@0.3.0","maintainers":[{"name":"mykola","email":"mbilokonsky@gmail.com"}],"homepage":"https://github.com/bombadil-labs/loam#readme","bugs":{"url":"https://github.com/bombadil-labs/loam/issues"},"bin":{"loam":"dist/cli/bin.js"},"dist":{"shasum":"29716e602a6f2f9e2f0df0436984456dc6cde016","tarball":"https://registry.npmjs.org/@bombadil/loam/-/loam-0.3.0.tgz","fileCount":405,"integrity":"sha512-QG2zAYpfxIm9J485TBUoVf5cKD/9YyDT7FctEQ2IzrEilTvu5fY1Ok5+4KZxT1uaXjOOBCxDKHWOOIUbCXE8AA==","signatures":[{"sig":"MEYCIQCd8iLGlU1ZUunVH0NcUWMrGoXud50/fVdpgc9nJqdsPwIhAMdN8SX7FBxTlTaeWC2f0uKI7EyLgnGXxwngONIP6bTh","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bombadil%2floam@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":6989385},"type":"module","engines":{"node":">=24"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./client":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./browser":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"gitHead":"5bfa389fda3908c4d21541f6ec824841bf5ea302","scripts":{"p5":"node scripts/p5-triage.mjs","lint":"eslint .","test":"vitest run","build":"node scripts/build-docs.mjs --check && tsc -p tsconfig.build.json && node scripts/build-bundles.mjs","check":"npm run format:check && npm run lint && npm run typecheck && npm run build && npm test","format":"prettier --write .","release":"node scripts/release.mjs","version":"node scripts/sync-version.mjs","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit","adlc:patch":"node scripts/patch-adlc-npx.mjs && node scripts/patch-adlc-init.mjs && node scripts/patch-adlc-hollow-ts.mjs","format:check":"prettier --check .","prepublishOnly":"npm run check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:494caeba-b769-47cb-96e7-256ef18b0374"}},"repository":{"url":"git+https://github.com/bombadil-labs/loam.git","type":"git"},"_npmVersion":"11.17.0","description":"Loam — a general database grown on rhizomatic: signed, content-addressed deltas beneath; GraphQL query, mutation, and subscription above.","directories":{},"_nodeVersion":"24.19.0","allowScripts":{"esbuild@0.25.12":true,"better-sqlite3@12.11.1":true},"dependencies":{"graphql":"^17.0.2","@noble/hashes":"^1.8.0","better-sqlite3":"^12.11.1","@bombadil/rhizomatic":"^0.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.15.0","vitest":"^4.1.10","esbuild":"^0.25.12","prettier":"^3.3.0","happy-dom":"^20.11.1","@eslint/js":"^9.15.0","codemirror":"^6.0.2","typescript":"^5.7.0","@types/node":"^22.0.0","cm6-graphql":"^0.2.1","@codemirror/lint":"^6.9.7","@codemirror/view":"^6.43.6","@codemirror/state":"^6.7.1","typescript-eslint":"^8.15.0","@codemirror/language":"^6.12.4","@types/better-sqlite3":"^7.6.13","eslint-config-prettier":"^9.1.0","@codemirror/autocomplete":"^6.20.3"},"_npmOperationalInternal":{"tmp":"tmp/loam_0.3.0_1787844213856_0.844565766911433","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@bombadil/loam","version":"0.4.0","keywords":["database","crdt","graphql","content-addressed","capabilities","federation","rhizomatic"],"author":{"name":"Mykola Bilokonsky"},"license":"MIT OR Apache-2.0","_id":"@bombadil/loam@0.4.0","maintainers":[{"name":"mykola","email":"mbilokonsky@gmail.com"}],"homepage":"https://github.com/bombadil-labs/loam#readme","bugs":{"url":"https://github.com/bombadil-labs/loam/issues"},"bin":{"loam":"dist/cli/bin.js"},"dist":{"shasum":"945d3cd43f3b5a6c943850534664cda0fd7e9919","tarball":"https://registry.npmjs.org/@bombadil/loam/-/loam-0.4.0.tgz","fileCount":405,"integrity":"sha512-OeGNc6mI6xZZJixBzi8IKWtU7stm7fxrTZoP/E7nkiWlTsH6nZ/4mdQ/VBcsHVUUcwE+5JbxZYmWDzaCQ0mx6w==","signatures":[{"sig":"MEUCIF83pSeHTP/M7GaIYPxgIURZJtvEkyZviKOqIXSyKj2yAiEAglOAl0n8TIR1sTprkZ8rkwjcnvNifTBYiI8vlrxGens=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bombadil%2floam@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":7046180},"type":"module","engines":{"node":">=24"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./client":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./browser":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"gitHead":"76436206fcc1f06d404239c6a9547a7ef63d9e77","scripts":{"p5":"node scripts/p5-triage.mjs","lint":"eslint .","test":"vitest run","build":"node scripts/build-docs.mjs --check && tsc -p tsconfig.build.json && node scripts/build-bundles.mjs","check":"npm run format:check && npm run lint && npm run typecheck && npm run build && npm test","format":"prettier --write .","release":"node scripts/release.mjs","version":"node scripts/sync-version.mjs","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit","adlc:patch":"node scripts/patch-adlc-npx.mjs && node scripts/patch-adlc-init.mjs && node scripts/patch-adlc-hollow-ts.mjs","format:check":"prettier --check .","prepublishOnly":"npm run check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:494caeba-b769-47cb-96e7-256ef18b0374"}},"repository":{"url":"git+https://github.com/bombadil-labs/loam.git","type":"git"},"_npmVersion":"11.17.0","description":"Loam — a general database grown on rhizomatic: signed, content-addressed deltas beneath; GraphQL query, mutation, and subscription above.","directories":{},"_nodeVersion":"24.19.0","allowScripts":{"esbuild@0.25.12":true,"better-sqlite3@12.11.1":true},"dependencies":{"graphql":"^17.0.2","@noble/hashes":"^1.8.0","better-sqlite3":"^12.11.1","@bombadil/rhizomatic":"^0.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.15.0","vitest":"^4.1.10","esbuild":"^0.25.12","prettier":"^3.3.0","happy-dom":"^20.11.1","@eslint/js":"^9.15.0","codemirror":"^6.0.2","typescript":"^5.7.0","@types/node":"^22.0.0","cm6-graphql":"^0.2.1","@codemirror/lint":"^6.9.7","@codemirror/view":"^6.43.6","@codemirror/state":"^6.7.1","typescript-eslint":"^8.15.0","@codemirror/language":"^6.12.4","@types/better-sqlite3":"^7.6.13","eslint-config-prettier":"^9.1.0","@codemirror/autocomplete":"^6.20.3"},"_npmOperationalInternal":{"tmp":"tmp/loam_0.4.0_1787885902467_0.49790330017312523","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@bombadil/loam","version":"0.5.0","keywords":["database","crdt","graphql","content-addressed","capabilities","federation","rhizomatic"],"author":{"name":"Mykola Bilokonsky"},"license":"MIT OR Apache-2.0","_id":"@bombadil/loam@0.5.0","maintainers":[{"name":"mykola","email":"mbilokonsky@gmail.com"}],"homepage":"https://github.com/bombadil-labs/loam#readme","bugs":{"url":"https://github.com/bombadil-labs/loam/issues"},"bin":{"loam":"dist/cli/bin.js"},"dist":{"shasum":"108c60b76fd73eb1cb0e07e369c3bacceea0bc8c","tarball":"https://registry.npmjs.org/@bombadil/loam/-/loam-0.5.0.tgz","fileCount":420,"integrity":"sha512-on5FJzYLAUdMuiCQNReY43PgnlsJpi6RrGvumMwTcAEVEGL81f5aBTZYwQzmBFqbhVc+EXn6uD8Z0LWKyGDs8Q==","signatures":[{"sig":"MEUCIAoZntFMUHQNeivf2YwyGjU3I5VI8T6298bTqd0Mh4AUAiEAx5lT67I9KvH07VOl7sQspdBP8g5boFYLWFtDd4ycRDQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bombadil%2floam@0.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":7140346},"type":"module","engines":{"node":">=24"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./client":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./browser":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"gitHead":"d4b4ce93fa9371f73caf95cc08e187eba5029713","scripts":{"p5":"node scripts/p5-triage.mjs","lint":"eslint .","test":"vitest run","build":"node scripts/build-docs.mjs --check && tsc -p tsconfig.build.json && node scripts/build-bundles.mjs","check":"npm run format:check && npm run lint && npm run typecheck && npm run build && npm test","format":"prettier --write .","release":"node scripts/release.mjs","version":"node scripts/sync-version.mjs","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit","adlc:patch":"node scripts/patch-adlc-npx.mjs && node scripts/patch-adlc-init.mjs && node scripts/patch-adlc-hollow-ts.mjs","format:check":"prettier --check .","prepublishOnly":"npm run check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:494caeba-b769-47cb-96e7-256ef18b0374"}},"repository":{"url":"git+https://github.com/bombadil-labs/loam.git","type":"git"},"_npmVersion":"11.17.0","description":"Loam — a general database grown on rhizomatic: signed, content-addressed deltas beneath; GraphQL query, mutation, and subscription above.","directories":{},"_nodeVersion":"24.19.0","allowScripts":{"esbuild@0.25.12":true,"better-sqlite3@12.11.1":true},"dependencies":{"graphql":"^17.0.2","@noble/hashes":"^1.8.0","better-sqlite3":"^12.11.1","@bombadil/rhizomatic":"^0.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.15.0","vitest":"^4.1.10","esbuild":"^0.25.12","prettier":"^3.3.0","happy-dom":"^20.11.1","@eslint/js":"^9.15.0","codemirror":"^6.0.2","typescript":"^5.7.0","@types/node":"^22.0.0","cm6-graphql":"^0.2.1","@codemirror/lint":"^6.9.7","@codemirror/view":"^6.43.6","@codemirror/state":"^6.7.1","typescript-eslint":"^8.15.0","@codemirror/language":"^6.12.4","@types/better-sqlite3":"^7.6.13","eslint-config-prettier":"^9.1.0","@codemirror/autocomplete":"^6.20.3"},"_npmOperationalInternal":{"tmp":"tmp/loam_0.5.0_1787985526196_0.3502984108324514","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@bombadil/loam","version":"0.6.0","keywords":["database","crdt","graphql","content-addressed","capabilities","federation","rhizomatic"],"author":{"name":"Mykola Bilokonsky"},"license":"MIT OR Apache-2.0","_id":"@bombadil/loam@0.6.0","maintainers":[{"name":"mykola","email":"mbilokonsky@gmail.com"}],"homepage":"https://github.com/bombadil-labs/loam#readme","bugs":{"url":"https://github.com/bombadil-labs/loam/issues"},"bin":{"loam":"dist/cli/bin.js"},"dist":{"shasum":"0b69af1b3f1846f421810aeb1b91a9d0607b5abf","tarball":"https://registry.npmjs.org/@bombadil/loam/-/loam-0.6.0.tgz","fileCount":425,"integrity":"sha512-ajLgW146qroHaQFI6+hd8LJWkaJaSg+fD/lBn93Aar+X+Ct7z4uE60XPLg/l8UpIrldLHS1PJH7wnyOjh0RPCg==","signatures":[{"sig":"MEUCIDmqcprB7MV2pmhBbnL7fLWiDhKc/G7WM+NMLPh1hJqoAiEA/Bom1383+gTMof2mxuRbKHT6k77B2z7RdvbqmL/5F24=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bombadil%2floam@0.6.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":7213147},"type":"module","engines":{"node":">=24"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./client":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./browser":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"gitHead":"19be9d9d0299b9e8b5d2a260e8dc3ed4784285e1","scripts":{"p5":"node scripts/p5-triage.mjs","lint":"eslint .","test":"vitest run","build":"node scripts/build-docs.mjs --check && tsc -p tsconfig.build.json && node scripts/build-bundles.mjs","check":"npm run format:check && npm run lint && npm run typecheck && npm run build && npm test","format":"prettier --write .","release":"node scripts/release.mjs","version":"node scripts/sync-version.mjs","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit","adlc:patch":"node scripts/patch-adlc-npx.mjs && node scripts/patch-adlc-init.mjs && node scripts/patch-adlc-hollow-ts.mjs","format:check":"prettier --check .","prepublishOnly":"npm run check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:494caeba-b769-47cb-96e7-256ef18b0374"}},"repository":{"url":"git+https://github.com/bombadil-labs/loam.git","type":"git"},"_npmVersion":"11.17.0","description":"Loam — a general database grown on rhizomatic: signed, content-addressed deltas beneath; GraphQL query, mutation, and subscription above.","directories":{},"_nodeVersion":"24.19.0","allowScripts":{"esbuild@0.25.12":true,"better-sqlite3@12.11.1":true},"dependencies":{"graphql":"^17.0.2","@noble/hashes":"^1.8.0","better-sqlite3":"^12.11.1","@bombadil/rhizomatic":"^0.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.15.0","vitest":"^4.1.10","esbuild":"^0.25.12","prettier":"^3.3.0","happy-dom":"^20.11.1","@eslint/js":"^9.15.0","codemirror":"^6.0.2","typescript":"^5.7.0","@types/node":"^22.0.0","cm6-graphql":"^0.2.1","@codemirror/lint":"^6.9.7","@codemirror/view":"^6.43.6","@codemirror/state":"^6.7.1","typescript-eslint":"^8.15.0","@codemirror/language":"^6.12.4","@types/better-sqlite3":"^7.6.13","eslint-config-prettier":"^9.1.0","@codemirror/autocomplete":"^6.20.3"},"_npmOperationalInternal":{"tmp":"tmp/loam_0.6.0_1788218492452_0.3142782505923669","host":"s3://npm-registry-packages-npm-production"}},"0.7.0":{"name":"@bombadil/loam","version":"0.7.0","type":"module","description":"Loam — a general database grown on rhizomatic: signed, content-addressed deltas beneath; GraphQL query, mutation, and subscription above.","license":"MIT OR Apache-2.0","author":{"name":"Mykola Bilokonsky"},"keywords":["database","crdt","graphql","content-addressed","capabilities","federation","rhizomatic"],"repository":{"type":"git","url":"git+https://github.com/bombadil-labs/loam.git"},"homepage":"https://github.com/bombadil-labs/loam#readme","bugs":{"url":"https://github.com/bombadil-labs/loam/issues"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./client":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./browser":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"bin":{"loam":"dist/cli/bin.js"},"engines":{"node":">=24"},"publishConfig":{"access":"public"},"allowScripts":{"better-sqlite3@12.11.1":true,"esbuild@0.25.12":true},"scripts":{"build":"node scripts/build-docs.mjs --check && tsc -p tsconfig.build.json && node scripts/build-bundles.mjs","test":"vitest run","typecheck":"tsc --noEmit","lint":"eslint .","lint:fix":"eslint . --fix","format":"prettier --write .","format:check":"prettier --check .","check":"npm run format:check && npm run lint && npm run typecheck && npm run build && npm test","release":"node scripts/release.mjs","version":"node scripts/sync-version.mjs","prepublishOnly":"npm run check","adlc:patch":"node scripts/patch-adlc-npx.mjs && node scripts/patch-adlc-init.mjs && node scripts/patch-adlc-hollow-ts.mjs","p5":"node scripts/p5-triage.mjs"},"dependencies":{"@bombadil/rhizomatic":"^0.8.0","@noble/hashes":"^1.8.0","better-sqlite3":"^12.11.1","graphql":"^17.0.2"},"devDependencies":{"@codemirror/autocomplete":"^6.20.3","@codemirror/language":"^6.12.4","@codemirror/lint":"^6.9.7","@codemirror/state":"^6.7.1","@codemirror/view":"^6.43.6","@eslint/js":"^9.15.0","@types/better-sqlite3":"^7.6.13","@types/node":"^22.0.0","cm6-graphql":"^0.2.1","codemirror":"^6.0.2","esbuild":"^0.25.12","eslint":"^9.15.0","eslint-config-prettier":"^9.1.0","happy-dom":"^20.11.1","prettier":"^3.3.0","typescript":"^5.7.0","typescript-eslint":"^8.15.0","vitest":"^4.1.10"},"gitHead":"fa042fb5ea53b6d1f5de015e4a014b09a8e456b2","_id":"@bombadil/loam@0.7.0","_nodeVersion":"24.19.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-KDhsTTFj00YWSFG1pUGq7/9yB8zeKuXFwUpr9UxMF0sqvmteWapTc9+je4oWRKPr4/Xuglmip6M7wlOCmcV9Mw==","shasum":"fa899e730867aa23560e81edc8056113bab78e6c","tarball":"https://registry.npmjs.org/@bombadil/loam/-/loam-0.7.0.tgz","fileCount":435,"unpackedSize":7414426,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bombadil%2floam@0.7.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGBJwXgTsrZ1NHClUrc9sdnZb+SrJaoubW8NXxFRhn2tAiBrBGg219/qPKmsG6W5+2Lab2MUHzCGZ597VTnrMbH9LQ=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:494caeba-b769-47cb-96e7-256ef18b0374"}},"directories":{},"maintainers":[{"name":"mykola","email":"mbilokonsky@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/loam_0.7.0_1788370755350_0.21596632888663536"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-10T03:22:36.913Z","modified":"2026-09-02T17:39:15.969Z","0.0.0":"2026-07-10T03:22:37.307Z","0.0.1":"2026-07-10T03:54:02.927Z","0.1.0":"2026-07-11T16:36:53.014Z","0.2.0":"2026-08-15T18:49:29.116Z","0.3.0":"2026-08-27T15:23:34.105Z","0.4.0":"2026-08-28T02:58:22.623Z","0.5.0":"2026-08-29T06:38:46.416Z","0.6.0":"2026-08-31T23:21:32.667Z","0.7.0":"2026-09-02T17:39:15.613Z"},"bugs":{"url":"https://github.com/bombadil-labs/loam/issues"},"author":{"name":"Mykola Bilokonsky"},"license":"MIT OR Apache-2.0","homepage":"https://github.com/bombadil-labs/loam#readme","keywords":["database","crdt","graphql","content-addressed","capabilities","federation","rhizomatic"],"repository":{"type":"git","url":"git+https://github.com/bombadil-labs/loam.git"},"description":"Loam — a general database grown on rhizomatic: signed, content-addressed deltas beneath; GraphQL query, mutation, and subscription above.","maintainers":[{"name":"mykola","email":"mbilokonsky@gmail.com"}],"readme":"# Loam\n\nBeneath everything that grows, there is ground.\n\nLoam is a general database built on [rhizomatic](https://github.com/bombadil-labs/rhizomatic) — a\nportable format for signed, content-addressed deltas whose merge is union: order-blind,\nidempotent, conflict-free. Rhizomatic is the format and the reactive core; Loam is the wrapper\nthat makes it a deployable, GraphQL-fronted, persistent, multi-tenant, federatable server.\n\nIts shapes are grown, not imposed — you declare a hyperschema and a schema (a shape, and how to read it), and the medium resolves\nyour data into views, maintains them live, and remembers everything. Nothing is deleted; the\nstore only ever learns. Two Loam instances that meet simply merge. Trust is a lens the reader\nholds, not a verdict the ground hands down.\n\nThe design is in [SPEC.md](SPEC.md), the working record in [JOURNAL.md](JOURNAL.md), and the backlog\nof unbuilt work as ADLC tickets in `.adlc/tickets/`. This page is the manual;\n[how the repo is organized](#how-the-repo-is-organized) is spelled out below.\n\n**New here? Take [the interactive tutorial](https://bombadil-labs.github.io/loam/tutorial.html)** — it hands\nyou a real store running in your browser (no signup, no server, nothing to install until the\nlast step) and teaches Loam by growing one: fifteen lessons from \"you are the operator\" to\ncarrying your store out of the tab and serving it from your own machine, the same store proven\nhash for hash.\n\n**Evaluating the repo — human or agent?** Start in [`demos/`](demos/README.md): the tutorial's\nsource lives there, and beside it the **village** — five federated stores, an adversary, and a\nledger mapping every demonstrated behavior to the machinery that proves it, end-to-end over\nreal HTTP.\n\n---\n\n## Install\n\nLoam is a Node package (Node ≥ 24) that ships both a library and a `loam` CLI.\n\n```sh\nnpm install @bombadil/loam\n```\n\nIt depends on `@bombadil/rhizomatic` (the substrate), `graphql`, and `better-sqlite3` (the durable\nstore driver — a native addon with prebuilt binaries for common platforms).\n\n**Running from a clone.** If you were handed this repository rather than the package, build it\nfirst — the `loam` command lives at `dist/cli/bin.js` and does not exist until you do:\n\n```sh\nnpm ci && npm run build\nnode dist/cli/bin.js --help\n```\n\nAdd `npm link` if you would rather type `loam` than the path. Everything below says `loam`; from a\nclone, read that as `node dist/cli/bin.js`.\n\n## The model in one breath\n\n- A **delta** is a signed, content-addressed fact. A **store** is a grow-only set of them.\n- A **HyperSchema** gathers the deltas relevant to an entity into a **Hyperview**; a **Schema**\n  resolves that hyperview into a **View** — the answer. One hyperschema, many schemas; one schema,\n  many entities.\n- The **Gateway** fronts one store: it derives a GraphQL surface from the (schema, policy) pairs\n  you register, and serves `query`, `mutate`, and `subscribe` over it.\n- **Capabilities** govern writes: nothing is written except by a verified author a surviving\n  grant permits. The **operator** (the gateway's signing seed) roots the chain.\n- **Federation** is union at the substrate: peers exchange verified deltas; trust is the reader's\n  policy lens, never a write denial.\n\n## Quickstart — the CLI\n\n```sh\n# create a home directory and mint an operator identity (the seed is written 0600, never printed).\n# At a terminal this is guided — it also asks for a first user and stocks the shelf (§54); piped\n# and flagless it stays the bare two-file init.\nloam init --home ./my-store\n\n# give the store a shape. `--stock` registers one Loam ships, so day one needs no hand-written\n# gather term: the shelf is event, note, org, person, post, and the shallow-person reading\n# they nest (`loam register --help` describes each).\nloam register --stock note --home ./my-store\n\n# inspect a store\nloam store --home ./my-store\n\n# serve it over HTTP with a bearer token. `serve` holds the terminal until you stop it, so run it\n# in the background — the curl below reads $TOKEN from this same shell.\nexport TOKEN=$(openssl rand -hex 16)\nloam serve --http --home ./my-store --token \"$TOKEN\" --port 4321 &\n```\n\nThen write a note and read it back. Nothing was configured but the registration — the GraphQL\nsurface is generated from it:\n\n```sh\ncurl -s localhost:4321/default/graphql \\\n  -H \"authorization: Bearer $TOKEN\" -H \"content-type: application/json\" \\\n  -d '{\"query\":\"mutation { note(entity: \\\"note:groceries\\\", title: \\\"milk\\\") { title _hex } }\"}'\n\ncurl -s localhost:4321/default/graphql \\\n  -H \"authorization: Bearer $TOKEN\" -H \"content-type: application/json\" \\\n  -d '{\"query\":\"{ note(entity: \\\"note:groceries\\\") { title } }\"}'\n```\n\nRun `kill %1` when you are done. The quickstart backgrounds a server on port 4321; the suite\nbinds only ephemeral ports, so a forgotten server breaks nothing — it just keeps serving. To use\na second terminal instead, export the same `TOKEN` there — `serve` never prints it.\n\nA stock schema is an **ordinary registration**, never a shortcut past one: it crosses the same\ndoor, meets the same validation, and lands the same deltas as a file you wrote. Outgrow the shelf\nand you register your own by path — `loam register my-schema.json --home ./my-store`.\n[Schemas are data](#schemas-are-data) spells that file out stage by stage. The shelf itself is\nexported as `STOCK_SCHEMAS` if you would rather start from a working shape than a blank file —\nit is frozen through, so copy an entry (`structuredClone`) and edit the copy.\n\nOne thing the shelf does not decide for you: a stock shape reads **every author's claims, and\nevery author's strikes**. Its gather selects on the pointer alone — no `authoredBy` — and masks\nnegations with `drop`, so on a store that federates, a peer can both retract a field and set one.\nEvery stock prop is `pick byTimestamp desc`, so a peer's `Note.title` with a later timestamp wins\nyour view and keeps winning. Both halves need answering, and a trust mask alone answers only the\nstrikes: pass `authoredBy` to the gather so the outer select admits your operator only, or order\nthe props `byAuthorRank` so a stranger cannot outrank you. A store that federates writes its own\nbody. This is why the shelf is a starting point rather than a deployment.\n\n`loam serve` self-initializes: a fresh home mints (or, via `LOAM_SEED`, imports) an operator\nidentity, so a container serves with nothing but a token. Configuration is by flag or environment:\n\n| flag           | env          | meaning                                        |\n| -------------- | ------------ | ---------------------------------------------- |\n| `--home DIR`   | `LOAM_HOME`  | the store's home directory (default `.loam`)   |\n| `--token TOK`  | `LOAM_TOKEN` | the bearer token (required to serve)           |\n| `--port N`     |              | HTTP port (default 4321; `0` for ephemeral)    |\n| `--store PATH` |              | override the store file path                   |\n| `--seed HEX`   | `LOAM_SEED`  | import an operator seed instead of minting one |\n\nFour more flags open the store beyond loopback. They are off by default, and each one widens reach,\nso they are listed apart from the table above rather than buried in it:\n\n| flag                      | meaning                                                              |\n| ------------------------- | -------------------------------------------------------------------- |\n| `--host ADDR`             | the bind address (default `127.0.0.1` — loopback only; `0.0.0.0` opens the LAN) |\n| `--archive DIR`           | mirror every delta into a cold store inside the home                 |\n| `--public-url URL`        | the outside address this store is reached at — opens connector discovery |\n| `--oauth-allow-redirect O`| comma-separated origins a connector may redirect to (needs `--public-url`) |\n\n## The commands\n\n`loam <command> --help` describes any of these in full. The quickstart uses four of them; the rest\nexist and are easy to miss.\n\n| command    | what it does                                                                |\n| ---------- | --------------------------------------------------------------------------- |\n| `init`     | create a home — and at a terminal, a first user and a stocked shelf with it  |\n| `serve`    | boot a store and serve it (GraphQL + SSE + MCP over HTTP)                    |\n| `register` | define a schema from a file and register it in the home's store              |\n| `pull`     | land a peer's deltas — a live URL or a frozen offer file                     |\n| `federate` | open, list, adjust and sever federation channels                             |\n| `store`    | inspect a store                                                              |\n| `migrate`  | read an offer, re-express it in the current format, write it back            |\n| `user`     | provision a login user and manage role assignments                           |\n| `grant`    | read the ledger of every author with standing; grant and revoke              |\n| `client`   | mint and revoke non-interactive client credentials — a key, its grants, and a bearer in one motion |\n| `pen`      | provision a renderer pen: mint its seed, grant it write standing             |\n| `artifact` | ask whether a route may be published as an artifact, and what it could do    |\n| `repair`   | list and settle a store's quarantine                                         |\n| `slate`    | read the erasure slates staged over this store                               |\n| `erase`    | forget one delta at the bytes, on every tier, and leave a receipt             |\n| `tombstones` | read the receipts: which ids this store forgot, for whom, and why           |\n\n## The HTTP API\n\nA served store answers these doors per mount, behind a `Bearer` token:\n\n- **`POST /:mount/graphql`** — `{ query, variables? }` → `{ data, errors }`. Both queries and\n  mutations; the mutation acts as the token's identity. Every query field takes `asOf` (a\n  millisecond timestamp) and every view carries `_asOf` and `_forgotten` — the past as it stood,\n  confessing its lawful redactions (SPEC §26).\n- **`GET /:mount/subscribe?query=…`** — a `text/event-stream` (SSE). The query must be a\n  `subscription` operation (`subscription { plant(entity: \"…\") { height _hex _fromHex _changed } }`):\n  an initial snapshot, then one `data:` frame per change (`_fromHex → _hex`, `_changed`, and the\n  fields).\n- **`POST /:mount/mcp`** — an MCP JSON-RPC surface (`initialize`, `tools/list`, `tools/call`)\n  exposing `loam_query`, `loam_mutate`, `loam_register`, `loam_whoami`, `loam_docs`, and the\n  four `loam_federate_*` channel tools.\n- **`GET /:mount/whoami`** — who this door resolves the caller to be, and what standing the\n  ground currently grants them (SPEC §56). Answers the anonymous too, uniformly, saying in words\n  that reads are masked — an empty view for an unrecognized caller is not an empty store.\n- **`POST /:mount/register`** — `{ hyperschema: { name, alg?, body }, schema, roots, entity? }` →\n  `{ registered, lens, entity, bound }` (operator token only). The hyperschema-schema mutation mechanism, served:\n  the definition and its registration land as deltas, and the surface serves the new type\n  immediately. Republishing at the same entity evolves it. (An endpoint rather than a GraphQL\n  mutation because an empty store has no GraphQL surface to mutate through — this is how it\n  gains one.)\n- **`POST /:mount/append`** — `{ deltas: [wire deltas] }` → `{ accepted, duplicates }`. The\n  **non-custodial door**: a client signs its own deltas and presents them; the token\n  authenticates transport only, and each delta is authorized by its own verified author's\n  standing. The server never holds the key.\n- **`/:mount/rest/<v1|@hash>/<Schema>/<entity>`** — the REST/OpenAPI door, generated from the\n  same registrations as GraphQL (the `surface/` generator seam).\n- **`GET /:mount/federate`** — the store's published deltas as wire JSON (operator token only).\n\nA junk token is `401`; an unknown mount is `404` (only to the authenticated — an unauthenticated\ncaller cannot tell a real mount from a missing one). A missing token is `401` too, with two\ndeliberate exceptions: `whoami` answers the anonymous with its masked-reads sentence, and a mount\nwhose operator opened a public read surface (SPEC §12) serves anonymous reads on it.\n\n```sh\ncurl -s localhost:4321/default/graphql \\\n  -H \"authorization: Bearer $TOKEN\" -H \"content-type: application/json\" \\\n  -d '{\"query\":\"{ note(entity: \\\"note:groceries\\\") { title _hex } }\"}'\n```\n\n## Connectors — reaching a store from an MCP client\n\n`POST /:mount/mcp` serves MCP with a bearer token, which is enough for a local client. A hosted\nclient such as Claude cannot hold a bearer token, so it authenticates through the store's own OAuth\nauthorization server. Two flags open that door:\n\n```sh\nloam serve --http --home ./my-store --token \"$TOKEN\" \\\n  --public-url https://store.example \\\n  --oauth-allow-redirect https://claude.ai\n```\n\n`--public-url` is the address the outside world reaches, and it must be the address the client\ndials — the store publishes it in its discovery documents, so a mismatch stops the handshake before\nauthentication. `--oauth-allow-redirect` names the origins a connector may return to after consent.\nTogether they open discovery, dynamic client registration, consent, and token exchange; without\nthem the store serves MCP to bearer tokens only. At consent the person chooses where the\nconnection lives — one container under their name — and the exchange binds it there (SPEC §58):\nthe connection's writes land in an inbox pool inside that container, its reads resolve over that\ncontainer, and no store-wide grant is ever landed for it. Consenting again moves it.\n\nThe store must be reachable over **HTTPS**, terminated in front of Loam — a tunnel, a reverse\nproxy, or a funnel. Serve behind the terminator and name the public address with `--public-url`.\n\nTwo practical notes, both learned the hard way. Claude's custom connectors dial **port 443**\nregardless of the port in the URL, so the public address must be reachable there. And a store with\nusers refuses a non-loopback bind without HTTPS, because the login session cookie is `Secure` and a\nbrowser discards it otherwise — the refusal is deliberate and says so.\n\n**What a connector token can reach, stated plainly.** A connector's token resolves against the\n**whole mount**. Loam has no read verb today: reads are not scoped per user, so a connector\nauthorized against a store reads every lens and every delta that mount serves, and the same token\nopens `/graphql`, `/subscribe`, `/rest`, and `/append`. Grants scope **writes**, not reads. So\ninviting someone to connect to your store is inviting them to read all of it. Give a guest their own\nstore and federate, rather than a connection to yours, unless you mean to share everything.\n\n## Embedding the library\n\nEverything the CLI and server do is a small API you can drive directly.\n\n```ts\nimport { Gateway, MemoryBackend, SqliteBackend, entityGatherBody, serve } from \"@bombadil/loam\";\n\n// A store, governed by an operator seed. Omit the seed for an ungoverned local store.\nconst gateway = await Gateway.open(new SqliteBackend(\"./store.sqlite\"), { seed: operatorSeedHex });\n\n// Register a (HyperSchema, Schema) over the roots you want held live. The schema's body is a\n// rhizomatic term; the policy's props name the GraphQL fields and their shapes.\n// `entityGatherBody()` is the ordinary one — everything pointing at the root, bucketed by context.\n// It is a named constructor for the term \"Schemas are data\" spells out stage by stage below; reach\n// for `expandedGatherBody({ role, schema, reading })` when a field expands into a child's own view.\ngateway.register(\n  {\n    name: \"Plant\",\n    alg: 1,\n    body: entityGatherBody(),\n  },\n  {\n    props: new Map([[\"height\", { kind: \"pick\", order: { kind: \"byTimestamp\", dir: \"desc\" } }]]),\n    default: { kind: \"pick\", order: { kind: \"byTimestamp\", dir: \"desc\" } },\n  },\n  [\"plant:fern\"],\n  undefined, // claim templates, if the schema declares write shapes\n  [\"height\"], // writable: fields are read-only until named here (§21)\n);\n\n// Query returns a content-addressed snapshot: same deltas, any order, any machine → same _hex.\nconst result = await gateway.query(`{ plant(entity: \"plant:fern\") { height _hex } }`);\n\n// Serve it (multiple mounts, each a separate store; tokens map to identities).\nconst server = await serve({\n  mounts: { default: gateway },\n  tokens: { [tokenHex]: { operator: true } },\n  port: 4321,\n});\n```\n\n`Gateway.boot(backend, genesis)` opens a store already governed and registered from a genesis\ndelta-set (`assembleGenesis({ operatorSeed, registrations, grants })`). `register` binds in this\nprocess only; `publishRegistration` (and the genesis) lands the schema **as deltas**, so a\nreopened store grows its surface back with no re-registration code.\n\n## Schemas are data\n\nA schema is not configuration — it is DEFINED by deltas, like everything else. Registering a\nschema lands two of them:\n\n- a **definition** — rhizomatic's hyperschema-schema claims (`publishHyperSchemaClaims` shape: name,\n  alg, and the body as canonical CBOR) filed at a hyperschema entity, `hyperschema:<Name>` by default;\n- a **registration** — a reference under `loam.registration`: a `hyperschema` pointer to that\n  entity, the `schema` (the resolution program) as canonical JSON, and the roots. No schema body\n  rides it.\n\nThe GraphQL surface is **generated**: on boot (and after every publish) the gateway\nmeta-resolves each referenced entity via `loadSchema` over the surviving definitions. The\nconsequences are the whole point:\n\n- **Evolution is append.** Republish a definition at the same entity and the surface serves the\n  new shape. The schema's identity is the _entity_, not the name. Two of the three doors serve it\n  **live, with no restart** — `publishRegistration` and `POST /:mount/register`, both of which go\n  through the running gateway. **`loam register` does not.** It writes the deltas to the store\n  file, and a server already running answers from the memory it booted with, so it keeps serving\n  the old shape until you restart it. The CLI says so when it sees a live server.\n- **Deprecation is negation.** Negate a definition and its registration is unbound; the type\n  drops from the surface. Nothing is deleted; the store only learns.\n- **Foreign law stays inert.** In a governed store only operator-authored definitions and\n  registrations bind — a peer's federated definition merges as data and reshapes nothing, the\n  same discipline that keeps foreign grants powerless.\n- Streams subscribed before an evolution keep watching the shape they subscribed to; new\n  subscriptions see the new shape.\n\nThe `loam register` file — the **exact same shape** `POST /:mount/register` and the MCP\n`loam_register` tool take, so a registration is one object you can drop into a file, POST, or hand\nthe tool. It mirrors the parts: a **HyperSchema** (the gather) and a **Schema** (the resolution).\n\n```json\n{\n  \"hyperschema\": {\n    \"name\": \"Plant\",\n    \"alg\": 1,\n    \"body\": {\n      \"op\": \"group\",\n      \"key\": \"byTargetContext\",\n      \"in\": {\n        \"op\": \"select\",\n        \"pred\": { \"hasPointer\": { \"targetEntity\": { \"var\": \"root\" } } },\n        \"in\": { \"op\": \"mask\", \"policy\": \"drop\", \"in\": \"input\" }\n      }\n    }\n  },\n  \"schema\": {\n    \"props\": { \"height\": { \"pick\": { \"order\": { \"byTimestamp\": \"desc\" } } } },\n    \"default\": { \"pick\": { \"order\": { \"byTimestamp\": \"desc\" } } }\n  },\n  \"roots\": [\"plant:fern\"],\n  \"writable\": [\"height\"]\n}\n```\n\n**Anatomy of a registration.** Four parts — the whole read pipeline lives in the first three, and\nthe fourth is the only door out:\n\n- **`hyperschema`** — the gather program:\n  - **`name`** — the GraphQL field it generates (`{ plant(entity: …) }`) and the default entity\n    (`hyperschema:Plant`). Identity is the entity, not the name — rename freely by republishing at it.\n  - **`alg`** — the L2 **algebra version** the `body` is written against (_not_ a signing algorithm).\n    There is one algebra today, so this is always `1`; it exists so a v1 body keeps its v1 meaning\n    if the algebra ever grows a v2.\n  - **`body`** — a rhizomatic **gather term**, evaluated once per root. It selects and buckets the\n    relevant deltas; a pure function of the ambient root, so it resolves the same on every machine.\n- **`schema`** — the resolution program (a **Schema**): a per-property reduction — each prop names a\n  GraphQL field and says how to fold that bucket's deltas into one value. Each prop's rule is a\n  **Policy**; the map of them is the Schema. A Policy's JSON holds exactly one of five kinds:\n  - **`pick`** — the newest (or, per `order`, the first) surviving entry:\n    `{ \"pick\": { \"order\": { \"byTimestamp\": \"desc\" } } }`.\n  - **`all`** — every surviving entry, in order:\n    `{ \"all\": { \"order\": { \"byTimestamp\": \"desc\" } } }`.\n  - **`merge`** — the bucket reduced by an addend function — `max`, `min`, `sum`, `count`,\n    `and`, `or`, `concatSorted`:\n    `{ \"merge\": \"sum\" }`.\n  - **`conflicts`** — the surviving entries whose authors disagree, in order:\n    `{ \"conflicts\": { \"order\": { \"byTimestamp\": \"desc\" } } }`.\n  - **`absentAs`** — a constant to stand in when the bucket is empty, then the Policy for when it\n    is not: `{ \"absentAs\": { \"const\": 0, \"then\": { \"pick\": { \"order\": { \"byTimestamp\": \"desc\" } } } } }`.\n  An `order` is `{ \"byTimestamp\": \"desc\" | \"asc\" }`, `{ \"byAuthorRank\": [ … ] }`,\n  `{ \"byPred\": { \"pred\": …, \"then\": … } }`, `{ \"chain\": [ … ] }`, or the bare `\"lexById\"`.\n- **`roots`** — the entities held **live**: the gather runs for each, and its view stays current\n  as deltas arrive.\n- **`writable`** — the fields that accept a **surface write**. Immutable by default (SPEC §21): a\n  field is read-only until named here, and omitting the list entirely leaves the whole schema\n  read-only — which is why the example names `height`, the field its mutation writes.\n\nThe `hyperschema.body` reads inside-out, each stage feeding the next:\n\n1. **`mask` / `drop` over `input`** — `input` is the store's whole delta set; `drop` applies\n   retractions and passes on only the deltas still standing. (Nothing is erased — a retraction is\n   just another delta the mask honors.)\n2. **`select` … `hasPointer { targetEntity: { var: root } }`** — keep only deltas that carry a\n   pointer **at the current root** (`plant:fern`). `{ var: root }` is the ambient entity the gather\n   is running for.\n3. **`group` / `byTargetContext`** — for each surviving delta, file it under the **context** label\n   of the pointer that targets the root. A delta pointing at `plant:fern` with context `height`\n   lands in the `height` bucket. The result is a hyperview: one root, its buckets.\n\nThen the **Schema** folds each bucket. `height` and the `default` both `pick` the entry with the\nnewest timestamp (`order: byTimestamp desc`), so `plant(entity: \"plant:fern\") { height }` returns\nthe latest recorded height and drops the rest. Add a `width` prop and you'd surface that bucket\ntoo; leave it out and the bucket stays gathered but unread.\n\n`loam register` writes to the home's store directly, so run it before `loam serve` (the store is\nsingle-writer); a running server takes the same registration over `POST /:mount/register`.\n\n## Writes are claims\n\nA relation is one delta with many pointers — \"Miles hosted a screening of The Matrix with Wren\nand Sally on July 4\" is ONE fact filing simultaneously into four entities' views. The schema\ndeclares its write shapes as **claim templates** (data, traveling in the registration beside\nthe read program), and each template becomes a GraphQL mutation that emits exactly one signed\ndelta:\n\n```jsonc\n// in the register file/body, beside hyperschema/schema/roots:\n\"mutations\": {\n  \"hostScreening\": {\n    \"pointers\": [\n      { \"role\": \"host\",  \"at\": { \"arg\": \"host\" },   \"context\": \"events_hosted\" },\n      { \"role\": \"film\",  \"at\": { \"arg\": \"film\" },   \"context\": \"screenings\" },\n      { \"role\": \"guest\", \"at\": { \"arg\": \"guests\" }, \"context\": \"events_attended\", \"each\": true },\n      { \"role\": \"date\",  \"value\": { \"arg\": \"date\" } }\n    ]\n  }\n}\n```\n\n```graphql\nmutation {\n  hostScreening(host: \"person:miles\", film: \"film:the-matrix\",\n                guests: [\"person:wren\", \"person:sally\"], date: \"2026-07-04\") { delta }\n}\n```\n\nBecause templates travel with the schema, everyone who adopts a published schema **emits\nbyte-compatible facts** — the schema is a protocol, not just a lens. Each template is\ntrial-proven at registration: a mutation whose writes its own reads could never see is refused.\nFor shapes no template anticipated there is the generic **`_claim(pointers: […]) { delta }`**;\nfor clients that keep their own keys there is `POST /:mount/append`. The old primitive-prop\nmutations (`plant(entity:…, height: 4)`) remain as convenient sugar.\n\n**Removing a value is retraction, not `set(null)`** (SPEC §14). Writing is the dual of reading: a\nfield is a bucket resolved per-Policy, so to clear one you negate your OWN contributions to it and\nit re-resolves — the next `pick` steps up, an `all` list loses your tag, a `merge` withdraws your\naddend, and a field only you spoke for goes absent (rendered per its `absentAs`, so the null-ness\nlives in the lens, never on a reference). GraphQL exposes `clear<Type>(entity, fields: […])`; the\nREST door maps it to `DELETE /:mount/rest/vN/<Schema>/<entity>`. Retract-your-own is the whole\nreach: a clear never touches another author's claim — to keep others' claims out of a view you\nnarrow the schema Policy, not the ground.\n\nTo withdraw ONE value rather than a whole field there is `remove<Type>(entity, field, values: […])`\n(REST: a `DELETE` with an object body `{ field: [values] }`) — the one tag you added, a specific\n`merge` addend, the rest of the field left standing. Which fields accept a write at all is the\nregistration's **`writable`** list, and the posture is **immutable by default** (SPEC §21): only the\nfields it names accept a surface write, and the rest are read-only — assert, clear, and remove refuse\nthem with a reason, and a read-only prop is offered as no mutation argument at all. Omit `writable`\nand NOTHING is writable; silence means \"you may not\". It disciplines the front door, never the\nground — a reader who wants a hard guarantee still enforces it with a lens.\n\nEvery view also carries two content addresses: **`_hex`** (the resolved view — the answer) and\n**`_hviewHex`** (the gathered hyperview — the evidence). Two lenses over the same body and root\nshare `_hviewHex` while their `_hex` differs exactly when their schemas adjudicate\ndifferently.\n\n## Capabilities: authors, not owners\n\nNo ambient authority — and no ownership of ids. **Entities are unowned**: a pointer is a string\nthat matches or doesn't, and a delta is never a free-floating fact about an entity — it is an\nassertion _from a perspective_ (a verified author, an instance of origin). Anyone with standing\nmay point at anything; whether anyone **listens** is the reader's business (schemas, author\nranks, admission predicates, the operator-filtered constitutional reads).\n\nWhat a governed store enforces is exactly one thing: **the author's standing on this\ninstance** — a surviving, operator-rooted `write` grant at the store entity (`loam:store`). It\nis a publishing relationship, not a truth relationship.\n\n- The **operator** (the gateway seed) needs no grant and roots the chain; an `admin` grant can\n  mint further grants and retire them (revocation is negation; audit is a query).\n- A gateway opened without an operator seed is an **ungoverned local store** (any verified\n  delta is welcome); one with an operator asks for standing from everyone else.\n- Constitutional shapes stay honest: a grant-shaped delta from a non-admin _lands_ (writes are\n  open) and _binds nothing_ (effectiveness chains root in the operator) — the same discipline\n  that keeps federated foreign law inert.\n\n```ts\nimport { grantClaims, STORE_ENTITY } from \"@bombadil/loam\";\nimport { signClaims } from \"@bombadil/rhizomatic\";\n\nawait gateway.append([\n  signClaims(grantClaims(STORE_ENTITY, aliceAuthor, \"write\", operator, ts), operatorSeed),\n]);\n// Alice may now write — about anything, acting as herself:\nawait gateway.query(`mutation { plant(entity: \"plant:fern\", height: 40) { height } }`, undefined, {\n  actor: aliceSeedHex,\n});\n```\n\n**Negations, governed.** A negation is an assertion like any other — _whose negations a reader\nhonors_ is lens policy. A plain `mask drop` body honors every negation present (the honest\ndefault when community strikes should bind unconditionally). For a governed lens, use\n`governedGatherBody(operator)`: its mask trusts only the operator and the operator's direct\ngrantees — resolved as a **live view over the grant deltas themselves** — so a federated\nstranger's strike is inert, a community member's binds, and revoking their grant un-binds\ntheir strikes on the very next read. `tenantSchemaFor(operator)` applies the same discipline\nto the audit view (operator + operator-minted admins). The trusted sets reach **one link** of\nthe grant chain: standing minted by an admin binds enforcement (`holdsGrant` recurses fully)\nbut never enters a lens's trusted set, and an admin's revocation bars the door without by\nitself shrinking the trusted sets — the operator's signature is what the lenses read.\n`pullFrom`'s `admit` predicate remains the coarse boundary at the federation door.\n\n## Derived functions (the runner)\n\nFunction _definitions_ live in the store as data; a **runner** — a peer client — reads them,\ninstalls each into a derivation host with an implementation it holds, and animates the gateway so\nthey fire on ingest. A store with definitions but no runner is passive; attach a runner and it\ncomputes. In a governed store, only the operator's blessed definitions run.\n\n```ts\nimport { Runner } from \"@bombadil/loam\";\nRunner.attach(gateway, { seed: runnerSeedHex, implementations: { \"fn:avgHeight\": avgHeight } });\n```\n\n## Federation\n\nTwo instances meet and merge — union, order-blind, conflict-free — over the authed HTTP surface.\n\n```ts\nimport { pullFrom } from \"@bombadil/loam\";\n// pull a peer's published deltas into the local store; verify + merge, idempotent\nawait pullFrom(localGateway, \"https://peer.example/default\", peerOperatorToken);\n```\n\n**What a store admits is data.** One operator-signed declaration at `loam:trust` sets the\ndoor's posture — `open` (admit everything that verifies; the default, and the aggregator's\nstance), `roster` (the operator plus named authors), or `closed`. `pullFrom` and `federate`\nresolve the policy **live from the store's own deltas on every pull**: a roster edit is a\ndelta, the next pulse obeys it, and the history of who was trusted when is a query. A fresh\ndeclaration only _adds_ to the roster; removal is negation — strike the declaration that\nadmitted them. The same roster reaches read-time masks via `trustRosterPred(operator)` (an\n`inView` over the very same declaration deltas), so admission and resolution share one source\nof truth. An explicit `admit` predicate always overrides.\n\n```ts\nimport { trustClaims } from \"@bombadil/loam\";\n// the aggregator turns selective with one delta:\nawait gateway.append([signClaims(trustClaims(\"roster\", [alice, bob], operator, ts), seed)]);\n```\n\nA store publishes everything, or what its `offeredLens` (a term) selects. **Federation is union,\nnot a governed write:** a peer's deltas cross by signature verification alone, and whether they\nshape a local view is a read-time trust choice (a policy's `byAuthorRank`) — never a write denial.\nForeign law stays inert: a peer's self-signed grant merges as a delta but governs nothing, because\nit roots in no operator you blessed. **Each instance must have its own operator seed** — two\nsharing one trust each other's constitution completely.\n\n**The CLI recipe, end to end.** Pulling is one step, not the story. A governed store binds only\nits own operator's law, so the first query after a pull answers `nothing is registered` — the\nrefusal says the store holds registrations that do not bind, foreign law is inert. That is the\ndesign, not a bug: foreign law never reshapes your surface. The recipe that works end to end:\n\n```sh\nTOKEN=$(openssl rand -hex 16)                                                # THIS store's door token — minted here\nloam pull http://peer.example/default --token \"$PEER_TOKEN\" --home ./mine   # their deltas, yours now\n#   ^ PEER_TOKEN is the PEER's operator token — their door's secret, not yours to mint. Ask them.\nloam register plant.json --home ./mine                                      # your own schema, binding here\nloam serve --http --home ./mine --token \"$TOKEN\"                            # serve your ground\ncurl -s localhost:4321/default/graphql -H \"authorization: Bearer $TOKEN\" \\\n  -d '{\"query\":\"{ plant(entity: \\\"plant:fern\\\") { height } }\"}'\n```\n\nThe pull makes their facts live in your store; the register makes a lens you own; the serve\nanswers it. A store that pulls and never registers gathered someone else's world with no way to\nread it — the empty-surface refusal is the honest report of exactly that.\n\n### Channels — federation that carries law, not only bytes\n\nA **channel** is federation between two containers. You name a container to receive into and assign\nthe peer a **prefix**; their deltas land in a nested pool inside that container, and law that\narrives binds under your prefix. You register nothing.\n\n```sh\nloam federate open --from https://peer.example/default --into friends --prefix alice --token \"$PEER_TOKEN\"\nloam federate list\nloam federate set --channel channel:friends:alice --bless false     # reversible\nloam federate drop --channel channel:friends:alice --yes            # not reversible\n```\n\nThen `alice_Note` answers on your own surface, from alice's law, with your operator key. The prefix\nis **yours** — the peer never chooses it, so no peer can take a name your store already serves, and\na prefix that would collide at the GraphQL door is refused when you assign it.\n\nTwo toggles, both reversible and both read live from the ground: `--receiving false` freezes the\nchannel and keeps everything already received; `--bless false` stops new law binding and leaves law\nalready bound serving. Severing is `drop`, which purges that peer's pool at the bytes and leaves\nevery other channel whole.\n\n**What a channel gets right, now.** Each of these was once on the not-yet list and is landed\nwith rails (spec §46, §47):\n\n- **The standing sync survives a restart.** A channel's address rides its record and its token\n  lives in the home at 0600, so `loam serve` rebuilds its channels at boot and says how many it\n  is syncing. A channel it cannot resume is named on stderr rather than left in the list\n  reporting `receiving`.\n- **Two peers publishing byte-identical law both bind.** You and a friend can both start from\n  `loam register --stock note`; `alice:Note` and `bob:Note` each answer with their own peer's\n  data, under names you assigned.\n- **A peer's sibling lenses arrive as siblings** — two readings over one definition each serve\n  their own resolution under their own name.\n- **A binding lives in the pool it was blessed into**, so severing takes the peer's law with the\n  peer's data — nothing to retire, and a bystander channel's names keep serving.\n- **A peer's app arrives inert, and one act mounts it.** `federate list` names every app a channel\n  has received — route, bundle id, and whether this store runs it — and `federate bless-app\n  --channel <name> --route <route>` is the only thing that mounts one. Neither toggle above reaches\n  it: `blessing` governs NAMES, and running a stranger's code is a wider grant that takes its own\n  act. A mounted app serves under the channel's prefix, from the channel's own pool, behind the\n  probation frame, with its writes sequestered there — and dropping the channel takes it away.\n- **A peer's computed fields refuse until you run their code.** A registration whose values come\n  from the peer's own resolver code binds with that code WITHHELD: the fields answer with a reason\n  naming the act that supplies them, and `bless-app --resolvers <lens>` is that act.\n\n**What mounting a peer's app does not bound.** The pool bounds what that app may WRITE to your\nstore. It does not bound what its code may REACH: a bundle can open a socket or read the filesystem\nof the machine you run this on. And only the app's RENDER runs in a worker with a time and memory\nlimit — its module body is evaluated on the serving thread, when you bless it and again the first\ntime a process is asked for it. Mount a peer's app the way you would run their program (§24.5, an\nopen flag).\n\n**What channels do NOT do yet.** Each of these is real today, and each has a ticket:\n\n- **Federating still costs a peer your operator token.** `GET /:mount/federate` demands it, and that\n  token also registers root law, mints grants and reads everything. A container-scoped offer token\n  is designed and blocked on where a runtime-issued credential should live (T196/T188 — T196's\n  restart half landed; its shard stays open for this decision).\n\n**Over MCP**, an agent gets `loam_federate_status`, `_connect`, `_set` and `_drop`, each scoped by a\n`federate` grant naming one container. `_drop` **stages only**: it returns a link and a preview of\nwhat would go and what would remain, and purges nothing. A person completes the sever in the browser,\nbehind a session an agent cannot obtain.\n\n## Forgetting — erasure, GDPR, and harmful content\n\nBy default a store forgets nothing: revocation is negation, which _masks_ a delta from views but\nkeeps it in the ground, so the audit — who said what, when, and what was later withdrawn —\nsurvives. That is the right default for a store of record. But grow-only cannot be the _only_\nanswer. A data subject exercises their right to erasure; a delta is later judged unlawful or\nharmful; and the bytes must actually go.\n\n**Erasure is a real, destructive operation, and it is the instance operator's alone.** Only the\noperator — the data controller — may order a record removed: not its author, not a grantee, not a\npeer. The substrate cannot stop anyone from _minting_ a delta, so the store is careful never to\n_accept_ a removal-order it did not sign; the check runs at every door, append and federation\nalike.\n\n```ts\n// the operator honors a request: purge the bytes from every tier, leave a signed hole\nawait gateway.erase(deltaId, { reason: \"GDPR art. 17 request #4821\" });\n```\n\n`erase` removes the delta from the live store **and every backing tier** — the sqlite, and the\narchive vault if one is configured (a later heal will not replant it) — then re-seats the store\non what remains. What stays is a **tombstone**: a signed, append-only claim recording _that_ the\nid was forgotten, by whom, and when — never the content. The store remembers that it forgot. The\ndoor refuses the id's return thereafter (un-erasure is striking the tombstone). Content addressing\nis what makes this honest: retaining a hash retains zero bytes.\n\nThe same machinery has a terminal surface, so a compliance officer needs no script:\n\n```sh\nloam slate list --home ./mine        # what is staged for erasure, who asked, and when it is due\nloam erase <deltaId> --reason \"GDPR art. 17 request #4821\" --home ./mine\nloam tombstones list --home ./mine   # every receipt: which id, whose record, when, and why\nloam tombstones show <id> --home ./mine\n```\n\n`--reason` is required and has no default: the receipt is all that outlives the record, and one\nthat cannot say why is a receipt made less honest. If this home has a cold archive its\n`config.json` does not name — `loam serve --archive vault` does not write the name there — `erase`\nspots it and refuses until you name it with `--archive`, rather than sweep the primary and report a\ncompleteness it never verified. A vault parked outside the home is beyond that check.\n\n**The boundary, stated plainly: erasure is instance-level — Loam cannot retroactively retract a\ndelta that has already federated to another instance.** The physics is email you have already\nsent, or a file already downloaded. Once a peer has pulled a delta it lives on _their_ ground,\nunder _their_ operator's authority; your erasure clears it from _your_ store and refuses its\nre-entry through _your_ door, but it does not reach across the network and delete other people's\ncopies. Nor should it — a system where one signature could cascade a deletion everywhere would be\na censorship weapon, not a store of record. So a forged or coerced erasure order cannot propagate\na deletion: each operator decides for their own ground.\n\nWhat Loam gives you instead is precise, auditable, per-instance forgetting, plus the machinery to\nmake erasure across a federation a _coordinated_ act rather than a magic one:\n\n- **The tombstone travels as a request.** It federates like any claim, so downstream operators\n  _learn_ that you erased — GDPR Art. 17(2)'s \"inform downstream controllers,\" done as data. Each\n  peer's operator then chooses to honor it on their own store.\n- **Compliance is queryable.** Ask any store for the id and see what it returns — erased and\n  refused, or still held. No ambiguity to argue about.\n- **Bad actors are shut out going forward** by the trust roster (above): close the door, and the\n  next pulse stops admitting them.\n\nThe honest limit: this is rigorous, controller-level erasure and severance — not the power to\nunsend. No federated system can promise network-wide recall. Loam makes the boundary crisp and the\nper-instance act exact, rather than pretending the boundary is not there.\n\n## Deploy\n\nA `Dockerfile` builds and runs `loam serve --http` as a non-root user, the store on a `/data`\nvolume:\n\n```sh\ndocker build -t loam .\ndocker run -e LOAM_TOKEN=<secret> -v loam-data:/data -p 4321:4321 loam\n```\n\nBind `127.0.0.1` and terminate TLS in front. **Hosted persistence is a driver, not an image\nchange**: the `StoreBackend` seam is five members — `append`, `deltasSince`, `purge`, `holds`,\n`close` — so a libSQL/Turso client drops in beside `SqliteBackend` with no other change. The two\nerasure members are not optional decoration: `purge` must remove bytes on every tier the driver\nowns and `holds` must answer from the bytes, failing closed — a driver that stubs them breaks\nerasure's completeness guarantee (SPEC §11) while looking healthy.\n\n### Cold storage\n\nA store can keep an **archive** — a cold mirror written in the same appends:\n\n```sh\nloam serve --http --archive /mnt/backup/vault    # or add \"archive\": \"vault\" to config.json\n```\n\nThe archive is a directory of canonical delta files, one per delta, named by its content\naddress (`<id[0..2)>/<id>.json`). Plain file tools are backup tools here: rsync it, tar it,\ncopy it to a USB stick — copying files between two archives *is* replication, because merge is\nunion and the id is the name. The CRDT is what keeps this honest: a lagging copy is merely\nbehind, never wrong, so an unreachable archive never takes the store down (the lag is logged,\nloudly) and every serve heals the pair by two-way union before it boots. Which means restore\nafter disaster is no procedure at all: delete the lost sqlite and serve again — the archive\nreplants it. Embedders get the same pieces as values: `MirrorBackend(primary, mirror)` and\n`ArchiveBackend(root)`.\n\n## Migrations\n\nA store is grow-only and content-addressed, so a signed delta can never be rewritten — which makes\na breaking change to the on-wire format something you migrate to, not patch in place. Loam ships a\nmigration for every such change (a standing rule), and it supersedes rather than rewrites:\n\n```sh\nloam init --home ./store --seed <the store's original seed>   # re-signing is the operator's own hand\nloam migrate my-export.json --out migrated.json               # old deltas in, new deltas out\n```\n\nFor each delta a format change touched, the migration **re-signs** it into the new form and\n**negates** the original with a negation that points `supersededBy` at its replacement and records a\nreason — so the history reads as a linked chain of supersessions, nothing lost. It is idempotent\n(re-running adds nothing) and composes across versions. See [SPEC §20](SPEC.md).\n\n## How the repo is organized\n\n**Source.** `src/` is the library and CLI, split by seam: `gateway/` (the store's surface —\nGraphQL, mutations, registrations, accounts & capabilities, trust, erasure), `store/` (the\n`StoreBackend` drivers — sqlite, archive/mirror, localStorage), `server/` (the HTTP server\nitself — every door, MCP, login and OAuth, the admin pages), `surface/` (surfaces as\nmaterializations — the GraphQL and REST/OpenAPI doors from one generator seam), `federation/`\n(offer / pull / wire / translate), `runner/` (derived functions), `migrate/` (format migrations —\nold deltas in, new deltas out), `stock/` (the schema shelf `init` and `register --stock` read),\n`cli/`, and `browser/` + `client/` (the full in-page store and the\nread-only public client). `test/` mirrors that tree;\n[`demos/`](demos/README.md) holds the [tutorial](https://bombadil-labs.github.io/loam/tutorial.html) and the\nvillage.\n\n**The docs, by role — they don't overlap:**\n\n- **[README.md](README.md)** — this file: the manual (what Loam is, how to use it).\n- **[SPEC.md](SPEC.md)** + **[`spec/`](spec/)** — the design, and the record of what **is**: one\n  file per shipped capability under `spec/` (`NN-slug.md`), each closed by a `**Provenance.**`\n  footer linking the PR(s) that landed it and naming where it lives; `SPEC.md` is the index over\n  them. Read it to understand the system; it grows only when work lands — a landing adds a new\n  `spec/` file.\n- **`.adlc/tickets/`** — the backlog: unbuilt and partially-designed work, as ADLC tickets, one shard each.\n  The next thing to build is drawn from here, and its landing PR adds its `spec/` section file.\n- **[JOURNAL.md](JOURNAL.md)** — the append-only record: one entry per step, what was done and why.\n  An index over [`journal/`](journal/), one file per entry.\n- **[CLAUDE.md](CLAUDE.md)** — the process this repo runs by (the ADLC lifecycle).\n\n## Development\n\n```sh\nnpm run check   # format + lint + typecheck + build + all tests — the green gate\nnpm test        # tests only\n```\n\n## Releasing\n\n```sh\nnpm run release -- patch   # or minor / major\n```\n\nFrom a clean, up-to-date `main` only: runs the gate, bumps the version (syncing the in-source\nconstants), commits, tags `vX.Y.Z`, and pushes. The `release` GitHub Actions workflow picks up\nthe tag, runs the gate again, verifies the tag agrees with `package.json`, publishes\n`@bombadil/loam` to npm, and cuts a GitHub release with generated notes. A tag that lies about\nthe version refuses to publish.\n\nPublishing is tokenless — npm **trusted publishing** (OIDC): npm verifies that this repo's\n`release.yml` workflow minted the release, and provenance is generated automatically. There is\nno publish token to leak, rotate, or expire. (The one bootstrap exception: npm can only trust a\npackage that exists, so the very first publish was made locally by the author.)\n\nThe process this repo runs by is in [CLAUDE.md](CLAUDE.md).\n\n## License\n\nLicensed under either of\n\n- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)\n- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)\n\nat your option.\n\nUnless you explicitly state otherwise, any contribution intentionally submitted for inclusion in\nthis work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without\nany additional terms or conditions.\n","readmeFilename":"README.md"}