Configuration

Settings File

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"
  }
}

Agent Meta

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.

Agent Process Isolation

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.

Directory Structure

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.

Recovering from a corrupted settings or meta file

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:

  • Reads of the quarantined file return defaults / null so the app still boots.
  • Every writer that targets the quarantined path is skipped with a log entry — your original bytes stay on disk.
  • A timestamped snapshot is written next to the file as <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.
  • The renderer raises an error toast on first interaction naming the file, the decode reason, and the backup path.

What to do

  1. Quit Braide before editing. Quarantine clears on restart; if you fix the file while the app is still running, your edit will load only on the next boot.
  2. Inspect the toast / logarea=Settings or area=Store entries with reason=decode failed: … point at the offending field.
  3. Recover the content by either:
    • Restoring the snapshot: mv <file>.bak-<iso> <file> and re-edit only the field that broke.
    • Hand-editing the original to match the current schema (the .bak-<iso> is your reference copy).
    • Deleting the original to let Braide regenerate defaults on next launch. Use this only when you don't mind losing the content — the .bak-<iso> is still there if you change your mind.
  4. Restart Braide. The app reads the file fresh and, if it now decodes cleanly, drops the quarantine.

File locations

Quarantined fileWhere to lookBackup naming
Global settings$BRAIDE_HOME/settings.jsonsettings.json.bak-<iso> next to the original
Project settings$BRAIDE_HOME/projects/<project-id>/settings.jsonsettings.json.bak-<iso> inside the project directory
Session meta$BRAIDE_HOME/projects/<project-id>/sessions/<session-id>/meta.jsonmeta.json.bak-<iso> inside the session directory

$BRAIDE_HOME defaults to ~/.braide (see BRAIDE_HOME for overrides).

Environment Variables

VariableRequiredDescription
BRAIDE_HOMENoOverride the default home directory (~/.braide)
BRAIDE_WORKTREE_CREATESet by BraideSet 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_TOKENNoPassed 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_TOKENNoAlternative to GITHUB_TOKEN; same passthrough behaviour
ACP_LOG_LEVELNoMinimum console log level (trace, debug, info, warning, error, fatal, none). Default: info
ACP_LOG_FILE_LEVELNoMinimum file log level. Default: debug. See Logging
ACP_LOG_AREASNoComma-separated area filter. Default: * (all). See Logging
BRAIDE_PTY_CONPTYNoOverride 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

Login-shell environment (GUI launches)

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:

  • The capture runs your shell's login + interactive initialisation, so put exports where a login shell will see them — ~/.zprofile/~/.profile for zsh, not ~/.zshrc alone.
  • If the capture fails or times out, Braide falls back to the inherited environment and still enriches PATH so packaged launches can find node/npx/gh.
  • Braide-internal variables (NODE_ENV, NODE_PATH, ELECTRON_RUN_AS_NODE, …) are never overridden by your profile.

BRAIDE_HOME

By 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:

  • Testing — Run against an isolated data directory without affecting your real projects
  • Demos — Set up a self-contained environment with pre-configured projects
  • Multiple instances — Run separate Braide instances with independent data

When BRAIDE_HOME is not set, or set to an empty string, the default ~/.braide/ is used.

Electron app override

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:

PlatformPath
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_CREATE

Whenever 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_TOKEN

Braide'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_CONPTY

Overrides 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):

ValueBackendnode-pty options
dll / bundlednode-pty's bundled conpty.dll (arm64 default)useConpty: true, useConptyDll: true
system / builtinSystem 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 degradeduseConpty: true, useConptyDll: false
winpty / offLegacy winpty — node-pty's deprecated, lower-fidelity fallbackuseConpty: false
auto / onLet 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.

Ports

ServiceDefault Port
Braide3000
Terminal WebSocketDynamic (fetched via /api/terminal-port)