Guides
Configuration
The common things a stack needs, one at a time. Every option is listed, with its type and default, in the config options reference.
The config file
braid reads braid.config.ts from the directory you run it in,
or braid.config.mts if that's what's there (use it in a project
whose package.json isn't "type": "module"), or the
file given to --config. It default-exports an object
whose processes lists everything to run:
import { defineConfig } from "@aip-tech/braid";
export default defineConfig({
processes: [
{
name: "api",
command: "pnpm",
args: ["--filter", "./api", "dev"],
cwd: ".",
env: { NODE_ENV: "development" },
color: "blue",
},
],
});
Each process needs a unique name and a command.
Its env is visible to that process only.
Restarting on file changes
Give a process watch paths and braid restarts it when a
matching file changes. Leave it out for anything with its own hot reload.
{
name: "api",
command: "pnpm",
args: ["--filter", "./api", "dev"],
watch: ["api/src"],
ext: "ts,graphql,json",
exclude: ["api/src/__generated__"],
}
ext filters by extension (default ts,js,json).
exclude leaves a path and everything under it out of the
watch, which stops generated files from triggering their own restart.
Run braid start --no-watch to switch watching off for one run.
Processes that depend on each other
Five options cover the ways one process can rely on another. Each is independent, so combine only the ones you need:
| Option | What it does |
|---|---|
readyPattern | Defines when a process counts as ready: the first output line matching this regex. |
startAfter | Holds a process's first start until the processes it names are ready. |
dependsOn | Restarts a process whenever a process it depends on restarts, optionally running a command in between. |
onRestart | Runs a command after a process restarts itself. |
beforeRestart | Runs a command after a watched process stops and before it starts again. |
A typical pairing: a web app that waits for its API on first start, and regenerates its API client whenever the API restarts.
{
name: "api",
command: "pnpm",
args: ["--filter", "./api", "dev"],
watch: ["api/src"],
readyPattern: "^Server listening",
},
{
name: "web",
command: "pnpm",
args: ["--filter", "./web", "dev"],
startAfter: { processes: ["api"] },
dependsOn: {
processes: ["api"],
run: { command: "pnpm", args: ["--filter", "./web", "codegen"] },
},
}
When api restarts, web waits until
api prints its ready line, stops, runs the codegen command, then
starts again. Hook commands are retried (5 times, 1 second apart by
default), since the process they rely on may still be warming up.
readyPattern is matched against each line exactly as the app
printed it, so anchors like ^ work. If it never matches
within readyTimeoutMs (10 seconds by default), braid notes it
in the process's log and carries on.
Circular dependsOn or startAfter chains, and names
that don't exist, are rejected when the stack starts.
When a process crashes
By default, any process exiting with a non-zero code stops the whole
stack, so you're never debugging half of it. To restart just that process
instead, turn on autoRestart:
{
name: "worker",
command: "node",
args: ["worker.js"],
autoRestart: true,
maxRestarts: 10,
restartDelayMs: 1000,
}
Restarts back off exponentially (doubling, up to 10 seconds). After
maxRestarts crashes in a row, braid gives up and stops the
stack. A command that can't be started at all, such as a typo, stops the
stack straight away rather than retrying.
Stopping cleanly
braid stops a process with SIGTERM, then SIGKILL
if it's still running after stopTimeoutMs (5 seconds by
default). Raise it for an app with slow cleanup:
{ name: "db", command: "./start-db.sh", stopTimeoutMs: 15000 }
Everything an app prints while shutting down still reaches its log.
A process started with pnpm --filter ./api run dev may print
ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL or
ELIFECYCLE Command failed whenever braid stops it. pnpm reports
its script ending on SIGTERM as a failure. braid knows it
stopped the process, so it's harmless. To avoid it, run the tool with
pnpm exec from the package's folder:
{
name: "api",
command: "pnpm",
args: ["exec", "tsx", "index.ts"],
cwd: "api",
watch: ["src"],
}
watch paths are relative to cwd.
Processes you start yourself
autoStart: false keeps a process out of
braid start. It's still listed in braid status,
and you start it when you need it:
{
name: "report",
command: "pnpm",
args: ["--filter", "./jobs", "nightly-report"],
autoStart: false,
}
npx braid start report
Logs
Each process writes to .braid/logs/<name>.log, with one
rotated backup. Logs rotate on every start, on watch and crash
restarts, and once a file passes logs.maxSizeBytes (5 MB by
default):
export default defineConfig({
processes: [/* ... */],
logs: { dir: ".braid/logs", maxSizeBytes: 5_242_880, timestamps: true },
});
timestamps prefixes every line with the time. braid's own
messages, such as plugin errors, go to .braid/daemon.log. Read
logs with braid logs.
Foreground or background
braid start runs the stack in a background daemon. Set
foreground: true, or pass --foreground, to keep
it attached to the terminal instead, streaming every process's output
until Ctrl+C. --daemon forces the
background mode whatever the config says.
Resource usage
braid status and the dashboard show each process's CPU and
memory, covering the app and everything it starts (the real server under
a pnpm dev, a bundler's workers). Figures refresh every
statsPollIntervalMs (2 seconds by default). CPU appears from
the second sample.
On macOS, memory is the physical footprint Activity Monitor shows; on Linux it's RSS. Windows gets no figures.