Skip to content

Configuration

Daemon Configuration

The daemon reads its config from a TOML file at the platform-specific config directory. On first run, a default config with an auto-generated auth token is created.

Config File Location

Determined by the directories crate using ProjectDirs::from("com", "hive", "Hive"):

PlatformPath
Linux~/.config/hive/Hive/config.toml
macOS~/Library/Application Support/com.hive.Hive/config.toml
WindowsC:\Users\<user>\AppData\Roaming\hive\Hive\config\config.toml

The config file is automatically created by hive install, which runs an interactive wizard to set these values. You can also create or edit the file manually.

When the daemon is launched with --config <path>, Hive anchors related paths to that config location. In the default layout this means:

  • ...\config\config.toml
  • ...\data\ for the SQLite DB, logs, and pasted-file attachments

This is especially relevant for the Windows service install: the service runs as LocalSystem, but Hive pins --config to the installing user's roaming profile so attachments and other daemon data stay under that same user profile instead of C:\Windows\System32\config\systemprofile\....

Full Config Reference

toml
# Network binding address. Use "0.0.0.0" for all interfaces.
bind = "127.0.0.1"

# Client WebSocket port.
port = 9178

# Auth token for client connections. Auto-generated if omitted.
token = "a3f8b2c1d4e5..."

# Path to the Claude CLI binary. Resolved via PATH if just "claude".
claude_bin = "claude"

# Human-readable name for this node. Shown in cluster status.
# display_name = "my-node"

# Default working directory when client sends "." as working dir.
# default_working_dir = "/home/user/projects"

# Extra arguments always passed to the Claude CLI.
# claude_extra_args = ["--dangerously-skip-permissions"]

# When true, PTY sessions are launched inside a named tmux session so they
# survive a daemon restart. On restart the daemon re-adopts any surviving
# hive-<uuid> tmux sessions instead of treating them as dead.
# Requires tmux installed on this node. Default: false.
# persistent_sessions = true

# Tracing-filter directive for the daemon's log subscriber.
# Default is "warn" (errors and warnings only). Set to "info" or "debug" while
# reproducing an issue, then revert. Accepts the same syntax as RUST_LOG, e.g.
# "hived=debug,hyper=warn". The RUST_LOG env var, when set, takes precedence
# over this field.
# log_filter = "info"

# Optional: agent-idle detection tuning.
# The daemon watches each PTY session's terminal title to detect when an
# interactive agent CLI stops working. This drives the in-app desktop
# popup/sound and the durable session name; there are no remote push channels.
# Omit this section to use the built-in Claude/Codex rules.
# [[notifications.agent_detection]]
# name = "claude"
# working_title_regex = '^\s*[\x{2800}-\x{28FF}]'   # leading braille spinner

# Optional: cluster configuration for multi-node mode.
# Omit this entire section for standalone mode.
[cluster]
# Auto-generated node UUID. Do not set manually.
# node_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"

# Peer node addresses in host:peer_port format.
peers = ["10.0.0.2:9179", "10.0.0.3:9179"]

# Port for inter-node peer WebSocket communication.
peer_port = 9179

# Shared secret for peer authentication. Must be identical on all nodes.
cluster_token = "shared-cluster-secret"

# Deprecated, no-op. Leaderless clustering has no leader, election, or quorum.
# These keys are still parsed for backward compatibility but no longer affect
# any behaviour; new configs can omit them.
heartbeat_interval_ms = 1000
election_timeout_min_ms = 3000
election_timeout_max_ms = 5000
# min_quorum = 2

# This node's client-facing addresses advertised to peers and clients.
# The first entry is treated as primary.
# If omitted, Hive auto-detects an address from the local machine.
# advertise_addresses = ["10.0.0.1:9178"]

# Human-readable name for this node in cluster context.
# Overrides top-level display_name when set.
# display_name = "node-a"

[notes]
# Markdown notes vault, S3-backed with a per-node local cache. See docs/notes.md.
enabled = true
# S3-compatible bucket (sync is disabled without it; the vault stays local-only).
bucket = "my-notes"
# Endpoint for non-AWS providers (MinIO/R2/B2). Omit for AWS S3.
# endpoint = "https://s3.amazonaws.com"
region = "us-east-1"
# Credentials. Omit to fall back to AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY.
# access_key = "AKIA..."
# secret_key = "..."
# Local vault directory. `~` expands to home; defaults to <data_dir>/notes.
local_dir = "~/.hive/notes"
# Background pull cadence in seconds (minimum 60).
sync_interval_secs = 300
# Claude model the in-vault notes agent runs with (node-local). Omit for the
# Claude CLI default. Applies to new agent chats.
# model = "claude-opus-4-7"

Full-text search runs natively inside the daemon - no external tools required. See Notes for the complete feature reference.

