Skip to main content

Configuration Reference

HomeCore is configured with a single TOML file. By default it looks for config/homecore.toml relative to the base directory (current working directory, or --home / HOMECORE_HOME).

# Config resolution order:
# 1. --config /path/to/file
# 2. HOMECORE_CONFIG env var
# 3. {base_dir}/config/homecore.toml

Full annotated example

# ── Server ────────────────────────────────────────────────────────────────────
[server]
host = "0.0.0.0"
port = 8080

# IP addresses or CIDR ranges that bypass JWT authentication entirely.
# Requests from these IPs are granted Admin-level access without a token.
# Useful for trusted LAN clients (dashboards, scripts on localhost).
# When a Bearer token IS present, JWT validation always runs regardless of whitelist.
whitelist = ["127.0.0.1", "192.168.1.0/24"]

# ── MQTT Broker ───────────────────────────────────────────────────────────────
[broker]
host = "0.0.0.0"
port = 1883

# Optional TLS listener (runs alongside plain-text port)
# tls_port = 8883
# cert_path = "/etc/homecore/broker.crt"
# key_path = "/etc/homecore/broker.key"

# When [[broker.clients]] entries are present, the broker requires credentials.
# Leave empty for open-access (development / trusted networks).
[[broker.clients]]
id = "internal.core"
password = "a-strong-random-password"
allow_pub = ["homecore/#"]
allow_sub = ["homecore/#"]

[[broker.clients]]
id = "plugin.hue"
password = "hue-plugin-password"
allow_pub = ["homecore/devices/hue_+/state", "homecore/plugins/hue/+"]
allow_sub = ["homecore/devices/hue_+/cmd"]

# ── Authentication ────────────────────────────────────────────────────────────
[auth]
# JWT signing secret. Change this! Use a long random string.
# If not set, a random secret is generated each startup (tokens expire on restart).
jwt_secret = "change-this-to-a-long-random-string"
token_expiry_hours = 24

# ── Location (required for solar triggers) ───────────────────────────────────
[location]
latitude = 38.9072 # Washington D.C. defaults
longitude = -77.0369
timezone = "America/New_York"

# ── Storage ───────────────────────────────────────────────────────────────────
# Paths are relative to base_dir unless absolute.
[storage]
state_db_path = "data/state.redb" # device registry, rules, users, scenes
history_db_path = "data/history.db" # time-series attribute history

# ── Rules ─────────────────────────────────────────────────────────────────────
[rules]
# Directory containing rule RON files. Hot-reloaded on any file change.
dir = "rules"

# ── Scheduler ─────────────────────────────────────────────────────────────────
[scheduler]
# On restart, fire time-based triggers that occurred within this window.
# Set 0 to disable catch-up entirely.
catchup_window_minutes = 15

# ── Startup ───────────────────────────────────────────────────────────────────
[startup]
# Seconds to wait after startup before mode manager publishes initial modes.
# Gives plugins time to connect and subscribe before receiving cmd messages.
plugin_ready_delay_secs = 10

# ── Modes ─────────────────────────────────────────────────────────────────────
[modes]
# Path to modes.toml file. Hot-reloaded on change.
path = "config/modes.toml"

# ── Calendar ──────────────────────────────────────────────────────────────────
# [calendar]
# dir = "calendars" # directory of .ics files; hot-reloaded
# expansion_days = 400 # how far ahead to expand recurring events

# ── Notifications ─────────────────────────────────────────────────────────────
[notify]
[[notify.channels]]
name = "telegram"
type = "telegram"
bot_token = "123456789:ABCDEFxxxxxxxxxxxxxxxxxxxxxxx"
chat_id = "-1001234567890"

[[notify.channels]]
name = "pushover"
type = "pushover"
api_key = "your-pushover-app-key"
user_key = "your-pushover-user-key"

[[notify.channels]]
name = "email-alerts"
type = "email"
from = "homecore@yourdomain.com"
to = ["you@yourdomain.com"]
[notify.channels.smtp]
host = "smtp.yourdomain.com"
port = 587
username = "homecore@yourdomain.com"
password = "smtp-password"
starttls = true

# ── Logging ───────────────────────────────────────────────────────────────────
[logging]
level = "info"
time_display = "local" # "local" | "utc"

[logging.targets]
hc_core = "info"
hc_api = "info"
hc_broker = "warn"
hc_mqtt_client = "info"

[logging.stderr]
enabled = true
format = "pretty" # "pretty" | "compact" | "json"
ansi = true

[logging.file]
enabled = false
dir = "logs"
prefix = "homecore"
rotation = "daily" # "daily" | "hourly" | "weekly" | "never"
max_size_mb = 100
compress = true
format = "json"

# [logging.syslog]
# enabled = false
# transport = "udp" # "udp" | "tcp"
# host = "192.168.1.100"
# port = 514
# protocol = "rfc5424" # "rfc5424" | "rfc3164"
# facility = "daemon"
# app_name = "homecore"

# ── Web Admin UI ──────────────────────────────────────────────────────────────
[web_admin]
enabled = false # serve pre-built Leptos/WASM admin UI
# dist_path = "ui/dist" # path to trunk build output, relative to home dir

# ── Engine ────────────────────────────────────────────────────────────────────
[engine]
drain_timeout_secs = 10 # time to wait for in-flight rule tasks on shutdown
fire_history_limit = 500 # max evaluation records per rule

