Logging

Braide uses structured JSONL logging across all server-side code. Every log entry is a single JSON object written to stdout and to log files on disk. Console and file outputs have independent log levels, so you can keep the terminal clean while capturing full detail on disk.

Log Levels

Log levels control the verbosity of output. Console and file destinations each have their own level — a message is emitted to a destination only if its level meets or exceeds that destination's minimum.

LevelDefault forDescription
traceHigh-frequency streaming data. Individual agent message and thought chunks with full event body. Produces significant volume during active sessions.
debugFileRequest/response detail, protocol messages, and internal state. Includes API request parameters, ACP protocol negotiation, session config options, heartbeat tick summaries, and terminal process signals.
infoConsoleSignificant state changes and operational events. Agent start/stop/ready, session creation, worktree operations, scheduled prompt triggers, terminal connect/disconnect, shutdown, and archive pruning.
warningDegraded but recoverable conditions. Skipped scheduled sessions (missing acpId or agent not running), loadSession failures falling back to newSession.
errorOperation failures. Schema decode errors, worktree/branch failures, agent process crashes, ACP connection drops, file I/O errors, GitHub API failures.
fatalUnrecoverable process errors. Uncaught exceptions and unhandled promise rejections that trigger process shutdown.

Each level includes all levels above it. Setting the console level to debug shows debug, info, warning, error, and fatal messages in the terminal.

Log Areas

Every log entry includes an area field identifying the subsystem that produced it. Filter areas using the ACP_LOG_AREAS environment variable.

AreaSubsystemExample messages
storeSession/project data persistenceSchema decode failures for projects, sessions, events, config, schedules
sessionsSession data models(reserved for future use)
lifecycleApp lifecycle and session orchestrationShutdown, agent restore, ACP session create/prompt, archive pruning
clientACP client callbacksSession updates from agent (with update type and event body), file read/write, terminal create/exit
promptPrompt construction(reserved for future use)
agentAgent process managementBinary download, process spawn/exit/restart, ACP protocol init, loadSession/recreateSession
heartbeatScheduled task runnerHeartbeat ticks, pending schedule details, session triggers, prompt completion
terminal-wsTerminal WebSocket serverClient connect/disconnect, spawn failures, message errors, server listen
terminal-managerTerminal process lifecycleProcess group signals, kill operations
worktreeGit worktree operationsWorktree create/remove, branch rename/archive, diff generation, branch listing, merge
eventsEvent file parsingEvent decode failures
trajectoryTrajectory file parsingTrajectory entry decode failures
sseServer-sent eventsSSE replay timing
settingsSettings file I/OSettings load/save failures
permissionPermission handlingPermission requests, auto-approvals (safe kind, previously approved), persisted approved commands
apiAPI route handlersRequest/response logging for sessions, prompt, config, stream, and issue endpoints

Area Filtering

Set ACP_LOG_AREAS to a comma-separated list of areas to include. Use - prefix to exclude. Default is * (all areas).

# Only show heartbeat and agent logs
ACP_LOG_AREAS=heartbeat,agent

# Show everything except high-volume client updates
ACP_LOG_AREAS=-client

# Show only errors from all areas (combine with level)
ACP_LOG_LEVEL=error

Output Format

Every log entry is a single-line JSON object (JSONL):

{"timestamp":"2026-04-14T09:37:19.941Z","level":"INFO","area":"agent","message":"Agent ready","agentId":"claude-code","name":"Claude Code","version":"1.0.0"}

Fields

FieldAlways presentDescription
timestampYesISO 8601 timestamp
levelYesLog level (TRACE, DEBUG, INFO, WARN, ERROR, FATAL)
areaYesSubsystem that produced the entry
correlationIdWhen setApp-level session ID for correlating all log entries within a session
messageYesHuman-readable description
acpIdWhen applicableACP SDK session ID (differs from the app session ID)
projectIdWhen applicableProject identifier
errorOn errorsError message string
dataOn session updatesFull event body from the agent

Additional context-specific fields (e.g. agentId, sessionId, path, stopReason) are included as top-level properties.

Correlation IDs

The correlationId field ties together all log entries for a single session across API routes, lifecycle operations, client callbacks, and permission handling. The value is the app-level session ID (e.g. 20260414-103452-zZY76b), not the ACP SDK's internal session UUID.

Use it to filter logs for a specific session:

grep '"correlationId":"20260414-103452-zZY76b"' ~/.braide/logs/acp.log

Redaction

All log entries are automatically scanned for confidential information before being written to any output. Redaction happens at the serialization boundary — both the Effect-TS logger and the imperative logger apply it, so no sensitive data reaches stdout or log files regardless of log level.

Detected values are replaced with [REDACTED]. The original data is never written.

Key-Name Detection

Any annotation key matching a sensitive pattern is redacted. The following patterns trigger redaction (case-insensitive):

token, secret, password, passwd, pwd, key, apikey, api_key, auth, credential, bearer, cookie, session, jwt, private, access_token, refresh_token, client_secret, encryption

Structural keys used by the logging framework itself (correlationId, projectId, sessionId, area, level, timestamp, message, statusCode, method, path, url, duration, count, id, etc.) are safe-listed and never redacted.

Value-Pattern Detection

String values are checked against known secret formats regardless of key name. This catches secrets that end up in generically-named fields. Detected patterns include:

PatternExample
GitHub tokensghp_*, gho_*, ghu_*, ghs_*, github_pat_*
OpenAI-style API keyssk-*, sk-proj-*
JWTseyJhbG... (three dot-separated base64url segments)
Bearer / Basic auth headersBearer <token>, Basic <encoded>
Slack tokensxoxb-*, xoxp-*, xoxa-*, xoxr-*, xoxs-*
AWS access key IDsAKIA* (20 uppercase alphanumeric characters)
PEM private keys-----BEGIN PRIVATE KEY-----, -----BEGIN RSA PRIVATE KEY-----

Nested Data

Redaction walks nested objects and arrays up to 8 levels deep. A secret buried inside an annotation object like { config: { credentials: { token: "ghp_..." } } } is redacted at the leaf.

Example

Given a log call:

log.info("auth check", { userId: "u-123", token: "ghp_abc123...", status: "ok" })

The written log entry will be:

{"timestamp":"...","level":"INFO","area":"api","message":"auth check","userId":"u-123","token":"[REDACTED]","status":"ok"}

Log File Locations

When BRAIDE_HOME is set (or defaults to ~/.braide), logs are written to disk in addition to stdout.

Global Log

~/.braide/logs/acp.log

Contains all log entries from all sessions and subsystems. Rotates at 5 MB, keeping 5 rotated files (.1 through .5).

Session Logs

~/.braide/projects/{projectId}/sessions/{sessionId}/logs/session.log

Contains only log entries that include both projectId and sessionId annotations. These files are append-only with no rotation, preserving the complete log history for each session.

Configuration

Log levels can be configured from the Settings UI, environment variables, or the settings file. Changes made through the Settings UI or settings file take immediate effect without restarting the server.

Settings UI

Open Settings > System > Logging to configure log levels. The panel shows two independent level pickers:

  • Console Log Level — controls what appears in the terminal (default: info)
  • File Log Level — controls what is written to log files on disk (default: debug)

Changes take effect immediately when a level is selected.

Log Viewer

The View Log button on the File Log Level card opens a near-full-screen log viewer for browsing the global log file.

Layout: A table with columns for Timestamp, Level, Area, Correlation ID, Message, and Update Type. Columns size to fit their content; the Message column takes the remaining space. The header row and filter row stay pinned when scrolling.

Scrolling and lazy loading: The log displays in ascending chronological order (oldest at top, newest at bottom). The most recent 100 entries load initially, with the view scrolled to the bottom. Scrolling up lazy-loads older entries in batches. "No earlier logs." appears at the top when all history is loaded; "Waiting for logs..." appears at the bottom.

Live tailing: While the viewer is open, new log entries are polled every 2 seconds and appended at the bottom. The view auto-scrolls to follow new entries only when already scrolled to the bottom. If you have scrolled up to browse history, new entries accumulate silently. The Skip to Current button reloads the latest entries and scrolls to the bottom.

Detail expansion: Rows with additional data beyond the standard columns show a small arrow (▶) next to the timestamp. Click the row to expand an inline panel showing all extra fields as prettified JSON. A Copy to clipboard button in the top-right corner of the detail panel copies the JSON to the clipboard. Use Arrow Up / Arrow Down to move the detail panel between expandable rows without clicking. Escape closes the detail panel; pressing Escape again closes the viewer.

Column Filters

A row of filter controls sits directly below the column headers. All filters are cumulative — only rows matching every active filter are shown. The Clear Filters button in the header resets all filters at once.

FilterTypeBehavior
LevelMulti-select dropdownSelect one or more log levels (TRACE, DEBUG, INFO, WARN, ERROR, FATAL). Options are populated from the loaded entries. Shows "All" when nothing is selected.
AreaMulti-select dropdownSelect one or more log areas (e.g. agent, lifecycle, store). Options are populated from the loaded entries.
Correlation IDSingle-value pillClick any correlation ID in the table to filter to that session's entries. The active filter appears as a pill with a × button to clear it. Only one correlation ID can be active at a time.
MessageText inputCase-insensitive substring search across log messages. Filters in real time as you type.
Update TypeMulti-select dropdownSelect one or more update types. Options are populated from the loaded entries.

Filtering is client-side against already-loaded lines — lazy loading and live tailing continue in the background so filtered results grow as more data loads. The status bar in the footer shows how many entries are loaded and the total count in the file.

Level Colors

Log levels are color-coded for quick scanning:

LevelColor
TRACEMuted gray
DEBUGSecondary text
INFOAccent blue
WARNGolden (#e6a817)
ERRORRed (#e53935)
FATALDark red (#b71c1c)

Environment Variables

VariableDefaultDescription
ACP_LOG_LEVELinfoMinimum level for console (stdout) output
ACP_LOG_FILE_LEVELdebugMinimum level for file output (global + session logs)
ACP_LOG_AREAS*Comma-separated area filter. Use - prefix to exclude (e.g. -client,-sse)

Settings File

Log settings can also be persisted in ~/.braide/settings.json under the logging key:

{
  "logging": {
    "consoleLevel": "info",
    "fileLevel": "debug",
    "areas": "agent,lifecycle,heartbeat"
  }
}

Persisted settings are applied to the environment at startup and whenever changed via the Settings UI. Environment variables set externally take precedence over persisted settings.