Field Details

FieldTypeDefaultDescription
bindString"127.0.0.1"Interface to bind the client WebSocket server
portu169178Port for client WebSocket connections
tokenStringauto-generated64-char hex string (256-bit) for client authentication
claude_binString"claude"Path or name of the Claude CLI binary
display_nameOption<String>(none)Human-readable node name, shown in cluster status
default_working_dirOption<String>(none)Default working directory for new sessions
claude_extra_argsOption<Vec<String>>(none)Extra arguments always passed to the Claude CLI
log_filterOption<String>"warn"Tracing filter directive. RUST_LOG env var overrides this when set
cluster.node_idOption<Uuid>auto-generatedUnique node identifier, auto-generated on first cluster start
cluster.peersVec<String>[]Peer node addresses (host:peer_port)
cluster.peer_portu169179Port for peer-to-peer WebSocket connections
cluster.cluster_tokenStringrequiredShared secret for inter-node authentication
cluster.heartbeat_interval_msu641000Deprecated no-op (no leader heartbeat in leaderless clustering); retained for compatibility
cluster.election_timeout_min_msu643000Deprecated no-op (no election); retained for compatibility
cluster.election_timeout_max_msu645000Deprecated no-op (no election); retained for compatibility
cluster.min_quorumOption<usize>(none)Deprecated no-op (no quorum); retained for compatibility
cluster.advertise_addressesVec<String>[]Client-facing addresses this node advertises to peers and clients; first entry is primary
cluster.display_nameOption<String>(none)Node name in cluster context (overrides top-level display_name)

Notifications Fields

All fields live under [notifications].

FieldTypeDefaultDescription
agent_detectionArraybuilt-inPer-agent title detection rules (see below); defaults to the Claude/Codex spinner rules
agent_detection[].nameStringrequiredLabel shown in logs
agent_detection[].working_title_regexStringrequiredRegex matched against the terminal title; a match means "working"

Agent-Idle Detection (daemon-native)

The daemon watches the OSC window-title each PTY session emits to detect when an interactive agent CLI stops working, needing no CLI on the host and no per-agent hook. Both Claude Code and Codex prefix the title with an animated braille-spinner glyph (U+2800..U+28FF) while processing and drop it once idle, so the built-in rules detect the working->idle transition. Plain shells never match, so they never false-fire.

The idle transition drives the in-app desktop popup/sound and persists the session's durable name. Detection is always on; there are no remote push channels to configure.

To tune or add agents, override agent_detection:

toml
[[notifications.agent_detection]]
name = "claude"
working_title_regex = '^\s*[\x{2800}-\x{28FF}]'

[[notifications.agent_detection]]
name = "my-agent"
working_title_regex = 'Thinking|Working'

Defining agent_detection replaces the built-in defaults, so include the Claude/Codex rule above if you still want it. To see what title your agent emits (for writing a regex), run the daemon at RUST_LOG=debug and watch the session.

In the Hive app

When a session goes idle the app raises a native desktop popup and plays a sound (both toggleable, per device, under Settings -> Notifications -> Desktop alerts). Each terminal in the sidebar gets a bell icon next to its close button to mute idle alerts for that session on this device. These are client-side preferences.

Task Scheduler

Tasks are created and managed through the Hive app or the WebSocket task messages described in tasks.md. Each task has a schedule that controls when it runs.

Schedule Types

One-shot - runs immediately when created, then stops:

json
{ "type": "one_shot" }

Interval - repeats every N seconds after each run completes:

json
{ "type": "interval", "seconds": 3600 }

Cron - runs on a cron schedule. Hive uses 6-field expressions with seconds at the front (seconds minutes hours day-of-month month day-of-week). Seconds are mandatory - a bare 5-field expression is rejected:

json
{ "type": "cron", "expression": "0 0 * * * *" }

Cron Expression Format

The scheduler uses the cron 0.12 crate and evaluates expressions in UTC. Expressions use six space-separated fields (a seventh optional year field is allowed):

┌──────────────── second (0-59)
│  ┌───────────── minute (0-59)
│  │  ┌──────────── hour (0-23)
│  │  │  ┌──────────── day of month (1-31)
│  │  │  │  ┌──────────── month (1-12 or names: jan-dec)
│  │  │  │  │  ┌──────────── day of week (1-7, Sunday=1, or names: sun-sat)
│  │  │  │  │  │
*  *  *  *  *  *

* and ? both mean "any". Steps (*/15), ranges (9-17), and lists (1,15) are supported. Day-of-month and day-of-week are combined with AND. The shorthands @hourly, @daily, @weekly, @monthly, and @yearly are also accepted.

Common examples:

