Skip to content

Configuration Reference

Complete reference for the agentwatch configuration file.

Overview

agentwatch uses a YAML configuration file, typically located at ~/.config/agentwatch/config.yaml.

Generating Configuration

# Generate default config
agentwatch --generate-config > ~/.config/agentwatch/config.yaml

# View current config
agentwatch config show

The update channel is not in this file. The self-update channel (stable/unstable) is a gateway-only setting in the gateway config (gateway.yaml), not this per-user config.yaml. Per-user daemons follow the gateway's channel and have no channel of their own. See Architecture & Installation.

Full Configuration

# =============================================================================
# Logging
# =============================================================================
logging:
  # Log level: DEBUG, INFO, WARNING, ERROR
  log_level: INFO

  # Log file path (null = stderr only; when stderr is a file, as under
  # launchd, the daemon uses ~/.cache/agentwatch/daemon.log instead)
  log_file: ~/.cache/agentwatch/daemon.log

  # Max log file size before rotation (MB)
  log_rotate_max_mb: 10

  # Number of rotated log files to keep
  log_rotate_backup_count: 9

# =============================================================================
# Service Settings
# =============================================================================
service:
  # How often to scan for new/removed tmux sessions (seconds)
  session_discovery_interval: 5

  # How often to capture each session's terminal (seconds)
  capture_interval: 1

  # tmux socket path (null = default socket)
  tmux_socket: null

  # Path to tmux binary
  tmux_bin: tmux

  # Debug settings
  debug:
    # Enable debug mode
    enabled: false

    # Directory for capture files (null = disabled)
    capture_dir: null

    # Auto-cleanup captures older than this (hours)
    retention_hours: 4.0

    # Log each capture
    log_captures: false

    # Log detection results
    log_detections: false

    # Log state changes (recommended for debugging)
    log_state_changes: true

    # Log hook executions
    log_hooks: true

    # Dry run mode - log hooks without executing
    dry_run_hooks: false

# =============================================================================
# Web Viewer (HTTPS Server)
# =============================================================================
web_viewer:
  # Enable/disable web viewer
  enabled: true

  # Host to bind (127.0.0.1 = localhost only)
  host: 127.0.0.1

  # Port to listen on
  port: 8081

  # Terminal streaming frame rate
  fps: 2.0

  # Max keyboard input messages per second
  max_input_rate: 20

  # Custom SSL certificate (optional)
  ssl_cert: null
  ssl_key: null

# =============================================================================
# Tunnel (agentwatch.sh)
# =============================================================================
tunnel:
  # Enable auto-provisioned tunnel
  # Requires device token in ~/.config/agentwatch/tokens/device
  enabled: false

# =============================================================================
# Cloudflare Tunnel (Manual)
# =============================================================================
cloudflare:
  # Enable manual Cloudflare tunnel
  enabled: false

  # Path to cloudflared config file
  config_file: ~/.cloudflared/config-agentwatch.yml

  # Path to cloudflared binary
  cloudflared_bin: cloudflared

# =============================================================================
# Coding Sessions
# =============================================================================
sessions:
  # Enable coding session management
  enabled: true

  # Prefix for auto-generated session names
  session_prefix: coding-

  # Maximum concurrent coding sessions
  max_sessions: 3
  codex_max_subagents: 16

  # Default lines to capture for session capture
  capture_lines: 50

  # Gateway setting: the operator's quota policy for agents, in percent of a
  # window left. Agents are told these steps at session start, in
  # `agentwatch whoami`, in `swap-account --help`, in the swap notice and in
  # each quota alert. A value that does not read is logged and the defaults
  # hold (the gateway still starts). They are also the launch floor: no new
  # session starts on an account whose 5-hour window is at or below the
  # 5-hour step, or whose weekly window is at or below the no-subagents step
  # counted from the account's reserve, unless forced (`session new --account
  # NAME --force`, or the operator naming it in the web viewer). Automatic
  # then refuses rather than use an OpenRouter key.
  quota_policy:
    seven_day_no_subagents: 2   # weekly: start no new subagents
    seven_day_swap: 1           # weekly: finish, compact, then swap (0-99; not above the line above)
    five_hour_no_subagents: 10  # 5-hour: no new subagents; never swap for it
  # Gateway setting: put this sessions block in gateway.yaml for quota alerts
  # to live parent sessions (including their child sessions' accounts). The
  # key names keep "mail" from when the alerts were mail.
  # Uses cached daemon readings; never adds provider probes. {} disables all.
  # Default: one alert per policy step (five_hour [10], seven_day [2, 1]). An
  # account that keeps a reserve has its weekly steps counted from it.
  quota_mail_thresholds:
    five_hour: [10]    # Percent remaining; [] disables this window
    seven_day: [2, 1]
  # An alert starts an agent turn. Only a tree with ongoing work is alerted: a
  # member Working or Blocked, or the recipient Idle for less than this.
  quota_mail_active_seconds: 300

  # Allowed working directories for coding sessions
  # Sessions can only be created under these paths
  allowed_workdirs:
    - "~"         # Home directory
    - "/tmp"      # Temporary files
    - "/var/tmp"  # Persistent temp

