# Quickstart

> Run the full stack on workerd with `pnpm run-local`, reach http://localhost:8787, and confirm success. Includes the first prompts that exercise bundled format blueprints versus from-scratch gadget creation, and which prompts require a configured gatekeeper.

- Repository: cloudflare/cloudflare-os
- GitHub: https://github.com/cloudflare/cloudflare-os
- Human docs: https://grok-wiki.com/public/docs/cloudflare-cloudflare-os-838773bb92dd
- Complete Markdown: https://grok-wiki.com/public/docs/cloudflare-cloudflare-os-838773bb92dd/llms-full.txt

## Source Files

- `README.md`
- `scripts/run-local.mjs`
- `run-dev-server.js`
- `wrangler.jsonc`
- `packages/workshop-backend/format-blueprints/README.md`

---

---
title: "Quickstart"
description: "Run the full stack on workerd with `pnpm run-local`, reach http://localhost:8787, and confirm success. Includes the first prompts that exercise bundled format blueprints versus from-scratch gadget creation, and which prompts require a configured gatekeeper."
---

`pnpm run-local` is the single command that boots the whole Cloudflare OS stack on wrangler and workerd. It installs dependencies if needed, builds only the two artifacts required to serve the app (`@gadgets/typed-storage` and the frontend Vite bundle), then launches `run-dev-server.js --serve-frontend-assets`, which serves the built frontend as static assets from the backend at http://localhost:8787. It is not meant for production use; it is the fastest way to see what the product does.

## Prerequisites

| Requirement | Notes |
| --- | --- |
| pnpm | The only supported package manager for this repo. |
| Node | `scripts/run-local.mjs` runs with `process.execPath`; it re-invokes the same Node binary for `run-dev-server.js` and the generators. |
| Cloudflare login | Only needed if you pass `--use-workers-ai-binding`. |

<Note>
A git checkout is preferred but not required. `run-local` hashes sources via `git ls-files` and falls back to a filesystem walk (skipping `.git`, `node_modules`, `dist`, `.wrangler`) when the tree is not a git checkout — for example an extracted tarball.
</Note>

## Run the stack

<Steps>
<Step title="Start the local stack">
```bash
pnpm run-local
```

Extra flags are forwarded verbatim to `run-dev-server.js`, so `pnpm run-local --use-workers-ai-binding` adds the Workers AI binding to `workshop-backend`.
</Step>

<Step title="Watch the startup output">
On a cold checkout you see the install and both builds, then the server line:

```text
> pnpm install
> pnpm --filter @gadgets/typed-storage build
> pnpm --filter @gadgets/workshop-frontend exec vite build

Starting local server at http://localhost:8787 ...
generated: /path/to/repo/wrangler.dev.jsonc
```

On a warm run with no source changes it short-circuits:

```text
Dependencies up to date; skipping install.
No source changes since last build; skipping build.

Starting local server at http://localhost:8787 ...
```
</Step>

<Step title="Open the app">
Visit http://localhost:8787. The request lands on the `dev-router` worker (`packages/router/src/index.ts`). Because the root `wrangler.jsonc` configures no `ASSETS` binding, the router forwards frontend requests to `workshop-backend`, which serves them from its own assets binding in `run-local` mode.
</Step>
</Steps>

## Confirm success

<Check>
Signals that the stack came up correctly:

- `generated: <repo>/wrangler.dev.jsonc` printed at startup — the dev router config was written with one service binding per discovered gatekeeper package.
- http://localhost:8787 serves the frontend rather than a 404 or connection refused.
- No `... watcher exited unexpectedly (code=…, signal=…)` lines on stderr; those come from the configurator/app UI watchers `run-dev-server.js` keeps alive.
- `.run-local-stamp` exists at the repo root after the build completes. It is written only after a successful build, so an interrupted build retries on the next run.
</Check>

The very first `/api` request also provisions the deployment: the bundled `.gadget` format blueprints in `packages/workshop-backend/format-blueprints/` are installed into the BLUEPRINTS KV namespace and the BLUEPRINT_CONTENT R2 bucket. Nothing wakes on deploy, so a fresh instance is provisioned by its first visitor.

## What run-local starts

```text
pnpm run-local  (scripts/run-local.mjs)
  ├─ source hash vs .run-local-stamp  → skip install/build when unchanged
  ├─ pnpm install                     (if needed)
  ├─ build @gadgets/typed-storage     → packages/typed-storage/dist
  ├─ vite build workshop-frontend     → packages/workshop-frontend/dist
  └─ run-dev-server.js --serve-frontend-assets
       ├─ load root .dev.vars into process.env (shell wins)
       ├─ build-format-blueprints.mjs  → src/generated/format-blueprints.ts
       ├─ discover packages/gatekeeper-*  (must have wrangler.jsonc)
       ├─ build + watch gatekeeper configurator / app UIs
       └─ write wrangler.dev.jsonc, then `wrangler dev`

  http://localhost:8787
       dev-router ──(no ASSETS binding)──▶ WORKSHOP_BACKEND ──▶ built frontend
```

Gatekeeper packages are discovered by name prefix (`gatekeeper-`) plus the presence of `wrangler.jsonc`, and each becomes a service binding whose name is the directory uppercased with `-` replaced by `_` — `gatekeeper-github` → `GATEKEEPER_GITHUB`.

## First prompts to try

The prompts below split into three groups by what they exercise.

