Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,8 @@ Be aware that workerd uses tcmalloc for memory allocation in the typical case. W

### Configuration System

- Uses **Cap'n Proto** for configuration files (`.capnp` format)
- Uses **Cap'n Proto** for configuration files (`.capnp` format); a `wrangler.json(c)` is also
accepted and translated (`src/workerd/server/cli/wrangler.rs`)
- Main schema: `src/workerd/server/workerd.capnp`
- Sample configurations in `samples/` directory
- Configuration uses capability-based security model
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ bazel build --config=thin-lto //src/workerd/server:workerd

`workerd` is configured using a config file written in Cap'n Proto text format.

For simple projects, `workerd serve wrangler.jsonc` also accepts a Wrangler
config. `main` is bundled with esbuild, as Wrangler does. The supported fields
are `name`, `main`, `compatibility_date`, `compatibility_flags`, `vars`,
`durable_objects`, `migrations`, `no_bundle` and `$schema`; any other field
is an error.

A simple "Hello World!" config file might look like:

```capnp
Expand Down
1 change: 1 addition & 0 deletions samples/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
exports_files([
"helloworld/config.capnp",
"helloworld/worker.js",
"wrangler/wrangler.jsonc",
])
1 change: 1 addition & 0 deletions samples/wrangler/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.wrangler/
32 changes: 32 additions & 0 deletions samples/wrangler/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Wrangler Config Example

workerd can run a project configured with a `wrangler.jsonc` instead of a
`config.capnp`. This one is a TypeScript Worker with a SQLite-backed Durable
Object that counts visits per path.

As Wrangler does, workerd bundles `main` with esbuild, so esbuild must be
installed: in the project (`npm i -D esbuild`), on `PATH`, or named by
`WORKERD_ESBUILD`.

To run the example on http://localhost:8787

```sh
$ ./workerd serve wrangler.jsonc
```

To run using bazel

```sh
$ bazel run //src/workerd/server:workerd -- serve ~/cloudflare/workerd/samples/wrangler/wrangler.jsonc
```

To test:

```sh
$ curl http://localhost:8787/alice
Hello, alice! (visit 1)
$ curl http://localhost:8787/alice
Hello, alice! (visit 2)
```

Counts are stored under `.wrangler/state/workerd/` and survive a restart.
30 changes: 30 additions & 0 deletions samples/wrangler/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) 2026 Cloudflare, Inc.
// Licensed under the Apache 2.0 license found in the LICENSE file or at:
// https://opensource.org/licenses/Apache-2.0

import { DurableObject } from 'cloudflare:workers';

interface Env {
GREETING: string;
COUNTER: DurableObjectNamespace<Counter>;
}

// One counter per name, stored in the object's SQLite database.
export class Counter extends DurableObject<Env> {
increment(): number {
const sql = this.ctx.storage.sql;
sql.exec('CREATE TABLE IF NOT EXISTS n (v INTEGER)');
const v = (sql.exec('SELECT v FROM n').toArray()[0]?.v as number) ?? 0;
sql.exec('DELETE FROM n');
sql.exec('INSERT INTO n VALUES (?)', v + 1);
return v + 1;
}
}

export default {
async fetch(request: Request, env: Env): Promise<Response> {
const name = new URL(request.url).pathname.slice(1) || 'world';
const n = await env.COUNTER.getByName(name).increment();
return new Response(`${env.GREETING}, ${name}! (visit ${n})\n`);
Comment thread
ask-bonk[bot] marked this conversation as resolved.
},
} satisfies ExportedHandler<Env>;
11 changes: 11 additions & 0 deletions samples/wrangler/wrangler.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
// workerd reads this file directly: `workerd serve wrangler.jsonc`.
"name": "counter",
"main": "src/index.ts",
"compatibility_date": "2026-10-01",
"vars": { "GREETING": "Hello" },
"durable_objects": {
"bindings": [{ "name": "COUNTER", "class_name": "Counter" }],
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["Counter"] }],
}
1 change: 1 addition & 0 deletions src/workerd/server/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Bazel labels: `//src/workerd/server:workerd`, `//src/workerd/server:workerd-cli`
| `process.rs` | Executable discovery, the compiled-binary format, `Process` (the files to watch, re-exec) |
| `socket_fd.rs` | `--socket-fd` / `--control-fd` descriptors, made owned; the platform halves are `socket_fd/{unix,windows}.rs` |
| `watch.rs` | `--watch`: the file watcher |
| `wrangler.rs` | A `wrangler.json(c)` config: the supported fields translated into a `Config`, `main` bundled with esbuild (or scanned under `no_bundle`) |
| `bridge.rs` | The cxx bridge (`workerd::server::cli`) to `config-compiler.h` and the build facts in `factory/bootstrap.h`; header `<workerd/server/cli/bridge.rs.h>` |

### `server/` (`workerd-server`)
Expand Down
3 changes: 3 additions & 0 deletions src/workerd/server/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,15 @@ wd_rust_crate(
cxx_bridge_hdrs = ["config-compiler.h"],
cxx_bridge_src = "cli/bridge.rs",
link_deps = [":config-compiler"],
test_compile_data = ["//samples:wrangler/wrangler.jsonc"],
deps = [
":workerd_capnp_rust",
"//src/rust/cxx/kj-rs-io",
"//src/workerd/server/server:workerd-server",
"@crates_vendor//:capnp",
"@crates_vendor//:clap",
"@crates_vendor//:serde",
"@crates_vendor//:serde_json",
"@crates_vendor//:socket2",
"@crates_vendor//:tokio",
] + select({
Expand Down
2 changes: 2 additions & 0 deletions src/workerd/server/cli/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ pub struct ConfigFileArgs {
#[arg(short, long)]
pub binary: bool,

/// A Cap'n Proto config (`.capnp`), or a `wrangler.json` / `wrangler.jsonc`, whose `main` is
/// bundled with esbuild as `wrangler` does.
#[arg(value_name = "config-file")]
pub config_file: String,
}
Expand Down
19 changes: 18 additions & 1 deletion src/workerd/server/cli/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod config;
mod process;
mod socket_fd;
mod watch;
mod wrangler;

use std::fmt::Display;
use std::io::IsTerminal;
Expand Down Expand Up @@ -350,6 +351,16 @@ fn load_config<T: CommandFactory>(
if config_file == "-" {
usage_error::<T>("-: Reading config from stdin is only allowed with --binary.");
}
if wrangler::is_wrangler_config(&config_file) {
if let Some(name) = const_name {
usage_error::<T>(format!("{name}: A wrangler config has no named constants."));
}
if !std::path::Path::new(&config_file).is_file() {
Comment thread
ry marked this conversation as resolved.
usage_error::<T>(format!("{config_file}: No such file."));
}
return wrangler::load(&config_file, process)
.unwrap_or_else(|error| config_errors(&[format!("{config_file}: {error}")], process));
}

let compiled = ffi::compile_config(
&config_file,
Expand All @@ -363,7 +374,13 @@ fn load_config<T: CommandFactory>(
.unwrap_or_else(|error| usage_error::<T>(format!("{config_file}: {error}")));
}

for error in &compiled.errors {
config_errors(&compiled.errors, process)
}

/// Prints errors in the config. Without `--watch` they end the process; with it the process waits
/// for the files to change and reloads.
fn config_errors(errors: &[impl Display], process: &Process) -> ! {
for error in errors {
eprintln!("{error}");
}
let Some(watcher) = process.watcher() else {
Expand Down
Loading
Loading