# =============================================================================
# Hooks
# =============================================================================
hooks:
  # Webhook example
  - name: my-webhook
    type: webhook
    trigger: state_change
    programs: ["Claude Code"]
    to_states: [Error, Blocked]
    url: "https://example.com/webhook"
    include: status
    timeout_seconds: 10
    retry_count: 0
    retry_delay_seconds: 1.0

  # Shell example
  - name: my-shell
    type: shell
    trigger: state_change
    to_states: [Idle]
    command: 'echo "$SESSION_NAME finished" >> ~/agentwatch.log'
    include: status

Section Reference

logging

Key Type Default Description
log_level string INFO Log level: DEBUG, INFO, WARNING, ERROR
log_file string ~/.cache/agentwatch/daemon.log Log file path (null = stderr only). When stderr is a regular file (launchd, a background start), nothing rotates it, so the daemon uses the default path instead
log_rotate_max_mb number 10 Max log file size (MB)
log_rotate_backup_count number 9 Rotated files to keep

service

Key Type Default Description
session_discovery_interval number 5 Session scan interval (seconds)
capture_interval number 1 Capture interval per session (seconds)
tmux_socket string null tmux socket path
tmux_bin string tmux Path to tmux binary

service.debug

Key Type Default Description
enabled boolean false Enable debug mode
capture_dir string null Directory for capture files
retention_hours number 4.0 Capture cleanup age (hours)
log_captures boolean false Log each capture
log_detections boolean false Log detection results
log_state_changes boolean true Log state changes
log_hooks boolean true Log hook executions
dry_run_hooks boolean false Log hooks without executing

web_viewer

Key Type Default Description
enabled boolean true Enable web viewer
host string 127.0.0.1 Bind address
port number 8081 Port number
fps number 2.0 Streaming frame rate
max_input_rate number 20 Max input messages/sec
ssl_cert string null Custom SSL certificate path
ssl_key string null Custom SSL key path

tunnel

Key Type Default Description
enabled boolean false Enable auto-provisioned tunnel

The device token is stored at ~/.config/agentwatch/tokens/device.

app

Key Type Default Description
ios_app_ids list [] Apple Developer team ids allowed to open this box's Universal Links, as TEAMID.sh.agentwatch.app

Gateway-side, like tunnel and location. Feeds GET /.well-known/apple-app-site-association, which 404s while this list is empty — the default until Apple enrollment gives an operator a team id. See native-app.md.

cloudflare

Key Type Default Description
enabled boolean false Enable manual Cloudflare tunnel
config_file string ~/.cloudflared/config-agentwatch.yml Cloudflared config path
cloudflared_bin string cloudflared Path to cloudflared

sessions

Key Type Default Description
enabled boolean true Enable coding session management
session_prefix string coding- Auto-generated session name prefix
max_sessions number 3 Max concurrent sessions
codex_max_subagents integer or null 16 Concurrent subagents per Codex session, excluding the main agent; positive integer, or null to use Codex's own configuration
quota_mail_active_seconds number 300 Quota alerts go only to a tree with ongoing work: a member Working or Blocked, or the recipient Idle for less than this many seconds (0 or more); gateway config
sleep_wake_notices boolean true macOS: after the Mac wakes from a sleep, mail the sessions it interrupted (when it slept and woke, for how long, what to check); gateway config
capture_lines number 50 Default capture lines
allowed_workdirs list ["~", "/tmp", "/var/tmp"] Allowed working directories

Set sessions.codex_max_subagents in the per-user daemon configuration (~/.config/agentwatch/config.yaml). Agentwatch passes -c agents.max_concurrent_threads_per_session=N to both the Codex TUI and its app server, without editing ~/.codex/config.toml. The limit applies to the whole subagent tree in each session. Set a larger number for more parallel workers, or null to omit the override; Codex requires a positive number and does not support an unlimited value. Restart Agentwatch and create a new Codex session to apply it; existing Codex sessions keep their launch configuration.

