@fungi.computer/oyster 0.0.0-stage → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1082 -2
  3. package/dist/agents.d.ts +114 -0
  4. package/dist/agents.js +322 -0
  5. package/dist/app-builds.d.ts +151 -0
  6. package/dist/app-builds.js +355 -0
  7. package/dist/app-sandbox.d.ts +2 -0
  8. package/dist/app-sandbox.js +2 -0
  9. package/dist/app-sources.d.ts +46 -0
  10. package/dist/app-sources.js +335 -0
  11. package/dist/app-storage.d.ts +141 -0
  12. package/dist/app-storage.js +281 -0
  13. package/dist/apps/gate.d.ts +105 -0
  14. package/dist/apps/gate.js +273 -0
  15. package/dist/apps/index.d.ts +5 -0
  16. package/dist/apps/index.js +5 -0
  17. package/dist/apps/installation.d.ts +248 -0
  18. package/dist/apps/installation.js +307 -0
  19. package/dist/apps/operations.d.ts +8 -0
  20. package/dist/apps/operations.js +98 -0
  21. package/dist/apps/tools.d.ts +33 -0
  22. package/dist/apps/tools.js +17 -0
  23. package/dist/auth.d.ts +11 -0
  24. package/dist/auth.js +93 -0
  25. package/dist/backend-request.d.ts +3 -0
  26. package/dist/backend-request.js +53 -0
  27. package/dist/build/build-jobs.d.ts +128 -0
  28. package/dist/build/build-jobs.js +197 -0
  29. package/dist/build/build-step.d.ts +26 -0
  30. package/dist/build/build-step.js +20 -0
  31. package/dist/build/bundled-html.d.ts +7 -0
  32. package/dist/build/bundled-html.js +64 -0
  33. package/dist/build/definition-emission.d.ts +34 -0
  34. package/dist/build/definition-emission.js +201 -0
  35. package/dist/build/effect.d.ts +231 -0
  36. package/dist/build/effect.js +396 -0
  37. package/dist/build/git-environment.d.ts +6 -0
  38. package/dist/build/git-environment.js +48 -0
  39. package/dist/build/html-recipe.d.ts +20 -0
  40. package/dist/build/html-recipe.js +51 -0
  41. package/dist/build/local-build.d.ts +57 -0
  42. package/dist/build/local-build.js +1078 -0
  43. package/dist/build/r2-artifact-writer.d.ts +31 -0
  44. package/dist/build/r2-artifact-writer.js +179 -0
  45. package/dist/build/snapshot-package-store.d.ts +2 -0
  46. package/dist/build/snapshot-package-store.js +21 -0
  47. package/dist/build/snapshot-program-scripts.d.ts +6 -0
  48. package/dist/build/snapshot-program-scripts.js +2 -0
  49. package/dist/build/submission-contract.d.ts +384 -0
  50. package/dist/build/submission-contract.js +70 -0
  51. package/dist/build/workspace-build.d.ts +50 -0
  52. package/dist/build/workspace-build.js +156 -0
  53. package/dist/build-contract.d.ts +4 -0
  54. package/dist/build-contract.js +4 -0
  55. package/dist/build-jobs.d.ts +2 -0
  56. package/dist/build-jobs.js +2 -0
  57. package/dist/build.d.ts +11 -0
  58. package/dist/build.js +13 -0
  59. package/dist/capability-descriptor.d.ts +46 -0
  60. package/dist/capability-descriptor.js +51 -0
  61. package/dist/catalog-frame.d.ts +39 -0
  62. package/dist/catalog-frame.js +72 -0
  63. package/dist/cause-class.d.ts +19 -0
  64. package/dist/cause-class.js +54 -0
  65. package/dist/cloudflare.d.ts +123 -0
  66. package/dist/cloudflare.js +391 -0
  67. package/dist/computer-terminal.d.ts +38 -0
  68. package/dist/computer-terminal.js +410 -0
  69. package/dist/computer.d.ts +60 -0
  70. package/dist/computer.js +33 -0
  71. package/dist/event-recipients.d.ts +57 -0
  72. package/dist/event-recipients.js +144 -0
  73. package/dist/facet-invocation.d.ts +4 -0
  74. package/dist/facet-invocation.js +45 -0
  75. package/dist/files.d.ts +42 -0
  76. package/dist/files.js +123 -0
  77. package/dist/frame-binding-wire-script.d.ts +2 -0
  78. package/dist/frame-binding-wire-script.js +1 -0
  79. package/dist/frame-binding-wire.d.ts +28 -0
  80. package/dist/frame-binding-wire.js +54 -0
  81. package/dist/git-admission.d.ts +52 -0
  82. package/dist/git-admission.js +188 -0
  83. package/dist/git-auth.d.ts +11 -0
  84. package/dist/git-auth.js +27 -0
  85. package/dist/git-door.d.ts +35 -0
  86. package/dist/git-door.js +108 -0
  87. package/dist/git-journal.d.ts +31 -0
  88. package/dist/git-journal.js +188 -0
  89. package/dist/git.d.ts +31 -0
  90. package/dist/git.js +64 -0
  91. package/dist/guest-policy.d.ts +9 -0
  92. package/dist/guest-policy.js +13 -0
  93. package/dist/host.d.ts +244 -0
  94. package/dist/host.js +1043 -0
  95. package/dist/index.d.ts +166 -0
  96. package/dist/index.js +320 -0
  97. package/dist/installation-consent.d.ts +79 -0
  98. package/dist/installation-consent.js +82 -0
  99. package/dist/internal/background-origins.d.ts +3 -0
  100. package/dist/internal/background-origins.js +8 -0
  101. package/dist/internal/delivery.d.ts +12 -0
  102. package/dist/internal/delivery.js +167 -0
  103. package/dist/internal/host-gate.d.ts +14 -0
  104. package/dist/internal/host-gate.js +62 -0
  105. package/dist/internal/registry-installations.d.ts +6 -0
  106. package/dist/internal/registry-installations.js +12 -0
  107. package/dist/invocation.d.ts +251 -0
  108. package/dist/invocation.js +377 -0
  109. package/dist/keys.d.ts +48 -0
  110. package/dist/keys.js +52 -0
  111. package/dist/mcp-owner.d.ts +27 -0
  112. package/dist/mcp-owner.js +143 -0
  113. package/dist/mcp.d.ts +89 -0
  114. package/dist/mcp.js +429 -0
  115. package/dist/media.d.ts +23 -0
  116. package/dist/media.js +12 -0
  117. package/dist/pack.d.ts +76 -0
  118. package/dist/pack.js +134 -0
  119. package/dist/page/capabilities.d.ts +23 -0
  120. package/dist/page/capabilities.js +256 -0
  121. package/dist/page/computer-handles.d.ts +79 -0
  122. package/dist/page/computer-handles.js +44 -0
  123. package/dist/page/lifetime.d.ts +6 -0
  124. package/dist/page/lifetime.js +19 -0
  125. package/dist/page/mount.d.ts +163 -0
  126. package/dist/page/mount.js +390 -0
  127. package/dist/page/storage.d.ts +3 -0
  128. package/dist/page/storage.js +82 -0
  129. package/dist/page/terminal.d.ts +5 -0
  130. package/dist/page/terminal.js +7 -0
  131. package/dist/page-script.d.ts +14 -0
  132. package/dist/page-script.js +5 -0
  133. package/dist/r2-files.d.ts +3 -0
  134. package/dist/r2-files.js +79 -0
  135. package/dist/release.d.ts +72 -0
  136. package/dist/release.js +164 -0
  137. package/dist/root-data.d.ts +86 -0
  138. package/dist/root-data.js +69 -0
  139. package/dist/shell/app-directory.d.ts +537 -0
  140. package/dist/shell/app-directory.js +195 -0
  141. package/dist/shell/browser-actions.d.ts +7 -0
  142. package/dist/shell/browser-actions.js +56 -0
  143. package/dist/shell/command-host.d.ts +112 -0
  144. package/dist/shell/command-host.js +502 -0
  145. package/dist/shell/commands.d.ts +25 -0
  146. package/dist/shell/commands.js +78 -0
  147. package/dist/shell/index.d.ts +8 -0
  148. package/dist/shell/index.js +8 -0
  149. package/dist/shell/keys.d.ts +41 -0
  150. package/dist/shell/keys.js +134 -0
  151. package/dist/shell/shell-module.d.ts +31 -0
  152. package/dist/shell/shell-module.js +37 -0
  153. package/dist/shell/window-manager.d.ts +124 -0
  154. package/dist/shell/window-manager.js +711 -0
  155. package/dist/shell/workbench-contract.d.ts +68 -0
  156. package/dist/shell/workbench-contract.js +1 -0
  157. package/dist/shell/workbench-host-types.d.ts +96 -0
  158. package/dist/shell/workbench-host-types.js +1 -0
  159. package/dist/shell/workbench-host.d.ts +34 -0
  160. package/dist/shell/workbench-host.js +347 -0
  161. package/dist/shell/workbench-model.d.ts +192 -0
  162. package/dist/shell/workbench-model.js +53 -0
  163. package/dist/shell/workbench.d.ts +4 -0
  164. package/dist/shell/workbench.js +2 -0
  165. package/dist/source/fork.d.ts +27 -0
  166. package/dist/source/fork.js +46 -0
  167. package/dist/source/index.d.ts +11 -0
  168. package/dist/source/index.js +11 -0
  169. package/dist/source/seed-receipt.d.ts +53 -0
  170. package/dist/source/seed-receipt.js +18 -0
  171. package/dist/source/team-source.d.ts +32 -0
  172. package/dist/source/team-source.js +42 -0
  173. package/dist/source/template-repository.d.ts +2 -0
  174. package/dist/source/template-repository.js +8 -0
  175. package/dist/source/template-write.d.ts +69 -0
  176. package/dist/source/template-write.js +344 -0
  177. package/dist/storage-frame.d.ts +64 -0
  178. package/dist/storage-frame.js +28 -0
  179. package/dist/tenant.d.ts +81 -0
  180. package/dist/tenant.js +71 -0
  181. package/dist/testing/capabilities.d.ts +29 -0
  182. package/dist/testing/capabilities.js +130 -0
  183. package/dist/testing/catalog.d.ts +3 -0
  184. package/dist/testing/catalog.js +53 -0
  185. package/dist/testing/index.d.ts +10 -0
  186. package/dist/testing/index.js +10 -0
  187. package/dist/testing/installation.d.ts +11 -0
  188. package/dist/testing/installation.js +25 -0
  189. package/dist/testing/native-worker.d.ts +21 -0
  190. package/dist/testing/native-worker.js +106 -0
  191. package/dist/testing/worker.d.ts +18 -0
  192. package/dist/testing/worker.js +67 -0
  193. package/dist/testing/workers-runtime.d.ts +24 -0
  194. package/dist/testing/workers-runtime.js +33 -0
  195. package/dist/webhook-delivery.d.ts +14 -0
  196. package/dist/webhook-delivery.js +17 -0
  197. package/dist/workers-git.d.ts +285 -0
  198. package/dist/workers-git.js +1174 -0
  199. package/dist/workers.d.ts +352 -0
  200. package/dist/workers.js +866 -0
  201. package/docs/api/README.md +82 -0
  202. package/docs/api/agents.md +332 -0
  203. package/docs/api/app-storage.md +209 -0
  204. package/docs/api/apps.md +1375 -0
  205. package/docs/api/auth.md +63 -0
  206. package/docs/api/build-contract.md +507 -0
  207. package/docs/api/build.md +1476 -0
  208. package/docs/api/cloudflare.md +421 -0
  209. package/docs/api/computer.md +203 -0
  210. package/docs/api/files.md +117 -0
  211. package/docs/api/git.md +470 -0
  212. package/docs/api/host.md +1328 -0
  213. package/docs/api/index.md +430 -0
  214. package/docs/api/invocation.md +738 -0
  215. package/docs/api/keys.md +175 -0
  216. package/docs/api/mcp.md +301 -0
  217. package/docs/api/media.md +40 -0
  218. package/docs/api/modules.md +42 -0
  219. package/docs/api/pack.md +138 -0
  220. package/docs/api/page/mount.md +298 -0
  221. package/docs/api/root-data.md +111 -0
  222. package/docs/api/shell/app-directory.md +332 -0
  223. package/docs/api/shell/command-host.md +224 -0
  224. package/docs/api/shell/commands.md +197 -0
  225. package/docs/api/shell/keys.md +183 -0
  226. package/docs/api/shell/shell-module.md +143 -0
  227. package/docs/api/shell/window-manager.md +267 -0
  228. package/docs/api/shell/workbench.md +186 -0
  229. package/docs/api/shell.md +1788 -0
  230. package/docs/api/tenant.md +194 -0
  231. package/docs/api/workers-git.md +1527 -0
  232. package/docs/api/workers.md +1678 -0
  233. package/docs/media/architecture.mmd +32 -0
  234. package/docs/media/architecture.png +0 -0
  235. package/docs/media/bonsai-network.png +0 -0
  236. package/docs/media/bonsai-repository.png +0 -0
  237. package/docs/site/agent-quickstart.mdx +349 -0
  238. package/examples/git/worker.ts +39 -0
  239. package/examples/git/wrangler.jsonc +32 -0
  240. package/examples/standalone/worker.ts +14 -0
  241. package/examples/standalone/wrangler.jsonc +21 -0
  242. package/package.json +249 -3
