Braide stores its settings at:
~/.braide/settings.json
This file is managed automatically by the app. It contains:
{
"theme": "system",
"selectedProjectId": "...",
"enabledAgents": ["agent-id-1", "agent-id-2"],
"registries": [
{ "url": "https://...", "active": true }
],
"terminalStates": {
"session-id": { "open": true, "height": 300 }
},
"agentMeta": {
"agent-id-1": { "tenant": "acme", "debug": true }
},
"agentProcessIsolation": {
"agent-id-1": "per-session"
}
}
The agentMeta object holds per-agent JSON metadata that Braide attaches as _meta on the newSession request for that agent — once per session at creation. Keys are agent IDs; values are arbitrary JSON objects specific to what that agent understands.
The meta is edited from the cog icon on each agent card in Settings > Agents. Changes are not auto-applied: an Apply button on the agent card restarts the agent with the new meta when you're ready. See Managing Agents > Additional Settings and ACP Protocol > Agent-Specific Metadata for details.
An absent or empty object for a given agent ID is treated the same as having no meta at all — no _meta field is sent on requests to that agent.
The agentProcessIsolation object opts individual agents into one process per session mode. Keys are agent IDs; values are either "shared" (the default) or "per-session". Absent agents fall back to a built-in defaults list — currently only junie defaults to per-session.
Toggle this from the Per-session process isolation switch in the agent's Additional Settings dialog. See Managing Agents > Process Isolation for the full behaviour, including switching modes and resume after app restart.
Per-session agents remember their per-session flag on the session itself, so Braide can lazy-respawn the dedicated process the first time you interact with the session after an app restart.
All project and session data is organized under ~/.braide/:
~/.braide/
├── settings.json # Global app settings (see above)
├── logs/
│ ├── acp.log # Next.js server log (rotated at 5 MB × 5 files)
│ ├── electron-main.log # Electron main process log, desktop app only (rotated at 5 MB × 5 files)
│ ├── electron-main-crash.log # Electron main FATAL entries, synchronously written (append-only)
│ ├── braide-crash.log # Next.js child FATAL entries, synchronously written (append-only)
│ └── crashes.log # Sticky last-200 lifecycle/FATAL entries from both processes
└── projects/
└── <project-id>/
├── settings.json # Project metadata (name, root path, archived flag)
└── sessions/
└── <session-id>/
├── meta.json # Session metadata (agent, label, queue, etc.)
├── events.jsonl # Append-only session event log
├── logs/
│ └── session.log # Per-session log file (append-only)
└── worktree/ # Isolated git worktree for this session
See Logging — Log File Locations for the purpose of each log file and which one to consult when troubleshooting silent crashes.
projects/<project-id>/settings.json — Stores the project's display name, root path on disk, and archived flag.sessions/<session-id>/meta.json — Stores the session's assigned agent, display label, queue state, and the path to its worktree.sessions/<session-id>/events.jsonl — Append-only log of every event in the session (prompts, agent updates, completions). Each line is a JSON-encoded event.sessions/<session-id>/worktree/ — A git worktree checked out on the session's branch. See Worktrees for details.Braide reads a small number of JSON files at startup — the global settings.json, each project's settings.json, and each session's meta.json. When one of those files fails to decode (truncated write, hand-edit gone wrong, schema drift after a downgrade), the app does not overwrite it with defaults. Instead the file is quarantined for the rest of the process lifetime:
null so the app still boots.<filename>.bak-<iso-timestamp> (e.g. settings.json.bak-2026-05-19T11-42-08-204Z). This is a verbatim copy of the file at the moment of detection, so a recovery is always possible even if you later edit the original.area=Settings or area=Store entries with reason=decode failed: … point at the offending field.mv <file>.bak-<iso> <file> and re-edit only the field that broke..bak-<iso> is your reference copy)..bak-<iso> is still there if you change your mind.| Quarantined file | Where to look | Backup naming |
|---|---|---|
| Global settings | $BRAIDE_HOME/settings.json | settings.json.bak-<iso> next to the original |
| Project settings | $BRAIDE_HOME/projects/<project-id>/settings.json | settings.json.bak-<iso> inside the project directory |
| Session meta | $BRAIDE_HOME/projects/<project-id>/sessions/<session-id>/meta.json | meta.json.bak-<iso> inside the session directory |
$BRAIDE_HOME defaults to ~/.braide (see BRAIDE_HOME for overrides).
| Variable | Required | Description |
|---|---|---|
BRAIDE_HOME | No | Override the default home directory (~/.braide) |
BRAIDE_WORKTREE_CREATE | Set by Braide | Set to 1 in the env of every git invocation Braide makes (worktree add, branch ops, etc.). Read by user-managed git hooks to detect Braide-driven calls and skip work. |
GITHUB_TOKEN | No | Passed through to agents, the terminal, and language servers as an ambient credential. Not used by Braide's own GitHub features — those use the token configured in Settings → Secrets. See GITHUB_TOKEN / GH_TOKEN |
GH_TOKEN | No | Alternative to GITHUB_TOKEN; same passthrough behaviour |
ACP_LOG_LEVEL | No | Minimum console log level (trace, debug, info, warning, error, fatal, none). Default: info |
ACP_LOG_FILE_LEVEL | No | Minimum file log level. Default: debug. See Logging |
ACP_LOG_AREAS | No | Comma-separated area filter. Default: * (all). See Logging |
BRAIDE_PTY_CONPTY | No | Override the Windows terminal backend (dll, system, winpty, auto). Absent, Windows arm64 defaults to the bundled conpty.dll; elsewhere node-pty's default. See BRAIDE_PTY_CONPTY |
When you launch Braide from the macOS Dock/Finder or a Linux desktop launcher, the OS starts it with a minimal environment — no login shell runs, so nothing in your ~/.profile, ~/.zprofile, ~/.bash_profile, or PowerShell $PROFILE is sourced. To close that gap Braide runs your login shell once at startup, captures its full environment, and merges it into its own process. Every subprocess Braide spawns — agents, the integrated terminal, and language servers — then inherits it.
This is how CLI agents pick up credentials you keep in your shell profile. If an agent authenticates via an environment variable (for example JUNIE_API_KEY), export it from your shell profile as usual:
# ~/.profile, ~/.zprofile, ~/.bash_profile, etc.
export JUNIE_API_KEY=...
and it reaches the agent whether you launch Braide from a terminal or from the Dock. On Windows, GUI apps already inherit your user/system environment variables; Braide additionally captures variables set in your PowerShell profile (pwsh, falling back to powershell.exe).
A few caveats:
~/.zprofile/~/.profile for zsh, not ~/.zshrc alone.PATH so packaged launches can find node/npx/gh.NODE_ENV, NODE_PATH, ELECTRON_RUN_AS_NODE, …) are never overridden by your profile.BRAIDE_HOMEBy default, Braide stores all data — settings, projects, sessions, and downloaded agents — under ~/.braide/. Set the BRAIDE_HOME environment variable to use a different directory:
export BRAIDE_HOME=/path/to/custom/directory
bun run braide:dev
Or inline for a single invocation:
BRAIDE_HOME=/tmp/braide-demo bun run braide:dev
The directory will be created automatically on first use. This is useful for:
When BRAIDE_HOME is not set, or set to an empty string, the default ~/.braide/ is used.
When running Braide as a packaged Electron desktop app, the BRAIDE_HOME override is persisted in an electron-config.json file located in Electron's user data directory:
| Platform | Path |
|---|---|
| macOS | ~/Library/Application Support/Braide/electron-config.json |
| Windows | %APPDATA%\Braide\electron-config.json |
| Linux | ~/.config/Braide/electron-config.json |
The file stores the override as a braideHome field:
{
"braideHome": "/path/to/custom/directory"
}
This value can be set from within the app's settings UI. When braideHome is absent or empty, the default ~/.braide/ is used. The Electron app passes this value as the BRAIDE_HOME environment variable to the embedded Next.js server on startup.
The settings UI expands ~ (and ~/, ~\) to your home directory at save time, so ~/.braide is stored as the absolute path. The BRAIDE_HOME environment variable itself is consumed verbatim — pass an absolute path there. See Supported Platforms for the full list of platform-specific behaviours.
BRAIDE_WORKTREE_CREATEWhenever Braide shells out to git — to create a session worktree, rename a session branch, or any other git operation — it sets BRAIDE_WORKTREE_CREATE=1 in the child process's environment. The variable has no effect on Braide itself; it's a contract for user-managed git hooks.
The intended use case is a project-level post-checkout hook that installs dependencies, regenerates lockfiles, or otherwise prepares a fresh worktree for direct (non-Braide) git worktree add use. When Braide is the one creating the worktree, you usually want to skip that work — Braide creates sessions opportunistically (a session may never need its dependencies installed at all), and a per-project worktree setup command handles the install in parallel with the agent handshake when it's actually needed.
Example: a .githooks/post-checkout that bails out when invoked by Braide:
#!/usr/bin/env bash
# Skip when Braide is driving — it handles setup separately, in parallel with
# the agent handshake, via the per-project worktreeSetupCommand.
[ "$BRAIDE_WORKTREE_CREATE" = "1" ] && exit 0
# …rest of the hook (npm install, etc.) only runs for direct git worktree add usage.
This is the only way for hook authors to distinguish a Braide-driven checkout from a direct git worktree add invocation.
GITHUB_TOKEN / GH_TOKENBraide's own GitHub features — browsing repository issues and pull requests and attaching them to sessions — authenticate with the personal access token you configure in Settings → Secrets, stored (encrypted where the OS keychain is available) in settings.json. They do not read GITHUB_TOKEN/GH_TOKEN from the environment.
If GITHUB_TOKEN (or GH_TOKEN) is set in your shell environment, Braide passes it through to agents, the terminal, and language servers like any other login-shell variable — it simply isn't used for Braide's own API calls. This keeps the token an agent sees independent from the (often more narrowly scoped) PAT Braide uses, and stops a broad shell token from silently overriding it.
The token Braide uses needs read access to issues and labels for the repositories you want to work with. See GitHub Integration for full details.
BRAIDE_PTY_CONPTYOverrides the native backend Braide's terminal uses to spawn shells on Windows. Leave it unset in normal use — Braide already picks the known-good backend per platform. This variable has no effect on macOS or Linux.
On Windows arm64, Braide defaults to node-pty's bundled conpty.dll — a full-fidelity, modern ConPTY. It's preferred over the system ConPTY, which on arm64 throws an uncaught AttachConsole failed on every kill (node-pty forks a console-list agent only on the system path). On Windows x64 the default is node-pty's own (system ConPTY).
Set BRAIDE_PTY_CONPTY to force a specific backend (case-insensitive):
| Value | Backend | node-pty options |
|---|---|---|
dll / bundled | node-pty's bundled conpty.dll (arm64 default) | useConpty: true, useConptyDll: true |
system / builtin | System ConPTY — works, but on arm64 its kill path forks a console-list agent that crashes (AttachConsole failed), so closes are noisy and child-process cleanup is degraded | useConpty: true, useConptyDll: false |
winpty / off | Legacy winpty — node-pty's deprecated, lower-fidelity fallback | useConpty: false |
auto / on | Let node-pty choose (drops the arm64 default) | — |
Use system to reproduce the arm64 bug or re-test against a future Windows/node-pty fix. The backend in effect is recorded per session in the pty-host diagnostics (useConpty / useConptyDll) — see Logging.
# Force system ConPTY on Windows arm64 (e.g. to verify an upstream fix)
set BRAIDE_PTY_CONPTY=system
Regardless of backend, node-pty runs in an isolated pty-host child process, so a hung or crashing spawn degrades the terminal gracefully rather than freezing the app.
| Service | Default Port |
|---|---|
| Braide | 3000 |
| Terminal WebSocket | Dynamic (fetched via /api/terminal-port) |