sessions.sleep_wake_notices is also gateway configuration, and only has an effect on macOS. When the Mac wakes from a sleep of a minute or more (a closed lid, or idle sleep), the gateway mails each session that was busy when it slept: Working or Blocked, Idle with its last turn ending within 10 minutes, or Idle with background shells, subagents or an active /goal. A session idle for longer with nothing running is not mailed, so a sleep never starts a turn in it. Set it to false to turn the notices off. Details: agent-mail.md.

board

The project board (Scotty behind /board). The per-user daemon reads this section each time it starts or reports the board, so a change needs no restart to take effect for the next start. Details: project-board.md.

Key Type Default Description
idle_timeout_minutes number 30 Minutes without traffic before the gateway stops the user's board process. 0 means never. The next request starts it again
board:
  idle_timeout_minutes: 30

email

Inbound email to sessions (<session>.<box>@agentwatch.sh). This is a gateway setting. Details: inbound-email.md.

Key Type Default Description
owner_user string none The OS user who gets the email that no session name of this box matches. The provisioner holds that email for this user.

A box with one daemon user needs no setting: that user is the owner. On Linux the daemon users are the login users of the box (uid 1000 and up), also a user who runs no daemon. The root gateway's own uid 0 is not one. On a box with several users and no owner_user, the provisioner refuses that email. The gateway ignores an owner_user that is not a listed user. Then there is no owner.

email:
  owner_user: selden

Push notification preferences

Web push preferences are not in the YAML file. Each user sets them in the web viewer (GET/PUT /api/push/preferences). The flags are needs_input (default on), error (default on), finished (default off) and needs_you (default on). needs_you controls the "needs you" push of the project board. The push title is the project id. The body is "1 item needs you" or "N items need you". N counts every item that waits in the board's Needs You inbox, not only the new ones. The board sends at most one push for each board in 10 minutes.

hooks

List of hook definitions. Each hook has:

Key Type Required Description
name string Yes Unique identifier
type string Yes webhook or shell
trigger string Yes state_change, program_change, any_change
programs list No Filter: current program must match
states list No Filter: current state must match
from_states list No Filter: previous state must match
to_states list No Filter: target state must match
from_programs list No Filter: previous program must match
to_programs list No Filter: target program must match
include string No Data format: status, json, text, png

Webhook-specific:

Key Type Default Description
url string Required Webhook URL
timeout_seconds number 10 Request timeout
retry_count number 0 Retry attempts
retry_delay_seconds number 1.0 Delay between retries

Shell-specific:

Key Type Default Description
command string Required Shell command

accounts

Per-user, in your own config.yaml, and read by its own consumer (a hand edit is live without a restart). Written by agentwatch accounts; credentials are never stored here.

accounts:
  claude_code:            # Claude setup-token logins, in priority order
    - name: primary
      organization_id: org-…
    - name: backup
      organization_id: org-…
      reserve_percent: 30     # optional: quota kept on this account
      reserve_mode: hard      # hard: never used below it; soft: others first
      plan_type: max_5x       # optional label: pro, max_5x, max_20x (accounts set-plan)
  openrouter:             # OpenRouter keys: overflow for Claude and Codex
    - name: cheap
      models:
        claude_code: z-ai/glm-5.3-flash
        codex: deepseek/deepseek-v4.1-flash
  codex:                  # second ChatGPT logins for Codex, in priority order
    - name: support
      account_id: b50e5a08-…  # the ChatGPT account, from the login's claims
      reserve_percent: 30      # quota new sessions leave for other users
      reserve_mode: hard       # the default for a managed account
      plan_type: 5x            # optional label: plus, 5x, 20x (accounts set-plan)
  shared:                 # this machine's own logins ("login"), shared with your terminal
    codex:
      reserve_percent: 2       # other accounts first below 2% ...
      reserve_mode: soft       # ... but still used last (the default)
      plan_type: 5x            # optional label, as for an account
  poll_interval_minutes: 60   # 0 disables the hourly stale-account poll
  handover:
    window_hours: 48          # batch runs use an account this long before its weekly reset
