Skip to content
Quickstart

Quickstart

Run your own Apps on Cloudflare Workers or celld: installable App Releases, each with its own Durable Object, SQLite database and declared bindings

Deciding what to run? Is Oyster for you? compares agent Computers and App builds with Cloudflare Workers Builds.

You need:

  • a Cloudflare account on Workers Paid, with Containers, Artifacts, Workers AI, Workflows and Browser Rendering (now called Browser Run) enabled;
  • Docker, which Wrangler uses to build the Computer’s container image;
  • Node 24, pnpm 11 or newer and Git.

The first install takes about 15 to 20 minutes, mostly building the seven Apps on the Computer. By default, Oyster builds two Apps at a time. Start a project, add Oyster, its Computer and Wrangler, and copy the example Worker out of the package. The local pnpm policy allows esbuild and workerd to install their native binaries and disables unneeded optional dependency scripts, so a fresh install exits successfully:

TERMINAL
mkdir my-oyster && cd my-oyster
cat > pnpm-workspace.yaml <<'YAML'
allowBuilds:
"@google/genai": false
"@mongodb-js/zstd": false
esbuild: true
exifreader: false
node-liblzma: false
protobufjs: false
workerd: true
YAML
pnpm add @fungi.computer/oyster @fungi.computer/computer react@^19.2.0 react-dom@^19.2.0
pnpm add -D wrangler
cp node_modules/@fungi.computer/oyster/examples/standalone/{worker.ts,wrangler.jsonc} .

If pnpm add reports ERR_PNPM_IGNORED_BUILDS, check that the policy file above exists before installing. If it names a new dependency not listed there, stop and review that package before allowing its scripts. Do not treat exit 1 as success.

worker.ts re-exports the whole host:

worker.ts
export * from "@fungi.computer/oyster/standalone";
export { default } from "@fungi.computer/oyster/standalone";

wrangler.jsonc names the Worker oyster, its Release bucket oyster-releases and its Artifacts namespace oyster, and runs the Computer from node_modules/@fungi.computer/computer/Dockerfile. Its "browser": { "binding": "BROWSER" } enables Cloudflare Browser Rendering for the zero-grant UI boot check and for Agents’ browser tool. Without the binding the UI of a Release is reported not_checked with code: "boot_browser_binding_missing"; the install still proceeds.

Create the bucket and deploy. Your Worker’s address is https://oyster.<your-subdomain>.workers.dev; your workers.dev subdomain is on the Workers & Pages overview in the Cloudflare dashboard.

TERMINAL
npx wrangler login
npx wrangler r2 bucket create oyster-releases
export OYSTER_URL=https://oyster.<your-subdomain>.workers.dev
npx wrangler deploy --var OYSTER_ORIGIN:$OYSTER_URL

Set OYSTER_ORIGIN in wrangler.jsonc instead if you deploy again later; every deploy replaces the Worker’s variables. If you rename the Worker’s name in wrangler.jsonc, update OYSTER_ORIGIN to that Worker’s URL at the same time.

For an API token instead of wrangler login, load CLOUDFLARE_API_TOKEN into your shell without committing it. Set CLOUDFLARE_ACCOUNT_ID (or account_id in wrangler.jsonc) explicitly, and skip the login command. Use Wrangler 4.135.0 or newer; record npx wrangler --version for deployment and teardown.

The Cloudflare API specification names these account-scoped permission groups for the deployment and resource deletion operations:

Permission group Used for
Workers Scripts Write Deploy, secrets, Worker deletion and Workflow deletion
Workers R2 Storage Write Release bucket creation, object cleanup and bucket deletion
Workers Containers Write Computer container application management
Artifacts Edit Artifacts namespace and repository management

Browser Rendering through the Workers BROWSER binding needs no additional API-token permission. The separate Browser Rendering Write permission is for its REST API, which this quick start does not use. Workers Paid entitlement is still required.

For stream-rehearsal diagnostics, include Browser Rendering (Read) in the account-scoped token checklist: it permits REST session inspection, separate from the BROWSER binding’s boot operation. Use the documented /browser-rendering/devtools/session list and devtools/session/{session_id} details operations; a session’s endTime, not its connection end time, reports closure. A plain 404 from another route does not establish a missing permission. If inspection is unavailable, record UNAVAILABLE, not confirmed cleanup. Do not broaden an existing token automatically.

