Skip to content
Crumble
Esc
↑↓navigate↵open⌘Jpreview
On this page

Configuration

Environment variables for the Crumble server, and what to set before exposing it to a network.

You configure the server with environment variables. Most have sensible defaults, and the ones that don’t are the ones this page spends time on.

When you run bun run dev, the coding and desktop images default to their published :main versions. Every other startup command leaves both images unset.

Exposing the Server

Sign-up starts open, so anyone who can reach the server can create an account. That’s harmless on 127.0.0.1, where it listens by default. To use it from other devices:

  1. Close sign-up, as described in Users and Sign-Up.
  2. Put the server behind a reverse proxy that serves HTTPS.
  3. Set CRUMBLE_PUBLIC_URL to the public https:// address.

If sign-in fails through the proxy or from another hostname, the server doesn’t know its public address: CRUMBLE_PUBLIC_URL is missing or wrong.

Users and Sign-Up

The first account on a server is its admin. A server that had accounts before admins existed makes its oldest account the admin.

Admins manage accounts in Settings > Users, a tab only admins see:

  • Users: every account, with an action to make it an admin or a regular user, or to remove it with all its bots and data. The server keeps at least one admin.
  • Invite: creates a one-time link for one new account. The link is shown once; revoke an unused one from the list.
  • Open sign-up: turn it off to allow new accounts only through invite links.

The settings every workspace shares are in Settings > Coding; see Set Up Workspaces.

Regular users manage only their own bots. Over the API, the admin routes are under /v1/admin/.

Passkeys

Passkeys belong to the hostname of the server’s address. The server takes it from CRUMBLE_PUBLIC_URL, then BETTER_AUTH_URL, then localhost when it listens on loopback.

Browsers don’t offer passkeys at an IP address, or at a plain http:// address other than localhost. Changing the hostname later invalidates every passkey. Accounts then sign in with their passwords until they add new ones. An account created with a passkey alone has no password, so it can’t sign in at the new hostname.

Encryption Key

Each account encrypts its credentials with its own key. Where that key lives depends on CRUMBLE_MASTER_KEY.

Without a master key, the account key sits in .secret.key, next to the database it protects, and the server says so at startup. Anyone who can read the data directory, or a backup of it, can decrypt the credentials.

To keep a usable key out of the data directory, generate a master key and store it with the server’s other secrets:

openssl rand -base64 32

Set CRUMBLE_MASTER_KEY to it and restart the server. On start, each account’s key is wrapped with the master key and stored in .secret.key.wrapped, and the plain .secret.key is deleted. New accounts only ever get the wrapped file.

From then on, the server refuses to start an account if CRUMBLE_MASTER_KEY is missing or different, and it never replaces a key that has encrypted data. If you lose the master key, the account’s credentials can’t be recovered.

Removing the Master Key

Stop the server first. Then, in each account’s directory, CRUMBLE_DATA_DIR/users/USER_ID, unwrap the key with the master key still in the environment:

bun -e 'const c = require("node:crypto"), f = require("node:fs");
const [iv, tag, data] = f.readFileSync(".secret.key.wrapped", "utf8").trim().split(":").map((p) => Buffer.from(p, "base64"));
const d = c.createDecipheriv("aes-256-gcm", Buffer.from(process.env.CRUMBLE_MASTER_KEY ?? "", "base64"), iv);
d.setAuthTag(tag);
const key = Buffer.concat([d.update(data), d.final()]);
if (key.length !== 32) throw new Error("not an account key");
const fd = f.openSync(".secret.key", "wx", 0o600);
f.writeSync(fd, key);
f.fsyncSync(fd);
f.closeSync(fd);' && sync && rm .secret.key.wrapped

The wrapped file is deleted only after the plain key is unwrapped and saved to disk. If unwrapping fails, both files stay as they were.

Then unset CRUMBLE_MASTER_KEY and start the server.

Environment Variables

Variable Default Purpose
CRUMBLE_DATA_DIR ./data Where the server stores accounts and all account data.
CRUMBLE_HOST 127.0.0.1 Listen address. Keep it on loopback unless the server is behind HTTPS.
CRUMBLE_PORT 7337 Listen port.
CRUMBLE_PUBLIC_URL Unset Public https:// address, used for sign-in and for MCP OAuth client metadata.
BETTER_AUTH_URL The listen address Browser address for sign-in. CRUMBLE_PUBLIC_URL overrides it.
CRUMBLE_MASTER_KEY Unset 32 random bytes in base64 that wrap each account’s encryption key. See Encryption Key.
CRUMBLE_SKILLS_DIR ./skills Directory of default skills.
CRUMBLE_WORKSPACE_BACKEND Microsandbox Set to railway to run workspaces on Railway. See Set Up Workspaces.
CRUMBLE_WORKSPACE_IMAGE Unset Microsandbox image for workspaces. Enables coding jobs.
CRUMBLE_WORKSPACE_DESKTOP_IMAGE Unset Microsandbox image with a desktop. Enables bot desktops.
CRUMBLE_RAILWAY_REGION Railway’s default Region for new Railway workspaces, such as europe-west4.
CRUMBLE_CLAUDE_COMMAND claude Path to the Claude Code executable, if it isn’t on PATH.
CRUMBLE_CLAUDE_TELEMETRY Enabled Set to 0, false, no, or off to turn off Claude Code telemetry.
NODE_ENV Unset Set to production to minify the web app.

Was this page helpful?