# ── Plugins (managed) ────────────────────────────────────────────────────────
# Each [[plugins]] entry defines a managed plugin that HomeCore supervises.
# [[plugins]]
# id = "plugin.hue"
# binary = "plugins/hc-hue/bin/hc-hue" # relative to HOMECORE_HOME
# config = "plugins/hc-hue/config/config.toml"
# enabled = true

# [[plugins]]
# id = "plugin.wled"
# binary = "plugins/hc-wled/bin/hc-wled"
# config = "plugins/hc-wled/config/config.toml"
# enabled = true

# ── Ecosystem profiles ────────────────────────────────────────────────────────
# [ecosystem]
# profiles_dir = "config/profiles" # directory of .toml profile files

Section reference

[server]

KeyTypeDefaultDescription
hoststring"0.0.0.0"Bind address for the HTTP/WebSocket API
portinteger8080Listen port
whitelistarray of strings[]IP addresses/CIDR ranges that bypass JWT auth

[broker]

KeyTypeDefaultDescription
hoststring"0.0.0.0"Broker bind address
portinteger1883Plain-text MQTT port
tls_portintegerTLS MQTT port (requires cert_path and key_path)
cert_pathstringPath to TLS certificate file (PEM)
key_pathstringPath to TLS private key file (PEM)

[[broker.clients]] entries:

KeyDescription
idClient ID (used as MQTT username)
passwordPlain-text password (hashed internally)
allow_pubDocumentation of allowed publish topics
allow_subDocumentation of allowed subscribe topics

Note: The embedded broker enforces connection-level credentials but does not enforce per-topic ACL. allow_pub/allow_sub are documentation only. For strict topic ACL, use an external broker.

[auth]

KeyTypeDefaultDescription
jwt_secretstringrandomHS256 signing secret. Set a fixed value so tokens survive restarts.
token_expiry_hoursinteger24JWT lifetime

[location]

Required for SunEvent and SunEvent offset triggers.

KeyTypeDescription
latitudefloatDecimal degrees, e.g. 38.9072
longitudefloatDecimal degrees, e.g. -77.0369
timezonestringIANA timezone name, e.g. "America/New_York"

[scheduler]

KeyTypeDefaultDescription
catchup_window_minutesinteger15On restart, fire missed time-based triggers that occurred within this many minutes. Set 0 to disable.

[startup]

KeyTypeDefaultDescription
plugin_ready_delay_secsinteger10Grace period before mode manager publishes initial states. Prevents command-before-subscribe race with plugins.

[engine]

KeyTypeDefaultDescription
drain_timeout_secsinteger10Time to wait for in-flight rule tasks on shutdown before force-stopping
fire_history_limitinteger500Maximum evaluation records stored per rule

[web_admin]

KeyTypeDefaultDescription
enabledbooleanfalseServe the pre-built Leptos/WASM admin UI as static files via tower-http ServeDir
dist_pathstring"ui/dist"Path to the trunk build output directory, relative to HOMECORE_HOME

When enabled, HomeCore serves the Leptos admin UI at the root path. API routes at /api/v1 take priority over static file serving. A SPA fallback returns index.html for any unmatched path, enabling client-side routing. Disabled by default so that during development you can use trunk serve separately.

[[plugins]]

Each entry defines a managed plugin that HomeCore supervises. Managed plugins support heartbeat monitoring, start/stop/restart, and remote configuration.

KeyTypeDescription
idstringPlugin ID (matches the plugin's plugin_id config)
binarystringPath to the plugin binary (relative to HOMECORE_HOME)
configstringPath to the plugin config file (relative to HOMECORE_HOME)
enabledbooleanWhether the plugin should be started automatically

modes.toml reference

The modes configuration is a separate file (config/modes.toml), hot-reloaded when changed.

# Solar modes — computed from sunrise/sunset with optional offset
[[modes]]
name = "mode_night"
type = "solar"
# on_at_offset = 0 # minutes after sunset (negative = before)
# off_at_offset = 0 # minutes after sunrise (negative = before)

# Manual boolean modes — toggled via API or rule actions
[[modes]]
name = "mode_away"
type = "manual"
default = false

[[modes]]
name = "mode_vacation"
type = "manual"
default = false

[[modes]]
name = "mode_movie"
type = "manual"
default = false

Solar mode behavior:

  • mode_night is true from sunset to sunrise, false during daylight hours
  • Offsets shift the transition point: on_at_offset = -30 turns night mode on 30 minutes before sunset
  • Mode state is republished via MQTT as DeviceStateChanged at every transition

Mode API:

# Get all mode states
curl -s http://localhost:8080/api/v1/modes -H "Authorization: Bearer $TOKEN" | jq

# Set a manual mode on
curl -s -X PATCH http://localhost:8080/api/v1/modes/mode_away \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"on": true}'

# Adjust solar offset (minutes relative to sunset/sunrise)
curl -s -X PATCH http://localhost:8080/api/v1/modes/mode_night \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"on_at_offset": -30}'

Environment variable overrides

Some sensitive values can be set via environment variables instead of the config file:

HOMECORE_JWT_SECRET="your-secret"   \
HOMECORE_LAT="38.9072" \
HOMECORE_LON="-77.0369" \
HOMECORE_TZ="America/New_York" \
./bin/homecore

These are particularly useful in Docker deployments where secrets should not be baked into config files.