With an explicit account ID, user/account discovery is unnecessary. Do not add Workers KV Storage Read, User Details Read or Memberships Read just for that discovery. Wrangler 4.135.0 treats denied legacy-Sites KV inspection during wrangler delete as a warning, not a reason to broaden this token. AI Gateway Read/Edit permissions are not needed for this deploy or a teardown before any media ran; inspect that additional resource if media has created a gateway.

npx wrangler delete deletes the Worker, not the entire Oyster. For a throwaway deployment, also empty and delete the Release bucket, delete the Computer’s container application, delete the Workflow, and delete the Artifacts repositories and namespace. Use the configured resource names and confirm each is gone through Cloudflare’s dashboard or API. Capture teardown errors and the Wrangler version rather than silently adding unrelated token permissions.

Oyster 0.3 does not read data created by Oyster 0.1 or 0.2, and it does not convert it. An Oyster deployed on 0.2 and redeployed on 0.3 answers every request with 409 and {"error": "store_from_older_oyster", ...} (a short explanation page in a browser), and oyster install-apps prints the same message. Old data is left untouched, so redeploying 0.2.x brings the Oyster back exactly as it was.

To move to 0.3, start from a fresh store. The smallest way is to deploy under new names, leaving the old Oyster in place until you no longer need it. In wrangler.jsonc, rename name, both bucket_name values, artifacts.namespace and every workflows[].name; create the new release bucket; then run the quick start again. To reuse the names instead, tear the old Oyster down as in the teardown steps above (delete the Worker, which deletes its Durable Object data; empty and delete the release bucket; delete the Computer’s container application, the Workflow, and the Artifacts repositories and namespace), then deploy 0.3 and reinstall the Apps. Data in the old Oyster, such as Apps’ stored data, is not carried over.

Choose an owner key (separate from the Cloudflare API token) and store it as a secret:

TERMINAL
export OYSTER_OWNER_KEY=$(openssl rand -hex 24)
printf '%s' "$OYSTER_OWNER_KEY" | npx wrangler secret put OYSTER_OWNER_KEY
TERMINAL
npx oyster install-apps

The installer clones each App’s repository, seeds it on your Oyster with git push, and builds the Apps on your Computer, Tourtois first, installing each as its build succeeds. It prints a line per step and then where the time went:

CODE
tourtois source recorded at git:…
…
Building tourtois on the Computer (build_…)
Installed tourtois from its build build_…
Building bonsai on the Computer (build_…)
…
Installed grove from its build build_…
App fetch seed queue build clone deps compile upload install
tourtois …
…
Installed 7 Apps in …. Open https://oyster.<your-subdomain>.workers.dev/ to use Tourtois and the installed Apps.

Open $OYSTER_URL/login and sign in with the owner key: your Oyster opens on the Tourtois desktop with all seven Apps, as in the pictures above.

Run install-apps again to update them; a replaced Release keeps each App’s data. --ref picks a branch or tag of the App repositories, --repositories another host for them, and --timings <file> writes the timing table as JSON. Builds run two at a time on the example’s standard-4 Computer (12 GiB). --concurrency 1 runs them one at a time and --concurrency 3 three at once.

People and agents clone, fetch and push App repositories with ordinary git at /git/<installation>/<repo>.git. Give each agent its own key; Oyster shows each key once:

TERMINAL
curl -s -X POST -H "Authorization: Bearer $OYSTER_OWNER_KEY" \
-H 'content-type: application/json' \
-d '{"name":"morel","grants":[{"capability":"apps.use","targets":{"kind":"all"}}]}' \
$OYSTER_URL/api/keys

The answer’s plaintext field, starting oyk_, signs in to git and to /mcp. That key can read and write every App on this Oyster, including Apps installed later, without asking each time; anyone holding it can act as the owner in those Apps, and revoking it stops it immediately.

Any MCP client can call every installed App with a key. For Claude Code:

TERMINAL
claude mcp add --transport http oyster "$OYSTER_URL/mcp" \
--header "Authorization: Bearer <the agent's key>"

https://oyster.shiit.app/ runs this same host with the same seven Apps. Its koi-pond repository is a small canvas game three coding agents built together, each in its own fork with its own key, with Bonsai drawing a lane per agent as they pushed and merged. CONTEST.md tours it.