@@ -0,0 +1,32 @@
1
+ flowchart LR
2
+ people["Browsers<br/>git clients<br/>MCP agents"]
3
+
4
+ subgraph oyster["Oyster Worker · Cloudflare Workers or celld"]
5
+ direction TB
6
+ router["Router<br/>/apps · /api · /git · /mcp"]
7
+ home["Tourtois home<br/>launcher · windows · settings"]
8
+ registry[("OysterRegistry DO<br/>installations")]
9
+ instance["App Instance DO<br/>one per installation"]
10
+ facet["App backend<br/>Worker Loader facet<br/>with its own SQLite"]
11
+ door["git door<br/>per-agent keys"]
12
+ router --> home
13
+ router --> registry
14
+ router --> instance --> facet
15
+ router --> door
16
+ end
17
+
18
+ apps["Apps as immutable Releases<br/>Bonsai · Workflows · Shed<br/>Inkcap · Grove · Prints"]
19
+
20
+ subgraph services["Bindings the host provides"]
21
+ direction TB
22
+ r2[("R2<br/>Release artifacts")]
23
+ artifacts[("Artifacts<br/>git repositories")]
24
+ platform["Braid Workflows · Computers<br/>Agents · Workers AI"]
25
+ end
26
+
27
+ people --> router
28
+ home -. "UI in sandboxed frames" .-> apps
29
+ registry -- "Release refs" --> r2
30
+ door --> artifacts
31
+ facet -- "declared only" --> platform
32
+ facet -- "GIT binding" --> artifacts
Binary file
Binary file
Binary file
@@ -0,0 +1,349 @@
1
+ ---
2
+ title: Agent quick start
3
+ description:
4
+ Hand this guide to a coding agent to install Oyster from npm, run it locally,
5
+ install an App, connect over MCP and deploy.
6
+ ---
7
+
8
+ You are an agent setting up Oyster for a person. Follow these steps in order.
9
+ Run every command exactly as written, from the directory each step names.
10
+ Compare its output with the expected output before you go on. When a step fails,
11
+ read the "If it fails" notes under it before trying anything else.
12
+
13
+ ## What Oyster is
14
+
15
+ - Oyster hosts Apps on one Cloudflare Worker, or on celld, a Workers-compatible
16
+ runtime you host yourself.
17
+ - Each App is an immutable Release built from an exact git commit. Its backend
18
+ runs in its own Durable Object with its own SQLite, and it gets only the
19
+ bindings it declares.
20
+ - People use Apps in a browser. Agents call the same App methods over HTTP or
21
+ through one MCP endpoint, `/mcp`.
22
+
23
+ ## 1. Check prerequisites
24
+
25
+ You need Node 24 or newer, pnpm, Git, curl and openssl. You do not need a
26
+ Cloudflare account until step 7, and you never need Docker for these steps.
27
+
28
+ ```sh
29
+ node --version
30
+ pnpm --version
31
+ git --version
32
+ ```
33
+
34
+ Expected output: a `v24.` or later Node version, any pnpm version, then any
35
+ `git version 2.x` line.
36
+
37
+ If it fails:
38
+
39
+ - Wrong Node: install Node 24 with your version manager, for example
40
+ `mise use node@24` or `nvm install 24`.
41
+ - No pnpm: run `corepack enable`, or use `npm install` wherever this guide says
42
+ `pnpm add`.
43
+
44
+ ## 2. Create a project
45
+
46
+ Make a new directory, install Oyster and Wrangler, and copy the standalone
47
+ example Worker out of the package:
48
+
49
+ ```sh
50
+ mkdir my-oyster
51
+ cd my-oyster
52
+ pnpm add @fungi.computer/oyster
53
+ pnpm add -D wrangler
54
+ cp node_modules/@fungi.computer/oyster/examples/standalone/worker.ts .
55
+ cp node_modules/@fungi.computer/oyster/examples/standalone/wrangler.jsonc .
56
+ ```
57
+
58
+ Expected output: pnpm lists the added package after each `add`, and `ls` now
59
+ shows `node_modules`, `package.json`, `worker.ts` and `wrangler.jsonc`.
60
+
61
+ Every later command runs from this `my-oyster` directory unless the step says
62
+ otherwise.
63
+
64
+ If it fails:
65
+
66
+ - `ERR_PNPM_IGNORED_BUILDS` naming packages such as `esbuild`, `workerd` or
67
+ `protobufjs`: pnpm installed them without running their install scripts, and
68
+ nothing in this guide needs those scripts. Go on.
69
+ - `cp: cannot stat …/examples/standalone/…`: the install did not finish. Run
70
+ `pnpm add @fungi.computer/oyster` again and read its first error.
71
+
72
+ ## 3. Run a local Oyster
73
+
74
+ `worker.ts` and `wrangler.jsonc` are a complete single-owner Oyster. Run it
75
+ under `wrangler dev` on your machine. Choose an owner key and keep this terminal
76
+ open:
77
+
78
+ ```sh
79
+ export OYSTER_OWNER_KEY=$(openssl rand -hex 24)
80
+ export OYSTER_URL=http://127.0.0.1:8787
81
+ echo "$OYSTER_OWNER_KEY" > .oyster-owner-key
82
+ npx wrangler dev --local --ip 127.0.0.1 --port 8787 \
83
+ --var OYSTER_OWNER_KEY:$OYSTER_OWNER_KEY --var OYSTER_ORIGIN:$OYSTER_URL
84
+ ```
85
+
86
+ Expected output: a bindings table listing `env.MCP (OysterMcp)`,
87
+ `env.REGISTRY (OysterRegistry)`, `env.INSTANCES (OysterInstance)`,
88
+ `env.RELEASES (oyster-releases)` and `env.LOADER`, then
89
+ `Ready on http://127.0.0.1:8787`.
90
+
91
+ Keep it running. Open a second terminal in `my-oyster`, set the same two
92
+ variables there, and check that Oyster answers:
93
+
94
+ ```sh
95
+ export OYSTER_OWNER_KEY=$(cat .oyster-owner-key)
96
+ export OYSTER_URL=http://127.0.0.1:8787
97
+ curl -s -o /dev/null -w '%{http_code}\n' $OYSTER_URL/login
98
+ curl -s -o /dev/null -w '%{http_code}\n' $OYSTER_URL/api/installations
99
+ curl -s -H "Authorization: Bearer $OYSTER_OWNER_KEY" $OYSTER_URL/api/installations
100
+ ```
101
+
102
+ Expected output: `200`, then `401` (no key), then `[]` (no Apps installed yet).
103
+
104
+ If it fails:
105
+
106
+ - `Address already in use`: another process holds port 8787. Pick another port
107
+ and use it in both `--port` and `OYSTER_URL`.
108
+ - `curl` prints `000`: the server is not ready yet. Wait for the `Ready on`
109
+ line.
110
+ - `401` with the key: the two terminals hold different keys. Set
111
+ `OYSTER_OWNER_KEY` from `.oyster-owner-key` in both.
112
+ - An agent runtime that cannot keep a process in the foreground starts the same
113
+ command in the background with its output in a log, then waits until the
114
+ `/login` check prints `200`:
115
+
116
+ ```sh
117
+ npx wrangler dev --local --ip 127.0.0.1 --port 8787 \
118
+ --var OYSTER_OWNER_KEY:$OYSTER_OWNER_KEY --var OYSTER_ORIGIN:$OYSTER_URL \
119
+ > oyster-dev.log 2>&1 &
120
+ ```
121
+
122
+ To stop the server, press Ctrl-C in its terminal. A background server runs as
123
+ several processes (`npm exec`, `wrangler.js` and `workerd`); stop them all by
124
+ ending `wrangler.js`, then check that `/login` prints `000`:
125
+
126
+ ```sh
127
+ pkill -f 'wrangler[.]js dev .*--port 8787'
128
+ ```
129
+
130
+ ## 4. Add an App
131
+
132
+ An App is a git repository with a `package.json` that declares it under
133
+ `fungi.app`. Make the smallest one, a single committed HTML page:
134
+
135
+ ```sh
136
+ mkdir -p hello-app/bundle
137
+ cat > hello-app/package.json <<'EOF'
138
+ {
139
+ "name": "hello-app",
140
+ "private": true,
141
+ "fungi": { "app": { "displayName": "Hello", "capabilities": [] } }
142
+ }
143
+ EOF
144
+ cat > hello-app/bundle/index.html <<'EOF'
145
+ <!doctype html>
146
+ <title>Hello</title>
147
+ <h1>Hello from my first Oyster App</h1>
148
+ EOF
149
+ git -C hello-app init -q
150
+ git -C hello-app add .
151
+ git -C hello-app -c user.name=Oyster -c user.email=oyster@example.invalid \
152
+ commit -qm "Hello App"
153
+ ```
154
+
155
+ Then pack that commit into a Release and install it as installation `hello` with
156
+ `@fungi.computer/oyster/pack`:
157
+
158
+ ```sh
159
+ cat > install-hello.mjs <<'EOF'
160
+ import { execFileSync } from "node:child_process";
161
+ import { mkdtemp, rm } from "node:fs/promises";
162
+ import { tmpdir } from "node:os";
163
+ import { join, resolve } from "node:path";
164
+ import process from "node:process";
165
+ import { installRelease, packRelease } from "@fungi.computer/oyster/pack";
166
+
167
+ const source = resolve("hello-app");
168
+ const commit = execFileSync("git", ["-C", source, "rev-parse", "HEAD"], {
169
+ encoding: "utf8",
170
+ }).trim();
171
+ const out = await mkdtemp(join(tmpdir(), "oyster-hello-"));
172
+ try {
173
+ await packRelease({
174
+ appId: "app_hello",
175
+ sourceDirectory: source,
176
+ sourceRevision: `git:${commit}`,
177
+ recipe: {
178
+ kind: "bundled-html",
179
+ entry: "bundle/index.html",
180
+ assets: {
181
+ maxManifestBytes: 65_536,
182
+ maxObjects: 16,
183
+ maxObjectBytes: 1_048_576,
184
+ maxTotalBytes: 4_194_304,
185
+ },
186
+ assetBase: "./",
187
+ backend: null,
188
+ maxOutputBytes: 4_194_304,
189
+ },
190
+ outDirectory: join(out, "release"),
191
+ });
192
+ const response = await installRelease({
193
+ directory: join(out, "release"),
194
+ url: process.env.OYSTER_URL,
195
+ key: process.env.OYSTER_OWNER_KEY,
196
+ installationId: "hello",
197
+ });
198
+ console.log(`installed hello: ${response.status} ${await response.text()}`);
199
+ } finally {
200
+ await rm(out, { recursive: true, force: true });
201
+ }
202
+ EOF
203
+ node install-hello.mjs
204
+ curl -s -o /dev/null -w '%{http_code}\n' \
205
+ -H "Authorization: Bearer $OYSTER_OWNER_KEY" $OYSTER_URL/apps/hello/
206
+ ```
207
+
208
+ Expected output: one line starting
209
+ `installed hello: 201 {"installationId":"hello",` and containing
210
+ `"status":"enabled"`, then `200`. Running `node install-hello.mjs` again prints
211
+ `installed hello: 200`: the installation already exists, and Oyster switches it
212
+ to the Release just packed.
213
+
214
+ A person can open `$OYSTER_URL/login`, sign in with the owner key, and open
215
+ Hello from the installation list at `$OYSTER_URL/`.
216
+
217
+ If it fails:
218
+
219
+ - `installed hello: 401`: `OYSTER_OWNER_KEY` in this terminal differs from the
220
+ server's. Set it from `.oyster-owner-key`.
221
+ - `fetch failed` or `ECONNREFUSED`: the server from step 3 is not running.
222
+ - The build reads committed files only. To install an edit, commit it in
223
+ `hello-app` and run `node install-hello.mjs` again; Oyster switches the
224
+ installation to the new Release and keeps its data.
225
+
226
+ An App with a backend, typed methods and its own SQLite adds
227
+ `fungi.backendDefinition` and a `backend` entry to its recipe. The
228
+ [Notes example App](https://github.com/fungi-computer/oyster/tree/main/examples/notes-app)
229
+ is one, and the Oyster README's "Pack an App" and "Build App Releases" sections
230
+ describe the recipe.
231
+
232
+ ## 5. Connect over MCP
233
+
234
+ `/mcp` serves every installed App to MCP clients through one `execute` tool, in
235
+ which each App's methods are `env.<APP>`. The owner key signs in. For Claude
236
+ Code:
237
+
238
+ ```sh
239
+ claude mcp add --transport http oyster "$OYSTER_URL/mcp" \
240
+ --header "Authorization: Bearer $OYSTER_OWNER_KEY"
241
+ ```
242
+
243
+ To check `/mcp` without an MCP client, open a session and list its tools:
244
+
245
+ ```sh
246
+ MCP=(-H "Authorization: Bearer $OYSTER_OWNER_KEY" -H 'content-type: application/json' -H 'accept: application/json, text/event-stream')
247
+ SESSION=$(curl -s "${MCP[@]}" -D - -o /dev/null \
248
+ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}' \
249
+ $OYSTER_URL/mcp | grep -i '^mcp-session-id' | cut -d' ' -f2 | tr -d '\r')
250
+ curl -s "${MCP[@]}" -H "mcp-session-id: $SESSION" -H 'mcp-protocol-version: 2025-11-25' \
251
+ -d '{"jsonrpc":"2.0","method":"notifications/initialized"}' $OYSTER_URL/mcp
252
+ curl -s "${MCP[@]}" -H "mcp-session-id: $SESSION" -H 'mcp-protocol-version: 2025-11-25' \
253
+ -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' $OYSTER_URL/mcp
254
+ ```
255
+
256
+ Expected output: the last command prints an `event: message` block whose `data:`
257
+ line contains `"name":"execute"`.
258
+
259
+ If it fails:
260
+
261
+ - `SESSION` is empty: the `initialize` call was refused. Check the key as in
262
+ step 3.
263
+ - Named keys for each agent (`POST /api/keys`) come with the Git worker in
264
+ step 6. The plain standalone Worker answers `404` there.
265
+
266
+ ## 6. Add Git
267
+
268
+ The package also ships `examples/git`: the same Oyster with the git door, so
269
+ people and agents `clone`, `fetch` and `push` App repositories with ordinary
270
+ `git` at `/git/<installation>/<repo>.git`. Repositories live in Cloudflare
271
+ Artifacts, which `wrangler dev` cannot emulate, so the Git worker runs once it
272
+ is deployed in step 7. To use it, copy it over the standalone files:
273
+
274
+ ```sh
275
+ cp node_modules/@fungi.computer/oyster/examples/git/worker.ts .
276
+ cp node_modules/@fungi.computer/oyster/examples/git/wrangler.jsonc .
277
+ ```
278
+
279
+ Compared with `examples/standalone`, `examples/git/worker.ts` builds one
280
+ `oysterGitRoot` with `ownerOrKeyTenancy` and adds:
281
+
282
+ - the git door, mounted by `createOysterGitWorker(root)`;
283
+ - owner and named keys that sign in to git, plus `/api/keys` and
284
+ `/api/webhooks`;
285
+ - `OysterGitInstance` as `OysterInstance`, which hands Apps `env.GIT` and
286
+ `env.EVENTS`;
287
+ - the `ARTIFACTS` binding and the `KEYS` and `WEBHOOKS` Durable Objects in its
288
+ `wrangler.jsonc`, which names the Worker `oyster-git`.
289
+
290
+ With the Git worker deployed, give each agent its own key:
291
+
292
+ ```sh
293
+ curl -s -X POST -H "Authorization: Bearer $OYSTER_OWNER_KEY" \
294
+ -H 'content-type: application/json' \
295
+ -d '{"name":"morel","grants":[{"capability":"apps.use","targets":{"kind":"all"}}]}' \
296
+ $OYSTER_URL/api/keys
297
+ ```
298
+
299
+ Expected output: JSON with a `plaintext` field starting `oyk_`. Oyster shows it
300
+ once; that key signs in to `git` and to `/mcp`.
301
+
302
+ If it fails:
303
+
304
+ - `404`: the deployed Worker is the standalone one. Copy the `examples/git`
305
+ files and deploy again.
306
+
307
+ ## 7. Deploy to Cloudflare Workers
308
+
309
+ This step needs a Cloudflare account on Workers Paid; Git also needs Artifacts
310
+ enabled on it. Ask the person before you deploy anything.
311
+
312
+ `wrangler.jsonc` names the Worker (`oyster`, or `oyster-git` for Git) and its R2
313
+ bucket, `oyster-releases`. The Worker's address is
314
+ `https://<worker name>.<your-subdomain>.workers.dev`, or a custom domain. For
315
+ the standalone Worker:
316
+
317
+ ```sh
318
+ npx wrangler login
319
+ npx wrangler r2 bucket create oyster-releases
320
+ export OYSTER_URL=https://oyster.<your-subdomain>.workers.dev
321
+ npx wrangler deploy --var OYSTER_ORIGIN:$OYSTER_URL
322
+ printf '%s' "$OYSTER_OWNER_KEY" | npx wrangler secret put OYSTER_OWNER_KEY
323
+ ```
324
+
325
+ Expected output: `wrangler deploy` prints the deployed `https://` URL, and
326
+ `wrangler secret put` reports the secret uploaded. Then install Hello exactly as
327
+ in step 4, with `OYSTER_URL` set to the deployed address.
328
+
329
+ To keep `OYSTER_ORIGIN` across later deploys, set `vars.OYSTER_ORIGIN` in
330
+ `wrangler.jsonc`; every deploy replaces the Worker's variables.
331
+
332
+ If it fails:
333
+
334
+ - `R2 bucket … already exists`: use that bucket, or pick a new name and put it
335
+ in `wrangler.jsonc`.
336
+ - A secret prompt on stdin: pipe the key as shown; never type it into a log.
337
+ - Bindings or migrations refused: deploy the copied `wrangler.jsonc` unchanged
338
+ except for `vars` and the bucket name.
339
+
340
+ ## Where to go next
341
+
342
+ - The Oyster README (`node_modules/@fungi.computer/oyster/README.md`) explains
343
+ the host, Apps, capabilities, MCP and the shell in depth.
344
+ - [oyster.shiit.app](https://oyster.shiit.app/) is a complete deployment of the
345
+ Git worker with Fungi's App set: Bonsai, Workflows, Computers, Agents and
346
+ media.
347
+ - Oyster is built from the other Fungi packages, each its own repository under
348
+ [github.com/fungi-computer](https://github.com/fungi-computer) and on npm as
349
+ `@fungi.computer/<name>`. The README's "Part of Fungi" section lists them.
@@ -0,0 +1,39 @@
1
+ // Add Git to an Oyster Worker. `host.git` is the whole seam: `createOyster`
2
+ // mounts the git door at `/git/<installation>/<repo>.git`, and every clone,
3
+ // fetch and push passes host authentication, live installation admission and
4
+ // the App's own push policy before Artifacts sees a byte.
5
+ //
6
+ // One root composes every tenant host. HTTP, MCP and background wakes all reach
7
+ // it through `root.hostFor`, so they share one auth, one workspace and one
8
+ // capability set.
9
+ import type { TenantScope } from "@fungi.computer/oyster/tenant";
10
+ import {
11
+ createOysterGitMcpOwner,
12
+ createOysterGitWorker,
13
+ OysterGitInstance,
14
+ oysterGitRoot,
15
+ ownerOrKeyTenancy,
16
+ type OysterGitWorkerEnv,
17
+ } from "@fungi.computer/oyster/workers-git";
18
+
19
+ export { OysterRegistry } from "@fungi.computer/oyster/workers";
20
+ // The one binding entrypoint, whose table also hands Apps `env.GIT` and `env.EVENTS`.
21
+ export {
22
+ OysterBinding,
23
+ OysterKeys,
24
+ OysterWebhooks,
25
+ } from "@fungi.computer/oyster/workers-git";
26
+
27
+ // The owner key, or a named key minted at POST /api/keys, signs `git` in.
28
+ const root = oysterGitRoot<OysterGitWorkerEnv>({ tenancy: ownerOrKeyTenancy });
29
+
30
+ export class OysterMcp extends createOysterGitMcpOwner(root) {}
31
+
32
+ /** Instances that hand Apps `env.GIT` and `env.EVENTS`; wakes use the root's host. */
33
+ export class OysterInstance extends OysterGitInstance {
34
+ protected hostFor(scope: TenantScope) {
35
+ return root.hostFor(this.env, scope);
36
+ }
37
+ }
38
+
39
+ export default createOysterGitWorker(root);
@@ -0,0 +1,32 @@
1
+ {
2
+ "$schema": "../../../../node_modules/wrangler/config-schema.json",
3
+ "name": "oyster-git",
4
+ "main": "worker.ts",
5
+ "compatibility_date": "2026-09-04",
6
+ "compatibility_flags": ["nodejs_compat"],
7
+ "vars": { "OYSTER_ORIGIN": "https://oyster.example" },
8
+ "worker_loaders": [{ "binding": "LOADER" }],
9
+ "r2_buckets": [{ "binding": "RELEASES", "bucket_name": "oyster-releases" }],
10
+ "artifacts": [{ "binding": "ARTIFACTS", "namespace": "oyster" }],
11
+ "durable_objects": {
12
+ "bindings": [
13
+ { "name": "MCP", "class_name": "OysterMcp" },
14
+ { "name": "REGISTRY", "class_name": "OysterRegistry" },
15
+ { "name": "INSTANCES", "class_name": "OysterInstance" },
16
+ { "name": "KEYS", "class_name": "OysterKeys" },
17
+ { "name": "WEBHOOKS", "class_name": "OysterWebhooks" },
18
+ ],
19
+ },
20
+ "migrations": [
21
+ {
22
+ "tag": "v1",
23
+ "new_sqlite_classes": [
24
+ "OysterMcp",
25
+ "OysterRegistry",
26
+ "OysterInstance",
27
+ "OysterKeys",
28
+ "OysterWebhooks",
29
+ ],
30
+ },
31
+ ],
32
+ }
@@ -0,0 +1,14 @@
1
+ // A standalone Oyster: one owner, Apps installed from Releases, each App's
2
+ // backend in its own Durable Object with SQLite. Runs on Cloudflare Workers
3
+ // (`wrangler deploy`) and on celld (`celld dev`) from the same code.
4
+ import { createOysterWorker } from "@fungi.computer/oyster/workers";
5
+
6
+ // Every App binding in every invocation frame is the one `OysterBinding` entrypoint.
7
+ export {
8
+ OysterBinding,
9
+ OysterInstance,
10
+ OysterRegistry,
11
+ OysterMcp,
12
+ } from "@fungi.computer/oyster/workers";
13
+
14
+ export default createOysterWorker();
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "../../../../node_modules/wrangler/config-schema.json",
3
+ "name": "oyster",
4
+ "main": "worker.ts",
5
+ "compatibility_date": "2026-09-04",
6
+ "compatibility_flags": ["nodejs_compat"],
7
+ "vars": { "OYSTER_ORIGIN": "https://oyster.example" },
8
+ "worker_loaders": [{ "binding": "LOADER" }],
9
+ "r2_buckets": [{ "binding": "RELEASES", "bucket_name": "oyster-releases" }],
10
+ "durable_objects": {
11
+ "bindings": [
12
+ { "name": "MCP", "class_name": "OysterMcp" },
13
+ { "name": "REGISTRY", "class_name": "OysterRegistry" },
14
+ { "name": "INSTANCES", "class_name": "OysterInstance" },
15
+ ],
16
+ },
17
+ "migrations": [
18
+ { "tag": "v1", "new_sqlite_classes": ["OysterRegistry", "OysterInstance"] },
19
+ { "tag": "mcp-session-owner-v1", "new_sqlite_classes": ["OysterMcp"] },
20
+ ],
21
+ }