goribu.config.js
Default-export the result of defineConfig() from goribu/server:
import { defineConfig } from "goribu/server";
export default defineConfig({
name: "my-app",
compatibilityDate: "2026-09-01",
database: { type: "d1", name: "my-app-db" },
kv: { name: "my-app-kv" },
});
- Optional resources: D1 and KV declarations are optional.
- Node-loadable config: Keep
goribu.config.jsplain JavaScript, including in TypeScript apps. Node loads it directly; do not import JSX, a Document, or application components.
Options and defaults
| Option | Meaning and default |
|---|---|
name | Required Worker name. Lowercase letters, digits, and hyphens; no leading or trailing hyphen; at most 63 characters. |
domain | Optional application hostname connected during deployment. Omitted means no domain management. Available since 0.9.0. |
workersDev | Optional boolean. false turns off the workers.dev address and requires domain. Defaults to true. Available since 0.9.2. |
compatibilityDate | Required real date in YYYY-MM-DD format. |
compatibilityFlags | Optional array of Workers compatibility flags. Defaults to an empty array. |
database | Optional single D1 declaration. Omitted means no D1 binding. |
kv | Optional single KV namespace. Omitted means no KV binding. |
responseHeaders | Optional synchronous callback for Worker response defaults. |
- Unknown fields: Goribu rejects unknown configuration fields.
- Generated configuration: Edit
goribu.config.jsrather than generatedgoribu.wrangler.jsoncor production output.
Custom domain
Available in Goribu 0.9.0. The top-level domain option connects an application hostname during deployment.
Add one line to connect your application to a custom hostname:
import { defineConfig } from "goribu/server";
export default defineConfig({
name: "my-app",
compatibilityDate: "2026-10-02",
domain: "app.example.com",
});
- Hostname format: Use one bare hostname: a zone apex such as
example.comor subdomain such asapp.example.com. Goribu normalizes it to lowercase. - Rejected hostnames: Schemes, paths, ports, IP addresses, wildcards, and Cloudflare-managed
workers.devorr2.devaddresses are rejected. - Zone ownership: The hostname must belong to an active Cloudflare zone in the deployment account. Goribu finds the zone automatically.
- Browser login: Normal
goribu cloudflare-loginworks for custom domains. No separate DNS Read permission is required. - API tokens: Include Workers Scripts Write and Zone Read for the target zone alongside the app's other deployment permissions.
- Connection preview:
pnpm checkpreviews a domain connection without changing resources. - Attachment order:
pnpm run deploychecks ownership before provisioning, uploads the Worker, then checks again and attaches the hostname. For a new Worker, the domain conflict preview runs after its first upload. - Existing assignments: A connection to this Worker is reused. Conflicting assignments block the operation; Cloudflare's replacement overrides remain disabled.
- Activation: Cloudflare creates DNS records and a TLS certificate. Activation may still be pending after attachment.
- Attachment failure: If connection fails after upload, the Worker remains deployed and the command reports an error. Resolve the problem and retry deployment.
- Other addresses: The
workers.devaddress remains available unless you turn it off. Other attached domains also remain available. - Old hostnames: Changing or removing
domainnever disconnects an old hostname. Other attachments are listed as information. - Alias management: Manage aliases such as
www, redirects, and unwanted connections separately in Cloudflare. - Media hostname: Use a different hostname for
r2.domain, which serves bucket files. - Upload CORS: The planned application origin is included in R2 direct-upload CORS before domain attachment.
- Local validation: Development and standalone builds validate the hostname without contacting Cloudflare.
workers.dev address
Available in Goribu 0.9.2. Every app is also served at https://<name>.<account-subdomain>.workers.dev. Once the app has its own domain, that second address may be unwanted: OAuth callbacks, cookies, and CORS are tied to one host. Turn it off with workersDev: false:
import { defineConfig } from "goribu/server";
export default defineConfig({
name: "my-app",
compatibilityDate: "2026-10-02",
domain: "app.example.com",
workersDev: false,
});
- Required domain:
workersDev: falserequiresdomain; otherwise the app would have no address. - Setting ownership: Each deploy applies
workersDev. Remove the field to restore workers.dev rather than changing the dashboard setting. - Activation timing: Keep the default until the custom domain is connected and serving. DNS and TLS may need a few minutes; workers.dev works immediately.
- Upload origin: R2 direct-upload CORS drops the workers.dev origin when that address is disabled.
- Preview URLs: Deploys always turn off Cloudflare's per-version preview URLs. Goribu deploys production only; previews would be additional public app addresses.
- One-time warning: The first deploy disabling preview URLs on an existing Worker may print a Wrangler warning. No action is needed.
Resources
- D1 fields: A D1 declaration accepts
type: "d1",name, and optionalreplication. - KV fields: A KV declaration accepts only
name. - Resource names: D1 and KV names may contain lowercase letters, digits, hyphens, and underscores. Start with a letter or digit and use at most 63 characters.
Goribu owns the DB and KV bindings. You do not supply resource IDs or duplicate them in a Wrangler file.
- Replication default: Omitted replication enables it on new D1 databases and leaves existing settings untouched.
- Replication reconciliation: Explicit
trueorfalsereconciles replication during deployment. See D1 and KV. - Resource selection: Changing a resource name selects a different resource. It does not rename or delete the previous resource.
- Declaration removal: Removing a declaration leaves the production resource intact.
Environment variables
Use .env for local values and .env.local for local overrides:
CATALOG_API_TOKEN=local-placeholder
PUBLIC_API_ORIGIN=https://api.example.com
- Server values: Read server values through
req.envin handlers or middleware. - Browser values: Only
PUBLIC_*variables are available to browser code throughimport.meta.env. Values are embedded at build time and must not contain secrets.
import type { Handler } from "goribu/server";
type Env = { CATALOG_API_TOKEN: string };
// Keep this value on the server; never return it in page props.
export const GET: Handler<Record<string, unknown>, Env> = (req, res) => {
const token = req.env.CATALOG_API_TOKEN;
return res.json({ configured: Boolean(token) });
};
// Keep this value on the server; never return it in page props.
export const GET = (req, res) => {
const token = req.env.CATALOG_API_TOKEN;
return res.json({ configured: Boolean(token) });
};
// In browser-reachable application code; this value is public.
const apiOrigin = import.meta.env.PUBLIC_API_ORIGIN;
// In browser-reachable application code; this value is public.
const apiOrigin = import.meta.env.PUBLIC_API_ORIGIN;
- Worker-specific file: Cloudflare supports
.dev.varsfor local Worker bindings. - File precedence: If
.dev.varsexists, even empty, it replaces loading of both.envand.env.localfor the server. Choose one workflow; these files do not merge. - Local secrets exclusion: Build output never contains local server values.
- Build log wording: Cloudflare's Vite plugin still prints "Using secrets defined in .env" during a build. Since 0.9.2, Goribu removes that local copy before output is written, so nothing local is uploaded.
- Server build constants:
import.meta.env.DEV,PROD, andMODEwork in handlers and server modules. - Development guards: Vite replaces these values with constants. Production builds drop code that runs only when
DEVis true; use this for development-only routes:
import type { Handler } from "goribu/server";
// src/routes/dev-login.ts
export const POST: Handler = async (req, res) => {
if (import.meta.env.DEV) {
req.cookies.set("session", await createDevSession(req), { path: "/", httpOnly: true });
return res.redirect("/");
}
return res.notFound();
};
// src/routes/dev-login.js
export const POST = async (req, res) => {
if (import.meta.env.DEV) {
req.cookies.set("session", await createDevSession(req), { path: "/", httpOnly: true });
return res.redirect("/");
}
return res.notFound();
};
- Browser mode files: Browser values follow Vite's
.env,.env.local,.env.development,.env.production, and mode-specific local overrides. - Server mode distinction: A value in
.env.developmentdoes not become a server binding just because Vite can see it.
Production secrets
- Production source: Put production values in
.env.production. Deployment validates it with Wrangler and uploads its keys as Worker secrets. - Existing secrets: Existing secrets omitted from
.env.productionremain on the Worker. - Missing file: Without
.env.production, Goribu uses existing platform secrets. Local.envis never an upload fallback. - Public prefix: A
PUBLIC_*value stays public even when the same key is uploaded as a Worker secret. - Shared placeholders: Keep real environment files out of Git. Share placeholder names in
.env.example.
Config evaluation does not load dotenv files into process.env. If the config reads an environment variable, supply it in the shell running the command.
Response headers
Use a callback for defaults on Worker-served responses:
import { defineConfig } from "goribu/server";
export default defineConfig({
name: "my-app",
compatibilityDate: "2026-09-01",
// Return ordinary header values synchronously.
responseHeaders({ request, env, mode, nonce }) {
return { "X-Content-Type-Options": "nosniff" };
},
});
- Callback arguments:
responseHeadersreceives the original Request, Worker environment, development/production mode, and request nonce. - Return contract: Return a plain object of strings or arrays of strings synchronously.
- Header precedence: Route and framework headers take precedence over callback defaults.
Varytokens merge. - Worker-rendered static pages: A static page rendered by the Worker, in development or after a pre-render fallback, receives these headers and keeps its nonce. Prerendered HTML has neither.
- Disallowed defaults: Body, redirect, cookie, and transport headers cannot be blanket defaults:
Content-Type,Content-Length,Content-Range,Content-Encoding,Location,Set-Cookie,Trailer, andTransfer-Encoding. - Response ownership: Set those headers through the API that owns the response.
- Static Assets:
responseHeadersdoes not run for Static Assets. App-authoredpublic/_headersis rejected, so this API cannot establish a custom static-page header policy. - Security policy defaults: Goribu does not set a default CSP or CORS policy.
Development and build settings
- Vite configuration: Put Vite options in
vite.config.js, not the Goribu app config. Keepgoribu()in the plugins list. - CSS tooling: Tailwind is an app plugin. Ordinary CSS works too.
- Config reloads: Changes to
goribu.config.jsorvite.config.jsrestart the development session. - Invalid replacements: Invalid replacement config is reported while the existing session continues. Fix it before relying on new settings.
- Undiscovered routes: Restart
pnpm devif a newly added route is not recognized.
Import aliases
Add import aliases through Vite's resolve.alias option in vite.config.js. This example maps @ to your src directory using an absolute path. Merge it into your existing config, keeping any other plugins and settings:
import { fileURLToPath } from "node:url";
import { goribu } from "goribu/vite";
export default {
plugins: [goribu()],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
};
- Alias usage: Application code can use
import Button from "@/components/button"forsrc/components/button.tsxsrc/components/button.jsx. - Alias scope: Vite aliases apply during development and production builds.
- Config imports: Keep
goribu.config.jsimports Node-compatible. Node loads that file directly without Vite aliases.