| Prompt | Exercises | Needs a gatekeeper? |
| --- | --- | --- |
| "Make slides for my upcoming meeting with a customer." | Built-in slides blueprint | No |
| "Make a collaborative whiteboard app." | New gadget created from scratch | No |
| "Make a tic tac toe game." then "I'll be X and you be O. I've made my first move. Your turn." | New gadget plus agent interaction with the running gadget | No |
| "Make an issue dashboard for this GitHub repo." (attach a repo) | Gatekeeper-mediated external resource | Yes — GitHub integration configured |
| "Fix the typos in this Google Doc." (attach a doc) | Gatekeeper-mediated external resource | Yes — Google integration configured |

<Info>
The bundled format blueprints exist so a fresh deployment can write a doc or build a deck without anyone building one first. A *format* is an ordinary blueprint the deployment has promoted (`AdminConfig.formats`); the bundled set is the one it promotes out of the box.
</Info>

<Warning>
The two attachment prompts fail without credentials. `run-dev-server.js` seeds each connector's `CLIENT_ID` / `CLIENT_SECRET` from shared shell or `.dev.vars` variables — `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET` for `gatekeeper-github`, `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` for `gatekeeper-google`. Credentials already defined in a gatekeeper's own `wrangler.jsonc` win over the seeded values.
</Warning>

## Local configuration

<ParamField body=".dev.vars" type="KEY=VALUE file at repo root">
Loaded by `run-dev-server.js` before anything else. Blank lines and `#` comments are skipped; surrounding single or double quotes are stripped. Existing shell environment values take precedence over file values. The file is gitignored, so local secrets cannot leave the machine.
</ParamField>

<ParamField body="--use-workers-ai-binding" type="flag">
Includes the Workers AI binding in `workshop-backend`. Requires a Cloudflare login. Pass it through `pnpm run-local --use-workers-ai-binding`.
</ParamField>

<ParamField body="--serve-frontend-assets" type="flag">
Set automatically by `run-local`. The backend serves the pre-built frontend bundle as static assets because there is no Vite dev server in this mode.
</ParamField>

<ParamField body="VITE_BACKEND_HOST" type="string" default="localhost:8787">
`run-dev-server.js` derives the wrangler port from this value via `getWranglerPortFromBackendHost`, so `VITE_BACKEND_HOST=localhost:9000` also passes `--port 9000` to `wrangler dev`.
</ParamField>

<ParamField body="FORMAT_BLUEPRINTS_DIR" type="path" default="packages/workshop-backend/format-blueprints">
Points the build at a different format-blueprint directory. Whatever directory it names *is* the deployment's format set — it replaces the bundled one rather than adding to it. Same `<name>.gadget` + `<name>.json` layout.
</ParamField>

## Troubleshooting

<AccordionGroup>
<Accordion title="Nothing rebuilt after I edited a file">
`run-local` skips install and build only when the source hash matches `.run-local-stamp` **and** both `packages/workshop-frontend/dist` and `packages/typed-storage/dist` exist. The hash covers everything git tracks plus untracked-but-not-ignored files, including `pnpm-lock.yaml`, so any tracked source change flips it. If a change lives in an ignored path, it is invisible to the stamp.
</Accordion>

<Accordion title="Missing generated module on a clean checkout">
`src/generated/format-blueprints.ts` is gitignored and therefore absent on a fresh clone. `run-dev-server.js` runs `packages/workshop-backend/scripts/build-format-blueprints.mjs` before wrangler bundles the backend, so start through `pnpm run-local` (or `run-dev-server.js`) rather than invoking `wrangler dev` directly.
</Accordion>

<Accordion title="A gatekeeper does not appear">
Discovery requires both conditions: the directory name under `packages/` starts with `gatekeeper-`, and `packages/<name>/wrangler.jsonc` is a file. If either fails the package is silently skipped and no `GATEKEEPER_*` service binding is added to `wrangler.dev.jsonc`.
</Accordion>

<Accordion title="Port 8787 is taken">
Set `VITE_BACKEND_HOST` to another host:port before starting; the port is forwarded to `wrangler dev` as `--port`.
</Accordion>

<Accordion title="Interrupted build, then a stale-looking run">
The stamp is written only after both builds succeed. If you `Ctrl-C` mid-build, the next `pnpm run-local` rebuilds. `SIGINT` exits 130 and `SIGTERM` exits 143 after stopping the UI watchers.
</Accordion>
</AccordionGroup>

<Note>
This release is early access. Cloudflare OS v2 is a complete rewrite of v1 and, as of the August 2026 release, is capable but still has rough edges.
</Note>

## Next

<CardGroup cols={2}>
<Card title="Installation" href="/installation">
The three install paths, what `run-local` builds, its source-hash stamp caching, and where local data is written.
</Card>
<Card title="Local development" href="/local-development">
The two-terminal workflow with `pnpm dev-server` and `pnpm dev-client` on Vite port 3000.
</Card>
<Card title="Configure gatekeeper credentials" href="/configure-gatekeeper-credentials">
Register a third-party OAuth app and wire `CLIENT_ID` / `CLIENT_SECRET` so the GitHub and Google prompts work.
</Card>
<Card title="Manage bundled format blueprints" href="/bundled-format-blueprints">
The `.gadget` plus `.json` sidecar split, `FORMAT_BLUEPRINTS_DIR`, and `pnpm import:format-blueprint`.
</Card>
<Card title="Overview" href="/overview">
The router origin, Workers kernel, gadget sandboxes, and gatekeeper connectors.
</Card>
<Card title="Routing and worker bindings" href="/routing-and-bindings">
How the router resolves `/api/*`, `/gatekeeper/<name>/*`, and asset requests.
</Card>
</CardGroup>