ExpressionMeaning
0 * * * * *Every minute at second 0
0 0 * * * *Every hour at minute 0
0 0 9 * * *Daily at 09:00 UTC
0 0 9 * * MonEvery Monday at 09:00 UTC
0 0 9 * * Mon-FriWeekdays at 09:00 UTC
0 0 9 1 * *First day of every month at 09:00 UTC
* * * * * *Every second

Note: All times are UTC. The scheduler checks for due tasks every 100 seconds, so the actual fire time may lag behind the scheduled time by up to one scheduler tick.

TLS Configuration

The daemon can serve over wss:// (TLS) instead of plain ws:// by setting both tls_cert and tls_key in the config file.

Enabling TLS on the Daemon

toml
# Path to the PEM-encoded TLS certificate (full chain recommended).
tls_cert = "/etc/hived/cert.pem"

# Path to the PEM-encoded TLS private key.
tls_key = "/etc/hived/key.pem"

Both fields must be set. If either is missing or the files cannot be loaded, the daemon exits at startup with an error. When TLS is active the daemon logs:

INFO TLS enabled - listening on wss://

Generating a Self-Signed Certificate (for testing)

bash
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
  -days 365 -nodes -subj "/CN=localhost"

For production, use a certificate issued by a trusted CA (e.g. Let's Encrypt / certbot).

Connecting with TLS

CLI:

bash
hive --tls --host my-server.example.com ls
# or via environment variable
HIVE_TLS=true hive ls

Environment variable:

VariableCLI FlagDefaultDescription
HIVE_TLS--tlsfalseConnect using wss:// (TLS)

App (desktop / mobile / web): there is no manual TLS toggle in the Connect view. The app auto-negotiates the scheme per connection profile - it tries wss:// when the node offers it and falls back to ws:// otherwise, caching the working scheme. Note that wss:// requires connecting by the node's Tailscale MagicDNS hostname (e.g. my-node.tailnet.ts.net), not a bare IP, since the cert is issued for that hostname. See hive cert / the web client page for provisioning a TLS cert per node.

TLS Field Reference

FieldTypeDefaultDescription
tls_certOption<String>(none)Path to PEM certificate file. TLS is disabled when absent.
tls_keyOption<String>(none)Path to PEM private key file. TLS is disabled when absent.

Note: TLS applies only to the client-facing port (port, default 9178). Peer-to-peer cluster traffic (port 9179) does not currently use TLS.

Client Configuration

The hive client is configured entirely through CLI flags and environment variables. There is no config file.

Environment Variables

VariableCLI FlagDefaultDescription
HIVE_HOST--host127.0.0.1Daemon host address
HIVE_PORT--port9178Daemon port
HIVE_TOKEN--token(required)Authentication token
HIVE_NODES--nodes(none)Comma-separated cluster node list

Precedence

CLI flags override environment variables. When --nodes / HIVE_NODES is set, it overrides --host / --port.

Example Shell Setup

bash
# Standalone
export HIVE_TOKEN="a3f8b2c1d4e5..."

# Cluster
export HIVE_TOKEN="a3f8b2c1d4e5..."
export HIVE_NODES="10.0.0.1:9178,10.0.0.2:9178"

Cluster Configuration Examples

Two-Node Cluster

Node A (10.0.0.1):

toml
bind = "0.0.0.0"
port = 9178
token = "client-auth-token"

[cluster]
peers = ["10.0.0.2:9179"]
peer_port = 9179
cluster_token = "shared-cluster-secret"
advertise_addresses = ["10.0.0.1:9178"]

Node B (10.0.0.2):

toml
bind = "0.0.0.0"
port = 9178
token = "client-auth-token"

[cluster]
peers = ["10.0.0.1:9179"]
peer_port = 9179
cluster_token = "shared-cluster-secret"
advertise_addresses = ["10.0.0.2:9178"]

Three-Node Cluster

Each node lists the other two as peers:

toml
# On 10.0.0.1
[cluster]
peers = ["10.0.0.2:9179", "10.0.0.3:9179"]
peer_port = 9179
cluster_token = "shared-cluster-secret"
advertise_addresses = ["10.0.0.1:9178"]

Two-Node Cluster

No special configuration is needed for any cluster size. Leaderless clustering has no quorum: every node keeps serving reads and writes for the state it holds even when its peer is unreachable, and anti-entropy on reconnect merges whatever diverged. A two-node cluster is just two peers pointing at each other:

toml
[cluster]
peers = ["10.0.0.2:9179"]
peer_port = 9179
cluster_token = "shared-cluster-secret"

Concurrent edits to the same project or team made on two simultaneously isolated nodes resolve last-writer-wins by updated_at when they reconnect; the older edit is dropped. Distinct objects are never lost. See Clustering for the full merge semantics.

Hive - remote AI coding agents over WebSocket.