{"_id":"@seanmozeik/supabash-fs","_rev":"9-4af76ceb5827309bc9bf9414d3a7861d","name":"@seanmozeik/supabash-fs","dist-tags":{"latest":"0.7.0"},"versions":{"0.1.0":{"name":"@seanmozeik/supabash-fs","version":"0.1.0","keywords":["deno","edge","filesystem","just-bash","supabase","virtual-filesystem"],"author":"Sean Lees","license":"MIT","_id":"@seanmozeik/supabash-fs@0.1.0","maintainers":[{"name":"seanmozeik","email":"sean@mozeik.com"}],"dist":{"shasum":"80439344773473f22bca8ac0fdedd1e772dd04a2","tarball":"https://registry.npmjs.org/@seanmozeik/supabash-fs/-/supabash-fs-0.1.0.tgz","fileCount":23,"integrity":"sha512-RrOQjaLTW106Rl28u5Fi6HNYGPZ0uZfOnCzEK+aPnrF33YHUcCVdxmkaX6MO1y7E9xIDcpe4CsdZJk5PaIfpXw==","signatures":[{"sig":"MEUCIHuz6yP2SrFRk9huXYtEszTa/tGipRR2IupvoZqadbcUAiEA7HKdZD2f1uQ5n6yPRKK0Geyv4SXdSUKUdc2aBTqtrZY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":55311},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","shasum":"80439344773473f22bca8ac0fdedd1e772dd04a2","engines":{"bun":">=1.4.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./package.json":"./package.json"},"scripts":{"fix":"oxfmt --write . && oxlint --type-aware --type-check --deny-warnings --fix .","lint":"oxlint --type-aware --type-check --deny-warnings .","test":"vitest run --reporter=minimal","build":"bun run build:clean && bun run build:js && bun run build:types","check":"bun run format:check && bun run lint && bun run typecheck && bun run source:size","format":"oxfmt --write .","prepack":"bun run build && bun run build:smoke","build:js":"bun build ./src/api/supabash.ts --outfile ./dist/index.js --target browser --format esm --packages external","typecheck":"tsc --noEmit","check:deno":"deno check --config deno.json tests/deno/live-supabase.ts","build:clean":"bun ./scripts/clean-dist.ts","build:smoke":"bun ./scripts/check-built-package.mjs","build:types":"tsc -p tsconfig.build.json","source:size":"bun ./scripts/check-source-size.ts","format:check":"oxfmt --check .","package:check":"bun pm pack --dry-run","audit:production":"bun audit --production","package:consumer":"bun ./scripts/check-packed-consumer.ts"},"_npmUser":{"name":"seanmozeik","email":"sean@mozeik.com"},"_integrity":"sha512-RrOQjaLTW106Rl28u5Fi6HNYGPZ0uZfOnCzEK+aPnrF33YHUcCVdxmkaX6MO1y7E9xIDcpe4CsdZJk5PaIfpXw==","_npmVersion":"10.8.3","description":"Authenticated Supabase Storage filesystem for Just Bash.","directories":{},"sideEffects":false,"_nodeVersion":"26.3.0","dependencies":{"@supabase/supabase-js":"2.112.4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"bun@1.4.0","devDependencies":{"oxfmt":"0.64.0","oxlint":"1.79.0","vitest":"4.1.11","just-bash":"3.4.2","@types/bun":"1.4.0","typescript":"7.0.2","@types/node":"26.2.0","oxlint-tsgolint":"7.0.2001","@seanmozeik/de-clank":"0.1.7"},"peerDependencies":{"just-bash":">=3.4.2 <4"},"_npmOperationalInternal":{"tmp":"tmp/supabash-fs_0.1.0_1787827635518_0.9762508445621667","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@seanmozeik/supabash-fs","version":"0.2.0","keywords":["apply-patch","deno","edge","filesystem","just-bash","supabase","virtual-filesystem","workspace"],"author":"Sean Lees","license":"MIT","_id":"@seanmozeik/supabash-fs@0.2.0","maintainers":[{"name":"seanmozeik","email":"sean@mozeik.com"}],"dist":{"shasum":"b24c360f8f4edea449f186aef342af63704ccf83","tarball":"https://registry.npmjs.org/@seanmozeik/supabash-fs/-/supabash-fs-0.2.0.tgz","fileCount":92,"integrity":"sha512-Ca43sX7BGmE891h3eFa31VEwYvxf5py1YZFCigA6140shBhm6Q6BtF4c9XQkq0RMT8MFXfFcpBjJA/kNEmkcNA==","signatures":[{"sig":"MEQCIApT+kvL4puDiULF9v32pq+C+/+XLJ59pIfLpivSXrrhAiAj6kG8GUvoE5loAh7hIhPFHp+N2Q6q/8O3FrLakF3hAA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":302516},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","shasum":"b24c360f8f4edea449f186aef342af63704ccf83","engines":{"bun":">=1.4.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./ai-sdk":{"types":"./dist/ai-sdk/index.d.ts","import":"./dist/ai-sdk.js"},"./package.json":"./package.json"},"scripts":{"fix":"oxfmt --write . && oxlint --type-aware --type-check --deny-warnings --fix .","lint":"oxlint --type-aware --type-check --deny-warnings .","test":"vitest run --reporter=minimal","build":"bun run build:clean && bun run build:js && bun run build:types","check":"bun run format:check && bun run lint && bun run typecheck && bun run source:size","format":"oxfmt --write .","prepack":"bun run build && bun run build:smoke","build:js":"bun run build:js:root && bun run build:js:ai-sdk","typecheck":"tsc --noEmit","check:deno":"deno check --minimum-dependency-age=0 --sloppy-imports --config deno.check.json tests/deno/live-supabase.ts tests/deno/ai-sdk.ts","build:clean":"bun ./scripts/clean-dist.ts","build:smoke":"bun ./scripts/check-built-package.mjs && bun ./scripts/check-built-ai-sdk.mjs","build:types":"tsc -p tsconfig.build.json","source:size":"bun ./scripts/check-source-size.ts","format:check":"oxfmt --check .","build:js:root":"bun build ./src/index.ts --outfile ./dist/index.js --target browser --format esm --packages external","package:check":"bun pm pack --dry-run","build:js:ai-sdk":"bun build ./src/ai-sdk/index.ts --outdir ./dist --entry-naming ai-sdk.js --chunk-naming ai-sdk-chunks/[name]-[hash].js --splitting --target browser --format esm --packages external","audit:production":"bun audit --production","package:consumer":"bun ./scripts/check-packed-consumer.ts && bun ./scripts/check-packed-ai-sdk-consumer.ts && bun ./scripts/check-packed-deno-consumer.ts"},"_npmUser":{"name":"seanmozeik","email":"sean@mozeik.com"},"_integrity":"sha512-Ca43sX7BGmE891h3eFa31VEwYvxf5py1YZFCigA6140shBhm6Q6BtF4c9XQkq0RMT8MFXfFcpBjJA/kNEmkcNA==","_npmVersion":"10.8.3","description":"Authenticated Supabase Storage workspace filesystem for Just Bash, Apply Patch, and durable revisions.","directories":{},"sideEffects":false,"_nodeVersion":"26.3.0","dependencies":{"@supabase/supabase-js":"2.112.4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"bun@1.4.0","devDependencies":{"ai":"7.0.83","oxfmt":"0.64.0","oxlint":"1.79.0","vitest":"4.1.11","bash-tool":"1.3.19","just-bash":"3.4.2","@types/bun":"1.4.0","typescript":"7.0.2","@types/node":"26.2.0","@ai-sdk/openai":"4.0.50","oxlint-tsgolint":"7.0.2001","@seanmozeik/de-clank":"0.1.7"},"peerDependencies":{"ai":">=7 <8","bash-tool":">=1.3.19 <2","just-bash":">=3.4.2 <4","@ai-sdk/openai":">=4 <5"},"peerDependenciesMeta":{"ai":{"optional":true},"bash-tool":{"optional":true},"@ai-sdk/openai":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/supabash-fs_0.2.0_1787866220914_0.08807301751649388","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@seanmozeik/supabash-fs","version":"0.3.0","keywords":["apply-patch","deno","edge","filesystem","just-bash","supabase","virtual-filesystem","workspace"],"author":"Sean Lees","license":"MIT","_id":"@seanmozeik/supabash-fs@0.3.0","maintainers":[{"name":"seanmozeik","email":"sean@mozeik.com"}],"dist":{"shasum":"fff515e01f00e5288bd12e32b9112e34f19b092a","tarball":"https://registry.npmjs.org/@seanmozeik/supabash-fs/-/supabash-fs-0.3.0.tgz","fileCount":108,"integrity":"sha512-jNPxAJtrQoDIyOf9m+VnwoeLQ+4JEhkhSj+879BQ80dvlx2tV1VfleZQ7WVpfhgkwRF80JS6eqWDcOl4Qzt93g==","signatures":[{"sig":"MEYCIQCUDk6pTC+looeOaWpn7nmc8fPLDGRHXvlgz/HiNKd0XwIhALFJfswRzJaniPZJ61U2YItEYmYzay/BILp4I8XMPo2n","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":438086},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","shasum":"fff515e01f00e5288bd12e32b9112e34f19b092a","engines":{"bun":">=1.4.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./ai-sdk":{"types":"./dist/ai-sdk/index.d.ts","import":"./dist/ai-sdk.js"},"./package.json":"./package.json","./postgres/remove.sql":"./sql/postgres/0001_remove.sql","./postgres/install.sql":"./sql/postgres/0001_install.sql"},"scripts":{"fix":"oxfmt --write . && oxlint --type-aware --type-check --deny-warnings --fix .","lint":"oxlint --type-aware --type-check --deny-warnings .","test":"vitest run --reporter=minimal","build":"bun run build:clean && bun run build:js && bun run build:types","check":"bun run format:check && bun run lint && bun run typecheck && bun run source:size","format":"oxfmt --write .","prepack":"bun run build && bun run build:smoke","build:js":"bun run build:js:root && bun run build:js:ai-sdk","typecheck":"tsc --noEmit","check:deno":"deno check --minimum-dependency-age=0 --sloppy-imports --config deno.check.json tests/deno/live-supabase.ts tests/deno/ai-sdk.ts tests/deno/postgres-live.ts tests/postgres/edge-smoke/index.ts","build:clean":"bun ./scripts/clean-dist.ts","build:smoke":"bun ./scripts/check-built-package.mjs && bun ./scripts/check-built-ai-sdk.mjs","build:types":"tsc -p tsconfig.build.json","source:size":"bun ./scripts/check-source-size.ts","format:check":"oxfmt --check .","build:js:root":"bun build ./src/index.ts --outfile ./dist/index.js --target browser --format esm --packages external","package:check":"bun pm pack --dry-run","build:js:ai-sdk":"bun build ./src/ai-sdk/index.ts --outdir ./dist --entry-naming ai-sdk.js --chunk-naming ai-sdk-chunks/[name]-[hash].js --splitting --target browser --format esm --packages external","audit:production":"bun audit --production","package:consumer":"bun ./scripts/check-packed-consumer.ts && bun ./scripts/check-packed-ai-sdk-consumer.ts && bun ./scripts/check-packed-deno-consumer.ts","test:postgres:live":"bash ./scripts/run-postgres-integration.sh"},"_npmUser":{"name":"seanmozeik","email":"sean@mozeik.com"},"_integrity":"sha512-jNPxAJtrQoDIyOf9m+VnwoeLQ+4JEhkhSj+879BQ80dvlx2tV1VfleZQ7WVpfhgkwRF80JS6eqWDcOl4Qzt93g==","_npmVersion":"10.8.3","description":"Authenticated Supabase Storage and Postgres workspace filesystems for Just Bash, Apply Patch, and durable revisions.","directories":{},"sideEffects":false,"_nodeVersion":"26.3.0","dependencies":{"@supabase/supabase-js":"2.112.4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"bun@1.4.0","devDependencies":{"ai":"7.0.83","oxfmt":"0.64.0","oxlint":"1.79.0","vitest":"4.1.11","bash-tool":"1.3.19","just-bash":"3.4.2","@types/bun":"1.4.0","typescript":"7.0.2","@types/node":"26.2.0","@ai-sdk/openai":"4.0.50","oxlint-tsgolint":"7.0.2001","@seanmozeik/de-clank":"0.1.8"},"peerDependencies":{"ai":">=7 <8","bash-tool":">=1.3.19 <2","just-bash":">=3.4.2 <4","@ai-sdk/openai":">=4 <5"},"peerDependenciesMeta":{"ai":{"optional":true},"bash-tool":{"optional":true},"@ai-sdk/openai":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/supabash-fs_0.3.0_1787948021399_0.18765675287410755","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@seanmozeik/supabash-fs","version":"0.4.0","keywords":["apply-patch","deno","edge","filesystem","just-bash","supabase","virtual-filesystem","workspace"],"author":"Sean Lees","license":"MIT","_id":"@seanmozeik/supabash-fs@0.4.0","maintainers":[{"name":"seanmozeik","email":"sean@mozeik.com"}],"dist":{"shasum":"04dbbeb89d6dddea8f83dce026a106800eda1f3c","tarball":"https://registry.npmjs.org/@seanmozeik/supabash-fs/-/supabash-fs-0.4.0.tgz","fileCount":111,"integrity":"sha512-zD1Q5IKsBrBjSyI9ujRL38Tvwmp4N4pORKHgT09tfU+0qvm42mCwyaTYhU0wBi7PSvwoIYuOe5n6dbh2LxfOZQ==","signatures":[{"sig":"MEQCIGLvFxvY8eqtU9H+83II1amIgtJLxjztZCHSdO6969JYAiAMfzqml1aAaCkUpi5WOJHAWtLOA8SMOU7KJIH+xF0AdQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":471727},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","shasum":"04dbbeb89d6dddea8f83dce026a106800eda1f3c","engines":{"bun":">=1.4.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./ai-sdk":{"types":"./dist/ai-sdk/index.d.ts","import":"./dist/ai-sdk.js"},"./package.json":"./package.json","./postgres/remove.sql":"./sql/postgres/0001_remove.sql","./postgres/install.sql":"./sql/postgres/0001_install.sql"},"scripts":{"fix":"oxfmt --write . && oxlint --type-aware --type-check --deny-warnings --fix .","lint":"oxlint --type-aware --type-check --deny-warnings .","test":"vitest run --reporter=minimal","build":"bun run build:clean && bun run build:js && bun run build:types","check":"bun run format:check && bun run lint && bun run typecheck && bun run source:size","format":"oxfmt --write .","prepack":"bun run build && bun run build:smoke","build:js":"bun run build:js:root && bun run build:js:ai-sdk","typecheck":"tsc --noEmit","check:deno":"deno check --minimum-dependency-age=0 --sloppy-imports --config deno.check.json tests/deno/live-supabase.ts tests/deno/ai-sdk.ts tests/deno/postgres-live.ts tests/postgres/edge-smoke/index.ts","build:clean":"bun ./scripts/clean-dist.ts","build:smoke":"bun ./scripts/check-built-package.mjs && bun ./scripts/check-built-ai-sdk.mjs","build:types":"tsc -p tsconfig.build.json","source:size":"bun ./scripts/check-source-size.ts","format:check":"oxfmt --check .","build:js:root":"bun build ./src/index.ts --outfile ./dist/index.js --target browser --format esm --packages external","package:check":"bun pm pack --dry-run","build:js:ai-sdk":"bun build ./src/ai-sdk/index.ts --outdir ./dist --entry-naming ai-sdk.js --chunk-naming ai-sdk-chunks/[name]-[hash].js --splitting --target browser --format esm --packages external","audit:production":"bun audit --production","package:consumer":"bun ./scripts/check-packed-consumer.ts && bun ./scripts/check-packed-ai-sdk-consumer.ts && bun ./scripts/check-packed-deno-consumer.ts","test:postgres:live":"bash ./scripts/run-postgres-integration.sh"},"_npmUser":{"name":"seanmozeik","email":"sean@mozeik.com"},"_integrity":"sha512-zD1Q5IKsBrBjSyI9ujRL38Tvwmp4N4pORKHgT09tfU+0qvm42mCwyaTYhU0wBi7PSvwoIYuOe5n6dbh2LxfOZQ==","_npmVersion":"10.8.3","description":"Authenticated Supabase Storage and Postgres workspace filesystems for Just Bash, Apply Patch, and durable revisions.","directories":{},"sideEffects":false,"_nodeVersion":"26.3.0","dependencies":{"yaml":"2.9.0","unbash":"4.0.10","@supabase/supabase-js":"2.112.4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"bun@1.4.0","devDependencies":{"ai":"7.0.83","oxfmt":"0.64.0","oxlint":"1.79.0","vitest":"4.1.11","bash-tool":"1.3.19","just-bash":"3.4.2","@types/bun":"1.4.0","typescript":"7.0.2","@types/node":"26.2.0","@ai-sdk/openai":"4.0.50","oxlint-tsgolint":"7.0.2001","@seanmozeik/de-clank":"0.1.8"},"peerDependencies":{"ai":">=7 <8","bash-tool":">=1.3.19 <2","just-bash":">=3.4.2 <4","@ai-sdk/openai":">=4 <5"},"peerDependenciesMeta":{"ai":{"optional":true},"bash-tool":{"optional":true},"@ai-sdk/openai":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/supabash-fs_0.4.0_1788279131822_0.05620497078124331","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"@seanmozeik/supabash-fs","version":"0.4.1","keywords":["apply-patch","deno","edge","filesystem","just-bash","supabase","virtual-filesystem","workspace"],"author":"Sean Lees","license":"MIT","_id":"@seanmozeik/supabash-fs@0.4.1","maintainers":[{"name":"seanmozeik","email":"sean@mozeik.com"}],"dist":{"shasum":"aa7fb967456e5828e75d23e4df65a87bc704efdc","tarball":"https://registry.npmjs.org/@seanmozeik/supabash-fs/-/supabash-fs-0.4.1.tgz","fileCount":115,"integrity":"sha512-G3ZbSPQ3LmdVIXeY76CiRNOglfy6qVQnVQPQVLBtR53g1WEwnIYwnCp1YY/n1pI8FjaG2WTw+AL/y2ERLDqcyA==","signatures":[{"sig":"MEUCIFxOcP0Cv+f5pYmWwLU15/7L4Fos0ozLhgZ1PUrqAHL7AiEA6hNeJxUwhRq1lndaByySHh3M5j2J/XUVDzXzio9Vv0o=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":509049},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","shasum":"aa7fb967456e5828e75d23e4df65a87bc704efdc","engines":{"bun":">=1.4.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./ai-sdk":{"types":"./dist/ai-sdk/index.d.ts","import":"./dist/ai-sdk.js"},"./package.json":"./package.json","./postgres/remove.sql":"./sql/postgres/0001_remove.sql","./postgres/install.sql":"./sql/postgres/0001_install.sql"},"scripts":{"fix":"oxfmt --write . && oxlint --type-aware --type-check --deny-warnings --fix .","lint":"oxlint --type-aware --type-check --deny-warnings .","test":"vitest run --reporter=minimal","build":"bun run build:clean && bun run build:js && bun run build:types","check":"bun run format:check && bun run lint && bun run typecheck && bun run source:size","format":"oxfmt --write .","prepack":"bun run build && bun run build:smoke","build:js":"bun run build:js:root && bun run build:js:ai-sdk","typecheck":"tsc --noEmit","check:deno":"deno check --minimum-dependency-age=0 --sloppy-imports --config deno.check.json tests/deno/live-supabase.ts tests/deno/ai-sdk.ts tests/deno/postgres-live.ts tests/postgres/edge-smoke/index.ts","build:clean":"bun ./scripts/clean-dist.ts","build:smoke":"bun ./scripts/check-built-package.mjs && bun ./scripts/check-built-ai-sdk.mjs","build:types":"tsc -p tsconfig.build.json","source:size":"bun ./scripts/check-source-size.ts","format:check":"oxfmt --check .","build:js:root":"bun build ./src/index.ts --outfile ./dist/index.js --target browser --format esm --packages external","package:check":"bun pm pack --dry-run","build:js:ai-sdk":"bun build ./src/ai-sdk/index.ts --outdir ./dist --entry-naming ai-sdk.js --chunk-naming ai-sdk-chunks/[name]-[hash].js --splitting --target browser --format esm --packages external","audit:production":"bun audit --production","package:consumer":"bun ./scripts/check-packed-consumer.ts && bun ./scripts/check-packed-ai-sdk-consumer.ts && bun ./scripts/check-packed-deno-consumer.ts","test:postgres:live":"bash ./scripts/run-postgres-integration.sh"},"_npmUser":{"name":"seanmozeik","email":"sean@mozeik.com"},"_integrity":"sha512-G3ZbSPQ3LmdVIXeY76CiRNOglfy6qVQnVQPQVLBtR53g1WEwnIYwnCp1YY/n1pI8FjaG2WTw+AL/y2ERLDqcyA==","_npmVersion":"10.8.3","description":"Authenticated Supabase Storage and Postgres workspace filesystems for Just Bash, Apply Patch, and durable revisions.","directories":{},"sideEffects":false,"_nodeVersion":"26.3.0","dependencies":{"yaml":"2.9.0","unbash":"4.0.10","@supabase/supabase-js":"2.112.4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"bun@1.4.0","devDependencies":{"ai":"7.0.83","oxfmt":"0.64.0","oxlint":"1.79.0","vitest":"4.1.11","bash-tool":"1.3.19","just-bash":"3.4.2","@types/bun":"1.4.0","typescript":"7.0.2","@types/node":"26.2.0","@ai-sdk/openai":"4.0.50","oxlint-tsgolint":"7.0.2001","@seanmozeik/de-clank":"0.1.8"},"peerDependencies":{"ai":">=7 <8","bash-tool":">=1.3.19 <2","just-bash":">=3.4.2 <4","@ai-sdk/openai":">=4 <5"},"peerDependenciesMeta":{"ai":{"optional":true},"bash-tool":{"optional":true},"@ai-sdk/openai":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/supabash-fs_0.4.1_1788349934016_0.10584501585525374","host":"s3://npm-registry-packages-npm-production"}},"0.4.2":{"name":"@seanmozeik/supabash-fs","version":"0.4.2","keywords":["apply-patch","deno","edge","filesystem","just-bash","supabase","virtual-filesystem","workspace"],"author":"Sean Lees","license":"MIT","_id":"@seanmozeik/supabash-fs@0.4.2","maintainers":[{"name":"seanmozeik","email":"sean@mozeik.com"}],"dist":{"shasum":"9caeafee3077ebe2d84635f92358b9b465053b37","tarball":"https://registry.npmjs.org/@seanmozeik/supabash-fs/-/supabash-fs-0.4.2.tgz","fileCount":115,"integrity":"sha512-PDvT8gpiWUyQLqcfJhg1ZrhL12H7YqoocUjSOMx+JYxYQLvmqpSgbHiOlQCDDV4lxa8f/FFOrc0ZjnTmBFkLlg==","signatures":[{"sig":"MEUCIQC0SHiYxJQ702nIgSrdH0NB3+Rlq61D1Tl03v1UgjsD2wIgBLR7rg5eRpskMaXvQ2g3+qf1d2ZECWDmJy4svyGD6a8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":509408},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","shasum":"9caeafee3077ebe2d84635f92358b9b465053b37","engines":{"bun":">=1.4.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./ai-sdk":{"types":"./dist/ai-sdk/index.d.ts","import":"./dist/ai-sdk.js"},"./package.json":"./package.json","./postgres/remove.sql":"./sql/postgres/0001_remove.sql","./postgres/install.sql":"./sql/postgres/0001_install.sql"},"scripts":{"fix":"oxfmt --write . && oxlint --type-aware --type-check --deny-warnings --fix .","lint":"oxlint --type-aware --type-check --deny-warnings .","test":"vitest run --reporter=minimal","build":"bun run build:clean && bun run build:js && bun run build:types","check":"bun run format:check && bun run lint && bun run typecheck && bun run source:size","format":"oxfmt --write .","prepack":"bun run build && bun run build:smoke","build:js":"bun run build:js:root && bun run build:js:ai-sdk","typecheck":"tsc --noEmit","check:deno":"deno check --minimum-dependency-age=0 --sloppy-imports --config deno.check.json tests/deno/live-supabase.ts tests/deno/ai-sdk.ts tests/deno/postgres-live.ts tests/postgres/edge-smoke/index.ts","build:clean":"bun ./scripts/clean-dist.ts","build:smoke":"bun ./scripts/check-built-package.mjs && bun ./scripts/check-built-ai-sdk.mjs","build:types":"tsc -p tsconfig.build.json","source:size":"bun ./scripts/check-source-size.ts","format:check":"oxfmt --check .","build:js:root":"bun build ./src/index.ts --outfile ./dist/index.js --target browser --format esm --packages external","package:check":"bun pm pack --dry-run","build:js:ai-sdk":"bun build ./src/ai-sdk/index.ts --outdir ./dist --entry-naming ai-sdk.js --chunk-naming ai-sdk-chunks/[name]-[hash].js --splitting --target browser --format esm --packages external","audit:production":"bun audit --production","package:consumer":"bun ./scripts/check-packed-consumer.ts && bun ./scripts/check-packed-ai-sdk-consumer.ts && bun ./scripts/check-packed-deno-consumer.ts","test:postgres:live":"bash ./scripts/run-postgres-integration.sh"},"_npmUser":{"name":"seanmozeik","email":"sean@mozeik.com"},"_integrity":"sha512-PDvT8gpiWUyQLqcfJhg1ZrhL12H7YqoocUjSOMx+JYxYQLvmqpSgbHiOlQCDDV4lxa8f/FFOrc0ZjnTmBFkLlg==","_npmVersion":"10.8.3","description":"Authenticated Supabase Storage and Postgres workspace filesystems for Just Bash, Apply Patch, and durable revisions.","directories":{},"sideEffects":false,"_nodeVersion":"26.3.0","dependencies":{"yaml":"2.9.0","unbash":"4.0.10","@supabase/supabase-js":"2.112.4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"bun@1.4.0","devDependencies":{"ai":"7.0.83","oxfmt":"0.64.0","oxlint":"1.79.0","vitest":"4.1.11","bash-tool":"1.3.19","just-bash":"3.4.2","@types/bun":"1.4.0","typescript":"7.0.2","@types/node":"26.2.0","@ai-sdk/openai":"4.0.50","oxlint-tsgolint":"7.0.2001","@seanmozeik/de-clank":"0.1.8"},"peerDependencies":{"ai":">=7 <8","bash-tool":">=1.3.19 <2","just-bash":">=3.4.2 <4","@ai-sdk/openai":">=4 <5"},"peerDependenciesMeta":{"ai":{"optional":true},"bash-tool":{"optional":true},"@ai-sdk/openai":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/supabash-fs_0.4.2_1788353939215_0.4150680884314062","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@seanmozeik/supabash-fs","version":"0.5.0","keywords":["apply-patch","deno","edge","filesystem","just-bash","supabase","virtual-filesystem","workspace"],"author":"Sean Lees","license":"MIT","_id":"@seanmozeik/supabash-fs@0.5.0","maintainers":[{"name":"seanmozeik","email":"sean@mozeik.com"}],"dist":{"shasum":"8f5944c335c99f82196d54acec76738f1778d5f7","tarball":"https://registry.npmjs.org/@seanmozeik/supabash-fs/-/supabash-fs-0.5.0.tgz","fileCount":117,"integrity":"sha512-xh35kDQBVirBRapIWnalegVAVGyuWSvNbdNSE4hrBeaOF2JaM7cGI2Jt9/T9j4OoBRqu3RECRb6fRdHWpxN3Zg==","signatures":[{"sig":"MEQCIEdQIz50EkXQRFYfHTXCI8PmD0Rh9k7rVmMwZa3z+gztAiByy2IdviCZj4+4XkRdKg4iH/mFpfmVN0pGWutxNtLDBg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":537634},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","shasum":"8f5944c335c99f82196d54acec76738f1778d5f7","engines":{"bun":">=1.4.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./ai-sdk":{"types":"./dist/ai-sdk/index.d.ts","import":"./dist/ai-sdk.js"},"./package.json":"./package.json","./postgres/remove.sql":"./sql/postgres/0001_remove.sql","./postgres/install.sql":"./sql/postgres/0001_install.sql"},"scripts":{"fix":"oxfmt --write . && oxlint --type-aware --type-check --deny-warnings --fix .","lint":"oxlint --type-aware --type-check --deny-warnings .","test":"vitest run --reporter=minimal","build":"bun run build:clean && bun run build:js && bun run build:types","check":"bun run format:check && bun run lint && bun run typecheck && bun run source:size","format":"oxfmt --write .","prepack":"bun run build && bun run build:smoke","build:js":"bun run build:js:root && bun run build:js:ai-sdk","typecheck":"tsc --noEmit","check:deno":"deno check --minimum-dependency-age=0 --sloppy-imports --config deno.check.json tests/deno/live-supabase.ts tests/deno/ai-sdk.ts tests/deno/postgres-live.ts tests/postgres/edge-smoke/index.ts","build:clean":"bun ./scripts/clean-dist.ts","build:smoke":"bun ./scripts/check-built-package.mjs && bun ./scripts/check-built-ai-sdk.mjs","build:types":"tsc -p tsconfig.build.json","source:size":"bun ./scripts/check-source-size.ts","format:check":"oxfmt --check .","build:js:root":"bun build ./src/index.ts --outfile ./dist/index.js --target browser --format esm --packages external","package:check":"bun pm pack --dry-run","build:js:ai-sdk":"bun build ./src/ai-sdk/index.ts --outdir ./dist --entry-naming ai-sdk.js --chunk-naming ai-sdk-chunks/[name]-[hash].js --splitting --target browser --format esm --packages external","audit:production":"bun audit --production","package:consumer":"bun ./scripts/check-packed-consumer.ts && bun ./scripts/check-packed-ai-sdk-consumer.ts && bun ./scripts/check-packed-deno-consumer.ts","test:postgres:live":"bash ./scripts/run-postgres-integration.sh"},"_npmUser":{"name":"seanmozeik","email":"sean@mozeik.com"},"_integrity":"sha512-xh35kDQBVirBRapIWnalegVAVGyuWSvNbdNSE4hrBeaOF2JaM7cGI2Jt9/T9j4OoBRqu3RECRb6fRdHWpxN3Zg==","_npmVersion":"10.8.3","description":"Authenticated Supabase Storage and Postgres workspace filesystems for Just Bash, Apply Patch, and durable revisions.","directories":{},"sideEffects":false,"_nodeVersion":"26.3.0","dependencies":{"yaml":"2.9.0","unbash":"4.0.10","@supabase/supabase-js":"2.112.4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"bun@1.4.0","devDependencies":{"ai":"7.0.83","oxfmt":"0.64.0","oxlint":"1.79.0","vitest":"4.1.11","bash-tool":"1.3.19","just-bash":"3.4.2","@types/bun":"1.4.0","typescript":"7.0.2","@types/node":"26.2.0","@ai-sdk/openai":"4.0.50","oxlint-tsgolint":"7.0.2001","@seanmozeik/de-clank":"0.1.8"},"peerDependencies":{"ai":">=7 <8","bash-tool":">=1.3.19 <2","just-bash":">=3.4.2 <4","@ai-sdk/openai":">=4 <5"},"peerDependenciesMeta":{"ai":{"optional":true},"bash-tool":{"optional":true},"@ai-sdk/openai":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/supabash-fs_0.5.0_1788537566949_0.5838198465933158","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@seanmozeik/supabash-fs","version":"0.6.0","keywords":["apply-patch","deno","edge","filesystem","just-bash","supabase","virtual-filesystem","workspace"],"author":"Sean Lees","license":"MIT","_id":"@seanmozeik/supabash-fs@0.6.0","maintainers":[{"name":"seanmozeik","email":"sean@mozeik.com"}],"dist":{"shasum":"d1289f1159f222d161690784710c6b73022d84fe","tarball":"https://registry.npmjs.org/@seanmozeik/supabash-fs/-/supabash-fs-0.6.0.tgz","fileCount":123,"integrity":"sha512-siJFYlgSs0JkbOrEE06flSs4pJ+YkCOtDmS7KCVBR2oBC4mFOTGlcfls/Tcnrutp3QmCNjF1BGFffUIDUDnvdQ==","signatures":[{"sig":"MEUCIBvmd+ZQ8l8zbUqgUh07HRgWzMh5+baWL19Ov8p7Myw9AiEAjBSN+1yv1/nQFsuwuBZl52/XaJTUbEv34HO7ZBxqYW0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":566643},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","shasum":"d1289f1159f222d161690784710c6b73022d84fe","engines":{"bun":">=1.4.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./ai-sdk":{"types":"./dist/ai-sdk/index.d.ts","import":"./dist/ai-sdk.js"},"./package.json":"./package.json","./postgres/remove.sql":"./sql/postgres/0001_remove.sql","./postgres/install.sql":"./sql/postgres/0001_install.sql"},"scripts":{"fix":"oxfmt --write . && oxlint --type-aware --type-check --deny-warnings --fix .","lint":"oxlint --type-aware --type-check --deny-warnings .","test":"vitest run --reporter=minimal","build":"bun run build:clean && bun run build:js && bun run build:types","check":"bun run format:check && bun run lint && bun run typecheck && bun run source:size","format":"oxfmt --write .","prepack":"bun run build && bun run build:smoke","build:js":"bun run build:js:root && bun run build:js:ai-sdk","typecheck":"tsc --noEmit","check:deno":"deno check --minimum-dependency-age=0 --sloppy-imports --config deno.check.json tests/deno/live-supabase.ts tests/deno/ai-sdk.ts tests/deno/postgres-live.ts tests/postgres/edge-smoke/index.ts","build:clean":"bun ./scripts/clean-dist.ts","build:smoke":"bun ./scripts/check-built-package.mjs && bun ./scripts/check-built-ai-sdk.mjs","build:types":"tsc -p tsconfig.build.json","source:size":"bun ./scripts/check-source-size.ts","format:check":"oxfmt --check .","build:js:root":"bun build ./src/index.ts --outfile ./dist/index.js --target browser --format esm --packages external","package:check":"bun pm pack --dry-run","build:js:ai-sdk":"bun build ./src/ai-sdk/index.ts --outdir ./dist --entry-naming ai-sdk.js --chunk-naming ai-sdk-chunks/[name]-[hash].js --splitting --target browser --format esm --packages external","audit:production":"bun audit --production","package:consumer":"bun ./scripts/check-packed-consumer.ts && bun ./scripts/check-packed-ai-sdk-consumer.ts && bun ./scripts/check-packed-deno-consumer.ts","test:postgres:live":"bash ./scripts/run-postgres-integration.sh"},"_npmUser":{"name":"seanmozeik","email":"sean@mozeik.com"},"_integrity":"sha512-siJFYlgSs0JkbOrEE06flSs4pJ+YkCOtDmS7KCVBR2oBC4mFOTGlcfls/Tcnrutp3QmCNjF1BGFffUIDUDnvdQ==","_npmVersion":"10.8.3","description":"Authenticated Supabase Storage and Postgres workspace filesystems for Just Bash, Apply Patch, and durable revisions.","directories":{},"sideEffects":false,"_nodeVersion":"26.3.0","dependencies":{"yaml":"2.9.0","unbash":"4.0.10","@supabase/supabase-js":"2.112.4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"bun@1.4.0","devDependencies":{"ai":"7.0.83","oxfmt":"0.64.0","oxlint":"1.79.0","vitest":"4.1.11","bash-tool":"1.3.19","just-bash":"3.4.2","@types/bun":"1.4.0","typescript":"7.0.2","@types/node":"26.2.0","@ai-sdk/openai":"4.0.50","oxlint-tsgolint":"7.0.2001","@seanmozeik/de-clank":"0.1.8"},"peerDependencies":{"ai":">=7 <8","bash-tool":">=1.3.19 <2","just-bash":">=3.4.2 <4","@ai-sdk/openai":">=4 <5"},"peerDependenciesMeta":{"ai":{"optional":true},"bash-tool":{"optional":true},"@ai-sdk/openai":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/supabash-fs_0.6.0_1788727036442_0.5357267533995549","host":"s3://npm-registry-packages-npm-production"}},"0.7.0":{"name":"@seanmozeik/supabash-fs","version":"0.7.0","description":"Authenticated Supabase Storage and Postgres workspace filesystems for Just Bash, Apply Patch, and durable revisions.","keywords":["apply-patch","deno","edge","filesystem","just-bash","supabase","virtual-filesystem","workspace"],"license":"MIT","author":"Sean Lees","type":"module","sideEffects":false,"main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./ai-sdk":{"types":"./dist/ai-sdk/index.d.ts","import":"./dist/ai-sdk.js"},"./postgres/install.sql":"./sql/postgres/0001_install.sql","./postgres/lazy-reads.sql":"./sql/postgres/0002_lazy_reads.sql","./postgres/versioned-entries.sql":"./sql/postgres/0003_versioned_entries.sql","./postgres/remove.sql":"./sql/postgres/0001_remove.sql","./package.json":"./package.json"},"publishConfig":{"access":"public"},"scripts":{"audit:production":"bun audit --production","build":"bun run build:clean && bun run build:js && bun run build:types","build:clean":"bun ./scripts/clean-dist.ts","build:js":"bun run build:js:root && bun run build:js:ai-sdk","build:js:ai-sdk":"bun build ./src/ai-sdk/index.ts --outdir ./dist --entry-naming ai-sdk.js --chunk-naming ai-sdk-chunks/[name]-[hash].js --splitting --target browser --format esm --packages external","build:js:root":"bun build ./src/index.ts --outfile ./dist/index.js --target browser --format esm --packages external","build:smoke":"bun ./scripts/check-built-package.mjs && bun ./scripts/check-built-ai-sdk.mjs","build:types":"tsc -p tsconfig.build.json","check:deno":"deno check --minimum-dependency-age=0 --sloppy-imports --config deno.check.json tests/deno/live-supabase.ts tests/deno/ai-sdk.ts tests/deno/postgres-live.ts tests/postgres/edge-smoke/index.ts","check":"bun run format:check && bun run lint && bun run typecheck && bun run source:size","fix":"oxfmt --write . && oxlint --type-aware --type-check --deny-warnings --fix .","format":"oxfmt --write .","format:check":"oxfmt --check .","lint":"oxlint --type-aware --type-check --deny-warnings .","package:check":"bun pm pack --dry-run","package:consumer":"bun ./scripts/check-packed-consumer.ts && bun ./scripts/check-packed-ai-sdk-consumer.ts && bun ./scripts/check-packed-deno-consumer.ts","prepack":"bun run build && bun run build:smoke","source:size":"bun ./scripts/check-source-size.ts","test":"vitest run --reporter=minimal","test:postgres:live":"bash ./scripts/run-postgres-integration.sh","typecheck":"tsc --noEmit"},"dependencies":{"@supabase/supabase-js":"2.116.0","unbash":"4.0.11","yaml":"2.9.1"},"devDependencies":{"@ai-sdk/openai":"4.0.66","@seanmozeik/de-clank":"0.1.8","@types/bun":"1.4.2","@types/node":"26.5.1","ai":"7.0.100","bash-tool":"1.3.19","just-bash":"3.4.2","oxfmt":"0.68.0","oxlint":"1.83.0","oxlint-tsgolint":"7.0.2001","typescript":"7.0.2","vitest":"4.1.11"},"peerDependencies":{"@ai-sdk/openai":">=4 <5","ai":">=7 <8","bash-tool":">=1.3.19 <2","just-bash":">=3.4.2 <4"},"peerDependenciesMeta":{"@ai-sdk/openai":{"optional":true},"ai":{"optional":true},"bash-tool":{"optional":true}},"engines":{"bun":">=1.4.0"},"packageManager":"bun@1.4.0","_id":"@seanmozeik/supabash-fs@0.7.0","_integrity":"sha512-JtS9AQ1MBysWyRpXji47cFQm6kEpS8BPwmfhoLhYKo2fEYm27AxisPP3etZym3HO9e+SbC/WTM+63POzgU+rIA==","_nodeVersion":"26.3.0","_npmVersion":"10.8.3","shasum":"5b2868d212f05a0e01385c386abe70df5dd15028","dist":{"integrity":"sha512-JtS9AQ1MBysWyRpXji47cFQm6kEpS8BPwmfhoLhYKo2fEYm27AxisPP3etZym3HO9e+SbC/WTM+63POzgU+rIA==","shasum":"5b2868d212f05a0e01385c386abe70df5dd15028","tarball":"https://registry.npmjs.org/@seanmozeik/supabash-fs/-/supabash-fs-0.7.0.tgz","fileCount":128,"unpackedSize":619169,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDQdhqkd+lE4kJbUJqLhjCXN7+0wxUfHFKC/TBCH8xAEQIgS9CNTw6Gc1YPO2F6MyE6323AN11NIrcLsjyvbnAt8Ew="}]},"_npmUser":{"name":"seanmozeik","email":"sean@mozeik.com"},"directories":{},"maintainers":[{"name":"seanmozeik","email":"sean@mozeik.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/supabash-fs_0.7.0_1789426623795_0.4133433452527464"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-27T10:47:15.334Z","modified":"2026-09-14T22:57:04.146Z","0.1.0":"2026-08-27T10:47:15.673Z","0.2.0":"2026-08-27T21:30:21.081Z","0.3.0":"2026-08-28T20:13:41.547Z","0.4.0":"2026-09-01T16:12:12.007Z","0.4.1":"2026-09-02T11:52:14.143Z","0.4.2":"2026-09-02T12:58:59.352Z","0.5.0":"2026-09-04T15:59:27.124Z","0.6.0":"2026-09-06T20:37:16.672Z","0.7.0":"2026-09-14T22:57:03.969Z"},"author":"Sean Lees","license":"MIT","keywords":["apply-patch","deno","edge","filesystem","just-bash","supabase","virtual-filesystem","workspace"],"description":"Authenticated Supabase Storage and Postgres workspace filesystems for Just Bash, Apply Patch, and durable revisions.","maintainers":[{"name":"seanmozeik","email":"sean@mozeik.com"}],"readme":"# supabash-fs\n\n`@seanmozeik/supabash-fs` mounts one verified Supabase workspace as a writable\nfilesystem for [Just Bash](https://github.com/vercel-labs/just-bash). A workspace\ncan use Supabase Storage or the package-owned Postgres schema. Agents edit a\nstaged in-memory tree. The host commits, inspects history, and restores. No tool\ncall publishes durable state by itself.\n\nThe Storage backend remains byte-oriented and lazy. The Postgres backend is an\nexplicit UTF-8 text tree with an atomic commit transaction and a snapshot pinned\nto one immutable revision.\n\nAgents can also receive a composed filesystem with private writable mounts and\nshared read-only snapshots. The host commits each writable workspace separately.\nSee [shared filesystems and publishing](docs/shared-filesystems.md) for the full\npublisher/reader workflow, table-backed sources, revisions and migration to 0.6.\n\nThe public API can still change before version 1.0.\n\n## Architecture\n\nOne opened `Workspace` is the unit of work:\n\n1. `Supabash.open` verifies a user bearer token and derives the Storage prefix\n   from that user ID. Callers cannot supply a user ID, root, or prefix.\n2. `Supabash.openPostgres` verifies the same bearer token and opens one canonical\n   workspace identifier. Database RLS checks that the verified subject owns it.\n3. `Supabash.openDelegated` and `Supabash.openPostgresDelegated` are separate\n   trusted-host paths. They mount only the Storage prefix or Postgres workspace\n   that a short-lived signed capability admits. Storage capabilities are\n   verified in process against an Ed25519 public key. Postgres capabilities are\n   verified inside the database.\n4. `workspace.fs` is a Just Bash `IFileSystem`. Bash, Apply Patch, and optional\n   image inspection all use this same staged tree.\n5. `commit` publishes staged edits and returns an immutable transaction receipt.\n6. `checkpoint`, `checkpoints`, `deleteCheckpoint`, `history`, `diff`,\n   `readRevision`, `restore`, and `purge` are host APIs. They are not model\n   tools.\n\nAuthorization is not command-string filtering. The real boundaries are the\nvirtual filesystem, canonical path checks, the verified user or capability,\nand the backend authorization policy. The command policy is a damage limiter.\n\nThe root export does not load `ai`, `@ai-sdk/openai`, or `bash-tool`. AI SDK\ntools live on `@seanmozeik/supabash-fs/ai-sdk`.\n\n## Install\n\n```sh\nbun add @seanmozeik/supabash-fs just-bash\n```\n\n`just-bash` is a peer dependency. `ai`, `@ai-sdk/openai`, and `bash-tool` are\noptional peers for the AI SDK export. The package does not use Effect or a\nNode.js filesystem.\n\nDeno 2 can load the package through npm compatibility:\n\n```ts\nimport { Supabash } from 'npm:@seanmozeik/supabash-fs';\nimport { Bash } from 'npm:just-bash/browser';\n```\n\nWhen `deno run` uses a restricted environment allow-list, also allow reads of\n`__MINIMATCH_TESTING_PLATFORM__`. Just Bash's pattern-matching dependency reads\nthat name during import. The variable does not need a value.\n\nThe optional AI SDK export loads its supported provider package. Under a\nrestricted Deno permission set, that dependency also needs read access to its\ninstalled npm files, environment access for `OPENAI_API_KEY` and\n`OPENAI_BASE_URL`, and system-information access. The package's clean-consumer\ngate type-checks and runs both exports from the packed tarball under Deno 2.\n\n## Configure Supabase Storage\n\nCreate a private Storage bucket. The examples in this document use `workspaces`.\n\nApply the four policies in\n[`examples/storage-policies.sql`](./examples/storage-policies.sql). Change the\nbucket name in that file if you use a different name.\n\nThe policies allow an authenticated user to access only object names whose\nfirst path segment is that user's Supabase Auth ID. Keep these policies even if\nthe package runs only in server functions. Package path checks and Storage RLS\nare separate security barriers.\n\nThe package needs all four object operations:\n\n- `SELECT` for list, file information, download, and upsert checks.\n- `INSERT` for new objects.\n- `UPDATE` for object replacement through upsert.\n- `DELETE` for removed files and replaced entry types.\n\nDo not make the bucket public. Do not pass a service-role key to\n`Supabash.open`. Delegated access is a separate API with its own trust\nboundary, documented below.\n\n## Open a user workspace\n\nPass the incoming request, the Supabase URL, the publishable key, and the\nbucket name. The request must contain the user's bearer access token.\n\n```ts\nimport { Supabash } from '@seanmozeik/supabash-fs';\nimport { Bash } from 'just-bash/browser';\n\nexport const runCommand = async (\n  request: Request,\n  command: string,\n  config: { publishableKey: string; supabaseUrl: string },\n) => {\n  const workspace = await Supabash.open({\n    bucket: 'workspaces',\n    publishableKey: config.publishableKey,\n    request,\n    supabaseUrl: config.supabaseUrl,\n  });\n\n  const bash = new Bash({ cwd: '/', fs: workspace.fs });\n  const result = await bash.exec(command);\n\n  if (result.exitCode !== 0) {\n    await workspace.discard();\n    return { result };\n  }\n\n  const receipt = await workspace.commit({\n    context: { actor: 'workspace', correlationId: crypto.randomUUID() },\n  });\n  return { receipt, result };\n};\n```\n\nUse `just-bash/browser` in edge runtimes. It excludes commands that need\nNode.js or an operating-system filesystem.\n\n`Supabash.open()` does this work before it lists Storage objects:\n\n1. It accepts only a Supabase publishable key or a legacy anon key.\n2. It reads one bearer token from the supplied `Request`.\n3. It rejects a user token with the `service_role` claim.\n4. It calls `supabase.auth.getUser(token)` to verify the session.\n5. It derives the object prefix from the verified user ID.\n\nThe API does not accept a user ID or an object prefix. Extra caller properties\ncannot select a different root. Every Storage call uses the user's bearer\ntoken, so the configured RLS policies also check each operation.\n\n## Configure Supabase Postgres\n\nRun the versioned install asset as the Supabase database owner:\n\n```sh\npsql \"$DATABASE_URL\" -v ON_ERROR_STOP=1 \\\n  -f node_modules/@seanmozeik/supabash-fs/sql/postgres/0001_install.sql \\\n  -f node_modules/@seanmozeik/supabash-fs/sql/postgres/0002_lazy_reads.sql \\\n  -f node_modules/@seanmozeik/supabash-fs/sql/postgres/0003_versioned_entries.sql\n```\n\nThe package exports the same file as\n`@seanmozeik/supabash-fs/postgres/install.sql`. The matching removal asset is\n`@seanmozeik/supabash-fs/postgres/remove.sql`. For runtime file access, import\n`POSTGRES_INSTALL_SQL_URL` or `POSTGRES_REMOVE_SQL_URL` from the package root\nand pass the URL to the Bun, Deno, or Node file API.\n\nThe install creates the private `supabash` schema, the non-login\n`supabash_api` execution role, FORCE RLS policies, and public RPCs. Authenticated\nand service-role clients receive no direct table access. Normal RPC calls derive\nownership from the verified JWT subject.\n\nCreate a workspace once, then store its returned canonical identifier:\n\n```ts\nconst workspace = await Supabash.createPostgresWorkspace({ publishableKey, request, supabaseUrl });\n```\n\nOpen it through the Postgres backend:\n\n```ts\nconst mounted = await Supabash.openPostgres({ publishableKey, request, supabaseUrl, workspace });\n```\n\n`mounted.committedSnapshot()` returns a frozen, detached copy of the pinned\ndatabase revision. Each document includes its stored body, metadata, canonical\nrendered content, hashes, and byte sizes. It does not include staged edits.\nAfter a successful commit, it describes the new committed revision.\n\nThe API accepts no owner, root, prefix, or secondary slug. The caller supplies\nonly the canonical workspace identifier. RLS decides whether the verified\nsubject can use it.\n\nThe Postgres backend persists regular UTF-8 files. It derives directories from\npaths. It rejects invalid UTF-8, NUL text, symbolic links, mode changes, and\ndurable empty directories with `UNSUPPORTED_CONTENT`. Its public capabilities\nstate these limits:\n\n```ts\nmounted.capabilities;\n// {\n//   backend: 'postgres',\n//   content: 'utf8-text-tree',\n//   durableEmptyDirectories: false,\n//   modes: false,\n//   symbolicLinks: false,\n// }\n```\n\nJust Bash still supports reads, recursive `grep` and `find`, redirection,\nappend, `sed -i`, nested file paths, moves, and deletes. `/bin`, `/usr`, `/dev`,\n`/proc`, and `/tmp` belong to the shell adapter. They never enter a snapshot or\ncommit.\n\n## Writable Bash and Apply Patch\n\nNormal file edits inside the mounted root are allowed: redirection, `sed`,\n`mv`, `rm`, `mkdir`, and similar Just Bash commands. The host still decides\nwhen those staged edits become durable.\n\n```ts\nimport { applyPatch, Supabash } from '@seanmozeik/supabash-fs';\nimport { Bash } from 'just-bash/browser';\n\nconst workspace = await Supabash.open({\n  bucket: 'workspaces',\n  publishableKey,\n  request,\n  supabaseUrl,\n});\n\nconst bash = new Bash({ cwd: '/', fs: workspace.fs });\nawait bash.exec(String.raw`printf 'alpha\\n' > /notes.md`);\n\nconst patched = await applyPatch(workspace, {\n  diff: '-alpha\\n+beta\\n',\n  path: '/notes.md',\n  type: 'update_file',\n});\n\nif (patched.status !== 'completed') {\n  await workspace.discard();\n} else {\n  await workspace.commit();\n}\n```\n\nAn optional document codec can parse agent-visible YAML frontmatter into a\nstored text body plus one flat mapping of string, number, boolean, or null\nvalues. Postgres and the client share one canonical renderer for that stored\nshape. Opening the same workspace without a codec still projects the same\nYAML file, so metadata changes participate in the same revision, diff,\ncheckpoint, and restore history as body changes.\n\n```ts\nimport { createYamlFrontmatterCodec, Supabash } from '@seanmozeik/supabash-fs';\n\nconst documentCodec = createYamlFrontmatterCodec({\n  validate(metadata, path) {\n    if (typeof metadata.description !== 'string') {\n      throw new TypeError(`${path} needs a description`);\n    }\n  },\n});\n\nconst mounted = await Supabash.openPostgres({\n  documentCodec,\n  publishableKey,\n  request,\n  supabaseUrl,\n  workspace,\n});\n```\n\nFor this visible file:\n\n```md\n---\ndescription: A short route to the document's distinct context\n---\n\n# Pacing after demanding work\n\nProtect recovery time after a long call.\n```\n\nPostgres stores `description` in the revision entry's `metadata` column and\nstores the Markdown beginning at the heading in the content-addressed body.\nThe projected filesystem reconstructs the complete file.\n\n`applyPatch` supports `create_file`, `update_file` with optional `moveTo`, and\n`delete_file`. Paths go through the same canonical parser as the filesystem.\nA failed operation does not leave a partial local mutation. Batches default to\nall-or-nothing; pass `{ mode: 'ordered' }` to keep earlier successful edits.\n\nApply Patch never calls `commit`. Bash never calls `commit`.\n\n## AI SDK tools\n\n```ts\nimport { createWorkspaceFileSystemView } from '@seanmozeik/supabash-fs';\nimport { createTools } from '@seanmozeik/supabash-fs/ai-sdk';\nimport { generateText } from 'ai';\n\nconst filesystem = createWorkspaceFileSystemView(workspace.fs, {\n  root: '/memory',\n  hiddenRoots: ['private'],\n});\nconst bound = await createTools({\n  filesystem,\n  bash: { policyOptions: { allowNetwork: false } },\n  applyPatch: true,\n  viewImage: { enabled: false },\n});\n\nconst result = await generateText({ model, tools: bound.tools, prompt });\n\nbound.filesystem === filesystem; // true\n```\n\nThe factory binds all tools to the supplied filesystem. It can be a workspace's\n`fs`, a scoped view, or `createMountedFileSystem(...).fs`. Tools stage edits; the\nhost retains the original workspace handle for commit, history and restore.\n\n`createWorkspaceFileSystemView` presents one subtree as `/` and hides configured\npaths from tools. `createMountedFileSystem` composes independent sources under\nvisible mount points such as `/memories` and `/docs`. Use the latter when the\nagent needs personal writable files alongside shared read-only content. Both\nBash and Apply Patch use the exact same filesystem and permission checks.\n\nOptional `view_image` reads only from the supplied filesystem, allowlists image MIME\ntypes, enforces a byte limit before decoding, and rejects symbolic-link\nescapes. Its model output is an AI SDK file-content part with the detected MIME\ntype and filename. The production build keeps this implementation in a\nseparate chunk and loads it only when `viewImage.enabled` is true.\n\nTool text is truncated with a stable `\\n[truncated]\\n` marker. Errors and\noutputs redact bearer tokens, signed URLs, and secret-looking keys.\n\n`bash-tool` does not expose a typed preflight hook, so the AI SDK adapter\nwraps `execute` and inspects the command first. That is a damage limiter, not\nan authorization boundary. Just Bash also receives a 30-second wall-clock\ndeadline by default. Set `bash.limits.maxExecutionTimeMs` to a positive safe\ninteger to change it.\n\nThis package does not compact model context. Context management belongs to the\nAI runtime.\n\nHosts can add normal Just Bash commands with `bash.customCommands`. Add each\ncommand name to `bash.policyOptions.extraAllowCommands` so the command policy\ncan inspect pipelines and compound syntax before Just Bash runs it:\n\n```ts\nimport { defineCommand } from 'just-bash/browser';\n\nconst count = defineCommand('count', async (_args, context) => ({\n  exitCode: 0,\n  stderr: '',\n  stdout: String(context.stdin.length),\n}));\n\nawait createTools({\n  filesystem: workspace.fs,\n  bash: { customCommands: [count], policyOptions: { extraAllowCommands: ['count'] } },\n});\n```\n\n## Command policy\n\n```ts\nimport { createCommandPolicy } from '@seanmozeik/supabash-fs';\n\nconst policy = createCommandPolicy({\n  allowNetwork: false,\n  extraDenyCommands: ['reboot'],\n  inspectors: [\n    {\n      inspect: (command) =>\n        command.includes('prod-secret')\n          ? { allow: false, code: 'custom-deny', reason: 'Blocked by host policy.' }\n          : { allow: true },\n    },\n  ],\n});\n```\n\nThe default policy parses the complete command with Unbash. It can inspect\nordinary pipelines, redirections, substitutions, literal variables, loops,\nconditionals, `find -exec`, process substitutions, and grouped commands.\nCommand and path values that can only be known at execution time run normally\ninside Just Bash. Concrete nested commands remain visible to the policy, so a\nliteral blocked command inside a substitution is still denied. The policy\ndenies or bounds:\n\n- statically resolved paths outside the virtual root or reserved `.supabash` segments\n- recursive operations whose statically resolved target is the mounted root\n- excessive command length, pipeline depth, or segment count\n- network commands when network is disabled\n- host-process escapes\n- malformed syntax that cannot be parsed\n\nDo not treat command-string filtering as the security boundary.\n\n## Revisions, recovery, and retention\n\n```ts\nconst staged = workspace.changes();\nconst marker = await workspace.checkpoint({\n  idempotencyKey: 'safe-before-job-1',\n  label: 'safe',\n  retentionClass: 'short-lived',\n});\nconst receipt = await workspace.commit({\n  context: { actor: 'host', correlationId: 'job-1', idempotencyKey: 'job-1' },\n});\nconst page = await workspace.history({ limit: 20 });\nconst diff = await workspace.diff({\n  from: { checkpoint: marker.checkpointId },\n  to: { staged: true },\n});\nconst previous = await workspace.readRevision(marker.revision);\nconst plan = await workspace.restore(marker.revision);\nawait workspace.commit({ context: { actor: 'host', correlationId: 'restore-1' } });\nconst checkpoints = await workspace.checkpoints();\nawait workspace.deleteCheckpoint(marker.checkpointId);\nawait workspace.purge({ dryRun: true, maxRevisions: 50 });\n```\n\n- `changes` returns the current staged set without a durable write.\n- `checkpoint` names the current complete revision. It does not publish staged\n  edits. Its idempotency key returns the same marker on retry.\n- `checkpoints` lists pinned markers with their labels and retention classes.\n  `deleteCheckpoint` releases a marker so retention can remove its revision.\n- `commit` publishes staged changes and returns an immutable receipt.\n- `discard` drops uncommitted changes only. A clean read-only delegated\n  workspace can call it as a no-op; discarding staged changes requires write\n  permission.\n- `history` reads committed transactions with cursor pagination. An unknown\n  cursor fails with `REVISION_NOT_FOUND` instead of restarting the page.\n- `diff` compares two committed revisions, a checkpoint, or staged state.\n  Text file add/delete/modify entries include a `preview` truncated to\n  `previewBytes` (default 8_192). Pass `previewBytes: 0` to skip bodies.\n- `readRevision` returns a read-only historical view.\n- `restore` rebuilds the live tree the same way `open` and `discard` do, then\n  stages the difference against the current baseline. It does not commit. The\n  next successful commit records `metadata.sourceRevision` automatically.\n- `purge` never makes a retained revision unreadable and can dry-run.\n\nVisible files stay in their filesystem paths. History lives under a private\nnamespace that the virtual filesystem cannot list, read, write, move, link, or\ndelete:\n\n```text\n<verified-scope>/\n  notes.md\n  .supabash/\n    objects/<sha256>\n    revisions/<revision>.json\n    transactions/<transaction>/intent.json\n    transactions/<transaction>/complete.json\n    transactions/<transaction>/abort.json\n    checkpoints/<checkpoint>.json\n    idempotency/<key>.json\n    head.json\n```\n\nThe path parser reserves `.supabash` and `.supabash-directory`. Storage\nlistings filter the private namespace before constructing filesystem entries.\n\nA retry with the same idempotency key and the same operation returns the first\nreceipt. Reusing that key for different changes or context fails with\n`IDEMPOTENCY_CONFLICT`. Restore creates a new forward transaction when the host\ncommits it. It never rewrites earlier history.\n\n## Storage consistency\n\nSupabase Storage does not provide an atomic multi-object transaction or a\nconditional compare-and-swap write. This package does not claim atomic\nrevision publish.\n\nThe write sequence is:\n\n1. freeze staged mutation and hash uploads\n2. acquire the optional per-scope commit lease\n3. recover any earlier interrupted transaction\n4. run the final conflict and quota checks\n5. write an intent record and initial recovery snapshot when needed\n6. upload visible objects and delete removed paths\n7. write content-addressed bodies, the revision manifest, and `complete.json`\n8. write the idempotency receipt and update `head.json` last\n\nA network failure can stop the sequence after some uploads. An intent without\na complete record is rolled back to the current head, or to its captured\ninitial snapshot when no head exists, before the next workspace opens. Recovery\nwrites an abort marker only after that rollback succeeds. A complete record\nwhose final head write failed is adopted instead. Retry `commit()` on the same\nworkspace after a transient failure; the transaction fingerprint and optional\nidempotency key prevent a second logical commit. The last published revision\nalso remains readable through `readRevision` throughout recovery. A lost\noptional lease fails with `COMMIT_COORDINATION` before a complete revision is\npublished. When a coordinator is present, workspace open and partial-discard\nrecovery acquire the same per-scope lease before they inspect an unresolved\nintent.\n\nWhen no coordinator is supplied, a check-to-write race remains. Another writer\ncan change an object between conflict preflight and upload. Applications that\nneed strict per-scope serialization should supply a `CommitCoordinator`.\nWithout that coordinator, an opener can also race an active publisher while it\ndecides whether an incomplete intent needs recovery.\n\n```ts\ninterface CommitCoordinator {\n  readonly acquire: (input: {\n    readonly scope: string;\n    readonly transactionId: string;\n  }) => Promise<{ readonly lost: () => Promise<boolean>; readonly release: () => Promise<void> }>;\n}\n```\n\nThe package does not require Postgres. A caller can implement the coordinator\nwith a database lock, queue, Durable Object, or another system.\n\n## Postgres consistency\n\nThe Postgres backend loads the current head and its immutable manifest without\nfile bodies. File reads fetch individual bodies at that pinned revision. Repeated\nfilesystem reads share the downloaded body. A commit rebuilds its snapshot from\naccepted changes without reading unchanged files.\n\nApply `sql/postgres/0002_lazy_reads.sql` and then `0003_versioned_entries.sql`\nonce after the foundation installation, before upgrading clients to 0.7.0.\nTheir exports are `@seanmozeik/supabash-fs/postgres/lazy-reads.sql` and\n`@seanmozeik/supabash-fs/postgres/versioned-entries.sql`.\n`await workspace.committedSnapshot()` explicitly loads a complete detached\nsnapshot in one bulk request; `workspace.committedRevision()` reads only the revision.\n\nIts commit RPC takes a transaction-scoped\nadvisory lock, checks the expected head, validates the complete change set, and\nwrites current documents, workspace-local content-addressed bodies, revision\nmetadata, changed file versions, receipt changes, and the new head in\none transaction.\n\nA stale head returns HTTP 409 with `SUPABASH_COMMIT_CONFLICT`, which the client\nmaps to `COMMIT_CONFLICT`. Expected conflicts do not use retry-class SQLSTATE\n`40001`. Any other error rolls back the full transaction. Restore stages a\ntarget revision and the next commit creates a new forward revision.\n\nCommit, checkpoint, and purge have a one-second lock-wait limit. Lock timeout,\ndeadlock, and serialization failure map to retryable `COMMIT_COORDINATION`.\nCallers must bound retries and add backoff. A stale revision requires reopening\nand reconsidering the changes. A delegated read of an older pinned revision\nrequires `history` as well as `read`. Expired grants and purged revisions fail\nexplicitly.\n\nVersion 0.7.0 stores unchanged file entries once across successive revisions.\nSequence intervals select the correct file version without replaying changes.\nPurge removes a closed interval only when no retained revision needs it.\nDistinct upsert batches of at least 16 files use set-based database writes.\nMixed mutations keep their ordered semantics. Both paths validate receipts and\nroll back the whole transaction on failure.\nExisting revisions keep their original manifests. The upgrade seeds entries\nfrom current documents under a database lock; schedule it as a database migration.\nOpening a workspace still transfers its full path index. Large workspaces and\naggregate traffic therefore need measured database and application capacity.\n\n## Operation events\n\nPostgres options accept an optional synchronous observer. It has no network\ndestination and sends nothing unless the host supplies a callback:\n\n```ts\nconst workspace = await Supabash.openPostgres({\n  observability: {\n    onOperation(event) {\n      operationEvents.push(event);\n    },\n  },\n  publishableKey,\n  request,\n  supabaseUrl,\n  workspace: workspaceId,\n});\n```\n\nEvents can include backend kind, operation, duration, outcome, typed error code,\ndocument count, UTF-8 byte count, measured serialized payload bytes, change\ncount, and replay or conflict outcome. They do not include document bodies,\npaths, tokens, user IDs, workspace IDs, correlation IDs, metadata, or raw error\nobjects. Observer failures do not change workspace behavior. A host can record\nthese events to compare snapshot, projection, and commit latency later.\n\n## Indexing feed\n\nCommit receipts and history records expose a stable cursor for later text,\nvector, or graph indexes. Those indexes are not an authority and are not\nimplemented here.\n\nEach receipt includes logical revision, parent revision, transaction ID, opaque\nscope digest, changed paths, change kinds, before and after hashes, ETags and\nsizes when available, move relationships, entry kinds, committed time, actor,\ncause, correlation ID, idempotency key, metadata, schema version, and `cursor`.\nHistory follows the published parent chain, so commits with equal timestamps\nstill have one causal order. A page cursor names the last transaction returned.\n\nAn indexer should:\n\n1. read only committed transactions through `history({ cursor })`\n2. fetch new bodies through the scoped read API or `readRevision`\n3. ignore uncommitted state\n4. resume from `receipt.cursor` / `record.cursor`\n5. treat restore as a normal forward transaction\n6. rebuild from one retained revision when needed\n\n## Delegated access\n\nTrusted background jobs can open one scoped workspace when no live user JWT\nexists. This is not `openAsUser`. The host signs a short-lived compact JWS and\npasses it to `Supabash.openDelegated` or `Supabash.openPostgresDelegated`.\n\nStorage capabilities are signed with Ed25519. The delegate verifies them\nitself, so it must not be able to mint them, and it holds only keyed public\nkeys. Postgres capabilities are signed with HMAC-SHA256 and verified inside the\ndatabase, so the delegate holds nothing at all. Both signing keys are\n`CryptoKey` values.\n\n```ts\nimport {\n  createDelegatedCapability,\n  CAPABILITY_SCHEMA_VERSION,\n  Supabash,\n} from '@seanmozeik/supabash-fs';\n\nconst capability = await createDelegatedCapability({\n  claims: {\n    aud: 'supabash-jobs',\n    bucket: 'workspaces',\n    corr: 'job-1',\n    exp: Math.floor(Date.now() / 1000) + 300,\n    iat: Math.floor(Date.now() / 1000),\n    iss: 'https://example.invalid/issuer',\n    nonce: 'job-1',\n    ops: ['read', 'write', 'commit', 'history'],\n    origin: supabaseUrl,\n    prefix: userId,\n    sub: 'job-1',\n    sv: CAPABILITY_SCHEMA_VERSION,\n  },\n  keyId: 'k1',\n  privateKey,\n});\n\nconst workspace = await Supabash.openDelegated({\n  bucket: 'workspaces',\n  capability,\n  serviceRoleKey,\n  supabaseUrl,\n  verifier: {\n    audience: 'supabash-jobs',\n    issuer: 'https://example.invalid/issuer',\n    origin: supabaseUrl,\n    publicKeys: { k1: publicKey },\n    nonceStore,\n  },\n});\n```\n\n`openDelegated` options must never be passed to `createTools` or retained on\n`Workspace`. The model and Just Bash never receive the signing key, the\nservice-role credential, or unverified claims.\n\nThe capability binds issuer, audience, subject, origin, bucket, exact prefix,\nallowed operations, issued-at, expiry, nonce, correlation ID, and schema\nversion. Verification rejects expired tokens, future issued-at times outside\nclock skew, the wrong issuer or audience, the wrong project, bucket, or\nprefix, a changed subject, and an invalid signature. A host nonce store\nprevents replay. The default maximum capability lifetime is 900 seconds. The\nnonce is consumed only after signature, scope, bucket, origin, and workspace\nopen checks succeed, so a transient Storage failure does not destroy a valid\nretry.\n\nStorage RLS alone may not express this server-side delegation. After the\ncapability verifies, the package uses a trusted Supabase client clamped to the\nsigned prefix. That is a host trust boundary: anyone who can call\n`openDelegated` with a valid capability and the service-role key can read and\nwrite that prefix.\n\n### Postgres delegation\n\nPostgres delegation uses schema version 3 and a different signature scheme,\nbecause a different party verifies it. The database is the only verifier, so\nthe capability is a compact `HS256` JWS signed with a secret that the minting\nhost and the database share. The job that presents the capability never holds\nthat secret and needs no verification key at all.\n\nThe database owner registers the key once. The database mints the secret and\nreturns it exactly once, so no caller writes a secret into SQL statement text:\n\n```sql\nselect public.supabash_register_capability_verifier(\n  p_key_id => 'k1',\n  p_issuer => 'https://issuer.example',\n  p_audience => 'supabash-jobs',\n  p_origin => 'https://project.example'\n);\n```\n\nThe secret is stored in `supabash.capability_secrets`, which grants nothing to\nany role, and is read only by the SQL verification path. Put the returned value\nin the minting host's environment, for example\n`supabase secrets set SUPABASH_CAPABILITY_SECRET=<value>`. Rotate with the same\ncall, or register a second `p_key_id` for an overlap window and then\n`select public.supabash_revoke_capability_verifier('k1')`.\n\nThe signed claims replace `bucket` and `prefix` with `backend: 'postgres'` and\none canonical `workspace` UUID:\n\n```ts\nimport {\n  createPostgresDelegatedCapability,\n  importCapabilitySecret,\n  POSTGRES_CAPABILITY_SCHEMA_VERSION,\n  Supabash,\n} from '@seanmozeik/supabash-fs';\n\nconst secretKey = await importCapabilitySecret(capabilitySecret);\nconst capability = await createPostgresDelegatedCapability({\n  claims: {\n    aud: 'supabash-jobs',\n    backend: 'postgres',\n    corr: 'job-2',\n    exp: Math.floor(Date.now() / 1000) + 300,\n    iat: Math.floor(Date.now() / 1000),\n    iss: 'https://issuer.example',\n    nonce: 'job-2',\n    ops: ['read', 'write', 'commit', 'history'],\n    origin: supabaseUrl,\n    sub: 'job-2',\n    sv: POSTGRES_CAPABILITY_SCHEMA_VERSION,\n    workspace: workspaceId,\n  },\n  keyId: 'k1',\n  secretKey,\n});\n\nconst workspace = await Supabash.openPostgresDelegated({\n  capability,\n  expectedOperations: ['read', 'write', 'commit', 'history'],\n  serviceRoleKey,\n  supabaseUrl,\n});\n```\n\n`openPostgresDelegated` requires `read` because opening a `Workspace` projects\nits pinned text snapshot into the staged filesystem. Add only the other\noperations that the job needs. A `restore` capability can plan a forward\nrestore without also granting `history`.\n\nWhen `expectedOperations` is present, the granted operation set must be exactly\nthat set. Extra, missing, or duplicate operations fail before capability\nexchange or workspace load. The returned workspace exposes frozen `delegation`\ndetails: actor, correlation ID, operations, subject, and workspace. These come\nfrom the grant that the database minted after it verified the signature, not\nfrom the presented claims and not from caller-supplied identity fields. The\npackage reads the presented claims only to refuse an obviously wrong request\nearly and to detect a response that does not match the capability it sent.\n\nThe exchange RPC is owned by a least-privileged role that holds no privilege on\nthe secret table, so it never sees the secret. It calls one narrow function\nthat keeps its definer rights with the database owner, recomputes the MAC with\n`extensions.hmac`, compares it under a fresh random blind, and returns only a\nboolean. The RPC then consumes the nonce and returns an opaque short-lived\ngrant bound to the signed workspace and operations. Service-role RPC calls\ncannot select a workspace without this grant.\n\nWhy HMAC and not Ed25519: verifying an Ed25519 signature in SQL needed\n`pgsodium`, which Supabase has deprecated and no longer ships on PostgreSQL 17,\nand which a migration cannot install. `pgcrypto` offers no public-key signature\nverification. Moving verification into the process that opens the session was\nthe other option, and it was rejected: that process holds the service role, so\nit would both verify the capability and assert the result, and a caller holding\nonly the service role could then mint its own authority. Keeping verification\nin SQL keeps that caller unable to forge anything.\n\nWhat the symmetric secret changes: any party holding the secret can mint a\ncapability, so the minting host and the database are now equally trusted for\nminting. The database owner could already insert grants directly, so the set of\nprincipals that can create authority does not grow. What is given up is\ndistinguishing several mutually distrusting minters by key. Since the database\nis the only verifier, no other principal can observe that difference. Residual\nrisks: a leaked secret lets an attacker mint capabilities for any workspace\nuntil the key is rotated; a leaked capability is still bounded by its\n`workspace`, `ops`, `exp`, and single-use `nonce`; and the minting host must\nkeep the secret out of the delegate's environment, because a delegate that\nholds it stops being bounded by its capability. Unlike the Ed25519 scheme, the\nsigning secret is now inside the database, so a database dump contains it and\nanyone holding a dump can mint. Rotation is the answer to a suspected dump\nleak.\n\nThe boundary rests only on grants this package owns. `supabash.capability_secrets`\ngrants nothing to `anon`, `authenticated`, or `service_role`; row level security\nis enabled on it without `force`, so the installing owner keeps access and every\nother role is denied twice; and the only path to the secret is a definer-rights\nfunction that returns a boolean. Forging a capability therefore needs the\nminting host's secret, database-owner access, or a copy of the database. The\ninstall refuses to run if any PostgREST role holds a privilege on that table or\n`usage` on the `supabash` schema.\n\n`supabase_vault` is not used, and this is a deliberate reversal. On a stock\nSupabase project `service_role` holds `select` and `delete` on `vault.secrets`\nand `vault.decrypted_secrets` and `execute` on `vault.create_secret` and\n`vault.update_secret`; a live install proved it. The vault would give this\nsecret no protection from the one role the design exists to constrain, and the\nonly remaining barrier would be the PostgREST exposed-schema setting, which\nthis package does not control. Revoking the platform's grants would be fragile,\nbecause a platform upgrade can restore them.\n\nThreat model, in short:\n\n- Changing the subject, prefix, workspace, or operation set in a copied token\n  fails signature checks.\n- Copying a valid capability to another bucket or origin fails verification.\n- A caller holding only the service-role key and the capability format cannot\n  mint a capability. The Ed25519 private key and the Postgres capability secret\n  are both outside its reach.\n- A parent prefix is not implied by a child prefix.\n- Expiry and optional nonce stores block stale or replayed jobs.\n- A compromised model prompt cannot select a bucket, user, or credential.\n- Tool input and output redact tokens, signed URLs, and capabilities.\n- Cross-user history, diff, and restore fail because each workspace is scoped\n  to one verified prefix.\n\n## Options and limits\n\n```ts\ninterface SupabashOptions {\n  readonly bucket: string;\n  readonly coordinator?: CommitCoordinator;\n  readonly fetch?: typeof globalThis.fetch;\n  readonly limits?: WorkspaceLimits;\n  readonly maxFileSystemBytes?: number;\n  readonly publishableKey: string;\n  readonly request: Request;\n  readonly supabaseUrl: string;\n  readonly uploadConcurrency?: number;\n}\n\ninterface PostgresWorkspaceOptions {\n  readonly workspace: string;\n  readonly documentCodec?: TextDocumentCodec;\n  readonly fetch?: typeof globalThis.fetch;\n  readonly limits?: WorkspaceLimits;\n  readonly maxFileSystemBytes?: number;\n  readonly observability?: WorkspaceObservability;\n  readonly publishableKey: string;\n  readonly request: Request;\n  readonly supabaseUrl: string;\n}\n```\n\nDocumented defaults:\n\n| Limit                         | Default    |\n| ----------------------------- | ---------- |\n| `maxVisibleFiles`             | 10_000     |\n| `maxPathLength`               | 1_024      |\n| `maxFileSize`                 | 10_485_760 |\n| `maxStagedBytes`              | 52_428_800 |\n| `maxPatchSize`                | 1_048_576  |\n| `maxCommandLength`            | 32_768     |\n| `maxBashOutput`               | 262_144    |\n| `maxExecutionTimeMs`          | 30_000     |\n| `viewImage.maxBytes`          | 5_242_880  |\n| `maxHistoryPageSize`          | 100        |\n| `maxDiffPreviewBytes`         | 8_192      |\n| `maxTransactionMetadataBytes` | 16_384     |\n| `maxRevisions` purge hint     | 50         |\n| `uploadConcurrency`           | 4          |\n\n`maxFileSystemBytes` uses Just Bash's 1,073,741,824-byte in-memory default when\nit is omitted. Configured limits must be safe integers in their documented\nrange. Invalid values and runtime quota failures use `QUOTA_EXCEEDED`. Limits\nfail before a durable mutation when possible.\n\n## Errors\n\nAll package errors are `SupabashError` values:\n\n| Code                   | Meaning                                                      |\n| ---------------------- | ------------------------------------------------------------ |\n| `AUTHENTICATION`       | The bearer token is missing, malformed, or not verified.     |\n| `AUTHORIZATION`        | A key, verified identity, bucket, or root is unsafe.         |\n| `COMMIT_CONFLICT`      | A changed remote entry no longer matches the opened version. |\n| `COMMIT_COORDINATION`  | The optional commit lease was lost.                          |\n| `COMMIT_IN_PROGRESS`   | Code tried to mutate or commit an active commit.             |\n| `EXPIRED_CAPABILITY`   | The delegated capability has expired.                        |\n| `HISTORY_CORRUPTION`   | A history record could not be parsed.                        |\n| `IDEMPOTENCY_CONFLICT` | An idempotency key was reused for a different commit.        |\n| `INVALID_CAPABILITY`   | The delegated capability is not acceptable.                  |\n| `INVALID_PATCH`        | The V4A patch could not be applied.                          |\n| `INVALID_PATH`         | A virtual path is unsafe or invalid.                         |\n| `PARTIAL_COMMIT`       | Remote writes stopped before a complete revision.            |\n| `POLICY_DENIED`        | The command policy denied a Bash command.                    |\n| `QUOTA_EXCEEDED`       | A configured limit was exceeded.                             |\n| `REVISION_NOT_FOUND`   | The requested revision is missing.                           |\n| `STORAGE`              | A Supabase Storage operation failed.                         |\n| `UNSUPPORTED_CONTENT`  | The path or bytes cannot be used for this operation.         |\n\n`SupabashError.path` identifies the affected virtual path when one is\navailable. The original error is available through `error.cause`.\nUse `isSupabashError(error)` to recognize these errors across separately bundled\nentry points, where JavaScript constructor identity can differ.\n\n`error.retryable` is true when Supabash has classified the cause as transient.\n`error.outcomeUnknown` is true when a transport failure may have hidden a\nsuccessful durable mutation. Use the exported\n`isRetryableSupabashError(error)` and\n`isUnknownOutcomeSupabashError(error)` helpers instead of retrying every\n`STORAGE` error. Supabash does not retry automatically. When the outcome is\nunknown, repeat an operation only when it is idempotent or after reconciliation.\nFor a Postgres commit, the workspace retains the same transaction and generated\ncontext across an immediate retry, so the database can return the existing\nresult without creating a second logical commit.\n\nSession-verification transport and server failures also return retryable\n`STORAGE` errors. Invalid sessions return non-retryable `AUTHENTICATION` errors.\nKeep retries bounded, use backoff with jitter, and count retries against the\nhost's request admission limit. An unknown mutation outcome still requires\nidempotency or reconciliation, even when its error is retryable.\n\nSet a limit on active workspace operations at the application boundary. Count\nwork across all application workers that share the database; a separate large\nqueue in each worker can overload the same connection pool. Reject or defer\nexcess arrivals with a bounded queue and deadline. Increasing concurrency past\ncapacity can reduce throughput and increase failures. Use the\n[Modal capacity harness](scripts/stress/hill/README.md) to find a limit for the\ndeployment's actual workload, file sizes, and latency target.\n\n## Runtime and package size\n\nThe package builds for browser, Deno, and edge runtimes. It uses web-standard\n`Request`, `fetch`, `crypto`, `Blob`, and encoding APIs. It imports Just Bash\nfrom `just-bash/browser`.\n\nOpening a workspace lists every object below the prefix. Regular file bodies\nstay lazy. A very large object count increases open time.\n\nThe root bundle is tree-shakeable relative to the AI SDK export. Importing\n`@seanmozeik/supabash-fs` must not resolve `ai`, `@ai-sdk/openai`, or\n`bash-tool`. Import `@seanmozeik/supabash-fs/ai-sdk` only when those optional\npeers are installed. A clean root-only package test confirms that a missing AI\npeer is named in the import error. The image implementation is a separate\ndynamic chunk and is not loaded when image support is disabled.\n\nJust Bash is an in-process virtual shell. It does not start a container and\ndoes not provide operating-system isolation.\n\n## Development\n\nInstall Bun 1.4 and Deno 2, then install the pinned dependencies:\n\n```sh\nbun install --frozen-lockfile\n```\n\n```sh\njust verify\njust live\njust live-postgres\n```\n\n`just live` expects a local Supabase API at `SUPABASH_TEST_SUPABASE_URL` plus a\npublishable key, service-role key, and two user tokens or emails. It creates\nthe `workspaces` bucket when needed and runs the Deno suite. Point those\nvariables at a Docker stack, not a hosted project.\n\n`just live-postgres` installs the package SQL in a disposable Docker-backed\nSupabase database, runs the authenticated and delegated Deno integration\nsuite, removes all package-owned database objects and synthetic users, and\nchecks that cleanup succeeded. Its required environment variables are listed\nby `scripts/run-postgres-integration.sh` when one is missing.\n\nThe gate checks formatting, lint, TypeScript, source size, tests, the browser\nbuild, Deno type resolution, production dependencies, package contents, clean\nBun consumers, and a clean Deno consumer of the packed tarball. This repository\ndoes not add CI.\n\n## Licence\n\nMIT. See [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md) for source and\ndependency notices.\n","readmeFilename":"README.md"}