# Local Development



`ccp dev` starts a local HTTP server running your function in a real V8 isolate — the same runtime used in production.

## Start the Dev Server [#start-the-dev-server]

```bash
ccp dev
# ◼ Dev Server started!
# › http://0.0.0.0:8000
```

The server watches your project root recursively and hot-reloads on any change. Common build/output directories are ignored: `node_modules`, `.cluster`, `.git`, `dist`, `build`, `.next`, `.turbo`, and `target`.

If you have a preview page open in a browser, it reloads automatically after each successful rebuild — see [Live Preview Reload](#live-preview-reload) below.

## Options [#options]

| Flag                      | Default   | Description                                                                                   |
| ------------------------- | --------- | --------------------------------------------------------------------------------------------- |
| `--port <port>`           | `8000`    | Port to listen on                                                                             |
| `--hostname <host>`       | `0.0.0.0` | Hostname to bind to                                                                           |
| `--env <path>`            | `.env`    | Path to a custom env file                                                                     |
| `--public-dir <path>`     | —         | Serve static assets from this directory                                                       |
| `--client <path>`         | —         | Include a client-side script                                                                  |
| `--allow-code-generation` | `false`   | Allow `eval()` and `new Function()`                                                           |
| `--prod` / `--production` | `false`   | Set `process.env.NODE_ENV` to `"production"`                                                  |
| `--headless`              | auto      | Suppress styled banners; emit one-line `[dev] ...` logs. Auto-enabled when stdout isn't a TTY |

The default bind is `0.0.0.0:8000` in both interactive and headless runs — `0.0.0.0` listens on all IPv4 interfaces, including `localhost`, so no separate localhost listener is needed. This matches the port that Build's Sandbox preview routes by default. Pass `--port` and `--hostname` to override independently; e.g. `--hostname 127.0.0.1` for localhost-only access.

## Environment Variables [#environment-variables]

CCP automatically loads a `.env` file from the project root if one exists:

```bash
# .env
API_KEY=sk-abc123
DEBUG=true
```

```bash
ccp dev
# Automatically loaded .env file...
# ◼ Dev Server started!
```

Point to a different env file with `--env`:

```bash
ccp dev --env .env.staging
```

Variables are available in your handler via `process.env`:

```ts
export function handler(request: Request): Response {
  const key = process.env.API_KEY;
  return new Response(`Key: ${key}`);
}
```

## Static Assets [#static-assets]

For static sites, pass the public directory:

```bash
ccp dev --public-dir public
```

Static files are served directly. Non-matching paths fall through to your handler. See [Static Sites](/docs/ccp/static-sites) for more details.

## Logs [#logs]

`console.log()`, `console.error()`, and `console.warn()` output directly to your terminal with level prefixes:

```
INFO Hello from the handler
ERROR Something went wrong
WARN Deprecated API usage
```

## Timeouts [#timeouts]

The local dev server applies the same timeouts as production:

* **Tick timeout**: 500ms per I/O tick
* **Total timeout**: 30 seconds per request

## Headless Mode [#headless-mode]

For CI, dev VMs, and AI agents, run with `CCP_HEADLESS=1` (or pass `--headless`). The server skips styled banners and screen clears, emits terse `[dev] ...` lines suitable for log capture, and binds to `0.0.0.0` so a parent process can reach it.

```bash
CCP_HEADLESS=1 ccp dev --port 3000
# [dev] listening on http://0.0.0.0:3000
```

See [Headless Mode](/docs/ccp/headless) for the full reference.

## Live Preview Reload [#live-preview-reload]

`ccp dev` watches for file creates, edits, and deletes, coalescing bursts of
saves into a single rebuild. When a browser has a preview page open, it
reloads that page automatically after each successful rebuild — no need to
refresh manually or re-open the preview URL. A failed rebuild keeps the last
working page on screen and shows a build-failure notice instead of tearing
down the preview; refreshing the page cannot fix a failed build, only a new
successful revision can.

This works by injecting a small development-only client script into HTML
responses with a streaming parser. Non-HTML and already-compressed responses
are left untouched, and the client is never included in `--prod` builds or
deployed output.

Check the current build state at any time with the status endpoint:

```bash
curl -fsS http://127.0.0.1:8000/_cluster/dev/status
```

It reports the build status (`ready`, `building`, or `failed`) and the current
revision, and contains no source code or logs.