Key Description
claude_code Claude accounts; the list order is the priority order. Tokens in ~/.config/agentwatch/tokens/claude/<name>
openrouter OpenRouter keys, tried after every subscription. models is optional per agent; without it the session starts on the harness default model. Keys in ~/.config/agentwatch/tokens/openrouter/<name>
codex Codex accounts, tried after this box's own Codex login and before any OpenRouter key. Each login is a CODEX_HOME at ~/.config/agentwatch/tokens/codex/<name>/. reserve_percent (0–99, default 0) is the quota new sessions leave on the plan
shared This machine's own logins (login), per agent: reserve_percent and reserve_mode (default soft), and an optional plan_type. agentwatch never changes these logins
plan_type Optional, on a Claude account's plan block (pro, max_5x, max_20x), a Codex account (plus, 5x, 20x) or a shared login: which plan it is, as the operator records it (accounts set-plan). A label only: shown beside the account, never used to choose one. A value that is not a known type reads as unset
reserve_percent / reserve_mode On a Claude or Codex account: quota kept, and how firmly. hard (default): never used at or below it, not even by name. soft: other accounts first there; below it the account is at a quota alert step (the weekly steps count from the reserve), so a new session starts there only with session new --account NAME --force or from the web viewer
handover.window_hours How many hours before its weekly reset an account is handed to batch runs (accounts handover, accounts lease; default 48, 1–168; any other value is 48, with a warning). No command sets it: the operator edits it here
poll_interval_minutes How often the daemon polls (default 60 minutes). A Claude account is probed only when no box holds a reading younger than the interval less 5 minutes; Codex and OpenRouter are read every time. Each box polls at its own minute. A box with no accounts: block polls nothing, and 0 turns the poll off

Claude accounts are stored together in accounts.claude (the legacy claude_code list above is migrated on first write). An optional API renewal policy makes the daemon record monthly expected-credit grants at midnight UTC, without calling a provider. It works with or without a local plan:

accounts:
  claude:
    - name: primary
      api:
        organization_id: console-org
        credits: 200
        expires: '2026-10-14'
        granted_at: 1791504000
        renewal:
          credits: 100                 # future monthly grant, not today's balance
          renews_day: 11
          next_renewal: '2026-10-11'     # first grant; advanced atomically each month

Grants replace rather than accumulate; expiry is three days after the following renewal, through that UTC day. See accounts set-credit-renewal in the CLI.

Environment Variables

These override configuration values:

Variable Overrides
AGENT_WATCH_SOCKET service.tmux_socket
AGENT_WATCH_TARGET Target pane
AGENT_WATCH_COLS One-shot capture/render width
AGENT_WATCH_ROWS One-shot capture/render height
AGENT_WATCH_FORMAT Output format
AGENT_WATCH_SESSION_SECRET Session signing key
AGENTWATCH_TOKEN Device token

SSL Certificates

Certificates are used in this priority:

  1. Tunnel certificate - Auto-provisioned for *.agentwatch.sh
  2. Custom certificate - From ssl_cert/ssl_key config
  3. Auto-generated - Self-signed localhost certificate

Auto-generated Certificates

On first start, if no certificates are configured, a self-signed localhost certificate is generated automatically.

Custom Certificates

web_viewer:
  ssl_cert: /path/to/cert.pem
  ssl_key: /path/to/key.pem

Tunnel Certificates

When using the auto-provisioned tunnel, Cloudflare origin certificates are fetched automatically and stored at ~/.config/agentwatch/certs/.

Hook Shell Environment

Shell hooks receive these environment variables:

Variable Description
SESSION_NAME tmux session name
SESSION_ID tmux session ID
PROGRAM Current program
PROGRAM_VERSION Version (if detected)
STATE Current state
DETAIL State detail
PREVIOUS_PROGRAM Previous program
PREVIOUS_STATE Previous state
PREVIOUS_DETAIL Previous detail
TIMESTAMP ISO timestamp
DURATION_SECONDS Time in previous state
CAPTURE_FILE Capture file path (if include: text/png)
AGENTWATCH_TRIGGER Trigger type

Example Configurations

Minimal

web_viewer:
  enabled: true

sessions:
  enabled: true

With Desktop Notifications

hooks:
  - name: desktop-notify
    type: shell
    trigger: state_change
    to_states: [Blocked, Error, Idle]
    command: 'notify-send "agentwatch" "$SESSION_NAME: $STATE"'

With Remote Access

tunnel:
  enabled: true

hooks:
  - name: slack-notify
    type: webhook
    trigger: state_change
    to_states: [Error]
    url: "https://hooks.slack.com/services/..."

Debug Mode

logging:
  log_level: DEBUG

service:
  debug:
    enabled: true
    capture_dir: ~/.cache/agentwatch/captures
    log_state_changes: true
    log_hooks: true