Configuration
Every configuration key, its type and default, plus the annotated example file and the four layers that resolve them.
On this page
Seamless reads a single YAML file. Every key also has a SEAMLESS_* environment
override.
Where the config comes from
The file is looked up in this order, first hit wins:
$SEAMLESS_CONFIG~/.config/seamless/seamless.yaml./seamless.yaml
Precedence
Four layers resolve each key. Later layers win:
- Defaults - the built-in values in the table below.
- File - whatever the YAML sets.
- Environment -
SEAMLESS_*overrides the file. - Runtime override (DB) - the console's Settings forms store some blocks in the database. They win over file and env, take effect without a daemon restart, and stay until reset.
That fourth layer covers three blocks where the stored row simply wins:
| Block | Written by | When it takes effect |
|---|---|---|
briefing: |
Settings → Briefing | From the next session start. |
features: |
Settings → Features | Immediately in the console; an agent sees it from its next session (tool lists and briefings alike). |
console.level |
Settings → Experience, or the Home welcome card | Immediately, in the console only. |
The update: block's two switches, update.check and update.auto, are merged
the other way round: the more restrictive value wins. Settings →
Updates can always turn either off, and can turn
it on when file and env do not say false - but an explicit check: false or
auto: false in the file (or SEAMLESS_UPDATE_CHECK=false,
SEAMLESS_UPDATE_AUTO=false) is final, and the console shows that switch locked
with where it was set. check: false promises no update traffic at all, so it
is never one click from untrue, and with checks off nothing installs by itself
either. See the update block.
It exists so you can change what agents get injected - and what they can reach -
while they are running, and it is the one place where the config file is not the
last word: check the console before concluding a briefing:, features:, or
console.level setting is being ignored. No form ever writes your config file,
and Reset on each clears the stored row and hands that block back to
file/env.
Overrides can be in force without you having set them. Upgrading an
installation that already holds trial data seeds a stored features: override
with research on, so a feature that now ships off does not disappear from under
data you were already using. The console labels it a stored override, not your
choice; Reset clears it like any other.
The console level
console.level (SEAMLESS_CONSOLE_LEVEL) is how much of the
console you see: basic,
standard, or advanced. It is presentation only - briefings, MCP tools,
hooks, recall, and the gardener are identical at every level - and a screen a
level leaves out of the sidebar still opens from a link. A value outside those
three stops the daemon at startup with an error naming them; an empty value
means the default, basic.
A fresh installation starts at basic. Upgrading an installation that had
already recorded sessions stores advanced once, as the same kind of runtime
override (labeled "set by the upgrade" in Settings → Experience), so an upgrade
never hides a screen you were using. Like any override, it wins over this key
until Reset hands the level back to file/env.
The update check
The update: block drives the daemon's
automatic update checks and
automatic updates:
check(SEAMLESS_UPDATE_CHECK) is optional. Left unset, a release build checks and a build from source does not;trueorfalsedecides either way.falsemeans no request to GitHub at all, and so no automatic update either.check_interval(SEAMLESS_UPDATE_CHECK_INTERVAL) is the time between checks: a Go duration from1hto720h, default6h. Write the unit - a bare number other than0is refused rather than guessed at, since6could mean six seconds or six hours.auto(SEAMLESS_UPDATE_AUTO) is optional, and unset means on: an install made by the curl or PowerShell installer installs a newer release by itself. Homebrew, builds from source, client machines and layouts the daemon cannot vouch for are only told, whateverautosays (who updates itself).falsekeeps the check and its notices and leaves installing to you (seamlessd update, or Update now in the console). Likecheck: false, it is final, and the console cannot turn it back on. With checks off,autois off too.max_defer(SEAMLESS_UPDATE_MAX_DEFER) is how long a pending automatic update waits for a quiet daemon - no live agent session, no request in flight - before it takes the next lull in requests instead: a Go duration from1mto720h, default24h.0is refused. After 1.5 timesmax_deferit installs as soon as no request is in flight, and after twicemax_deferwhatever is in flight (when it installs).min_age(SEAMLESS_UPDATE_MIN_AGE) is the soak: how long a release must have been published before an automatic update takes it, measured on GitHub's clock. A Go duration from0(no wait) to720h, default24h. Installing by hand ignores it.
Set auto, max_defer and min_age in the file, not in the daemon's
environment. Any SEAMLESS_* variable besides SEAMLESS_CONFIG in the
daemon's environment makes the install notify-only - the installer rewrites
the service without it - so SEAMLESS_UPDATE_AUTO=true can never turn
automatic updates on, and the two durations set there switch them off rather
than tune them.
All five keys are new in v0.7.0. seamlessd releases before it refuse config
keys they do not know, so a file that sets one runs only with v0.7.0 or newer.
The example file spells out the keys whose default has a value and leaves
check and auto, whose default is unset, commented out.
Generating a key
mcp.api_key guards /api/mcp and the console. On a true first run - no
config file anywhere in the search order and no SEAMLESS_MCP_API_KEY in the
environment - seamlessd serve (or install-hooks) generates one and writes
it to ~/.config/seamless/seamless.yaml, so a fresh install never handles the
key by hand. An existing config file is never edited, even when its key is
empty; set one yourself:
openssl rand -hex 32
The daemon still starts with an empty key, but every MCP and hook request is
rejected until one is set - seamlessd doctor reports it as a warning.
Every key
| Key | Type | Default |
|---|---|---|
addr |
string | 127.0.0.1:8081 |
data_dir |
string | ~/.seamless |
role |
string | server |
server_url |
string | - |
allowed_hosts |
[]string | - |
tls.cert_file |
string | - |
tls.key_file |
string | - |
tls.ca_file |
string | - |
mcp.api_key |
string | - |
budgets.max_briefing_tokens |
int | 1500 |
budgets.recall_budget_tokens |
int | 1000 |
budgets.tool_event_max_chars |
int | - |
briefing.constraint_max_full |
int | 4 |
briefing.convention_max_full |
int | 4 |
briefing.memory_max_age_days |
int | - |
briefing.memory_max_items |
int | - |
briefing.findings_count |
int | 3 |
briefing.findings_max_age_days |
int | - |
briefing.ready_tasks_shown |
int | 3 |
briefing.pending_plan_max_days |
int | 7 |
briefing.stage_unknown_max_age_days |
int | 7 |
briefing.hard_cap_multiplier |
int | 2 |
briefing.include_parent_memories |
bool | true |
briefing.sibling_findings_count |
int | 2 |
briefing.include_sibling_memories |
bool | - |
briefing.utility_weight |
float64 | 0.4 |
briefing.utility_mode |
string | auto |
features.research |
bool | - |
features.momentum |
bool | - |
features.gamification |
bool | - |
console.level |
string | basic |
search.semantic_floor |
float64 | 0.3 |
llm.provider |
string | openai |
llm.openai.api_key |
string | - |
llm.openai.base_url |
string | https://api.openai.com/v1 |
llm.openai.chat_model |
string | gpt-4o |
llm.openai.embedding_model |
string | text-embedding-3-large |
llm.openai.embedding_dims |
int | 3072 |
llm.ollama.base_url |
string | http://127.0.0.1:11434 |
llm.ollama.chat_model |
string | llama3.3:latest |
llm.ollama.embedding_model |
string | qwen3-embedding:8b |
llm.ollama.embedding_dims |
int | - |
llm.anthropic.api_key |
string | - |
llm.anthropic.base_url |
string | https://api.anthropic.com |
llm.anthropic.chat_model |
string | claude-sonnet-5 |
gardener.enabled |
bool | true |
gardener.interval_minutes |
int | 60 |
gardener.dedup_threshold |
float64 | 0.88 |
gardener.staleness_days |
int | 90 |
gardener.digest_days |
int | 30 |
gardener.tool_event_retention_days |
int | 30 |
gardener.stale_plan_days |
int | 14 |
gardener.stale_stage_days |
int | 14 |
gardener.session_idle_minutes |
int | 45 |
capture.allowed_ports |
[]int | [80, 443] |
plan_capture.enabled |
bool | true |
plan_capture.auto_task |
bool | true |
plan_capture.inject_related |
bool | true |
update.check |
bool, optional | unset |
update.check_interval |
duration | 6h |
update.auto |
bool, optional | unset |
update.max_defer |
duration | 24h |
update.min_age |
duration | 24h |
Annotated example
This is seamless.yaml.example from the repository, verbatim.
# Seamless configuration example: every key, at its default. Safe to commit --
# it holds no secrets.
#
# `make install` seeds ~/.config/seamless/seamless.yaml from this file when that
# config does not exist yet, and never overwrites it afterwards. So fill in your
# secrets THERE, not here: the installed copy is the live one, and the daemon and
# hooks read it from any directory.
#
# Every key also has a SEAMLESS_* environment override, and env wins over the
# file. Config file search order: $SEAMLESS_CONFIG, ~/.config/seamless/seamless.yaml,
# ./seamless.yaml (gitignored -- the pre-install layout's config).
# HTTP bind address. Change it here and re-run `make install`: this file is the
# single source of truth for the bind, and the Makefile reads the port back out
# of it rather than assuming 8081.
# env: SEAMLESS_ADDR
addr: "127.0.0.1:8081"
# Data directory: SQLite database + markdown trees (memory/, notes/). A leading
# ~ is expanded to the home directory.
# env: SEAMLESS_DATA_DIR
data_dir: "~/.seamless"
# This install's part in a deployment. `server` (the default) runs the daemon
# here. `client` runs no daemon at all: serve refuses, there is no database and
# no corpus on this machine, and every surface -- the seam CLI, the hooks, the
# MCP registration -- dials server_url instead. A client REQUIRES server_url.
# env: SEAMLESS_ROLE
role: "server"
# The base URL clients dial, as a bare scheme://host[:port] with no path, query
# or fragment. Empty means "derive it from addr", which is what a loopback
# install wants. Set it when the bind address is not the address clients use: a
# wildcard bind, a LAN name, a TLS listener. Naming it also adds its host to the
# Host-header allowlist, which is what arms that guard on a wildcard bind.
# A wildcard host here is refused at load (0.0.0.0, ::, [::0], ...): that
# answers "where do I listen", not "where do I dial", so it belongs in addr
# instead.
# env: SEAMLESS_SERVER_URL
server_url: ""
# EXTRA Host-header values the daemon answers to, beyond the loopback names, a
# concrete bind host, and the host of server_url. Requests arriving under any
# other Host get 421 (the DNS-rebinding guard). Naming even one host turns the
# guard on for a wildcard bind, which otherwise has no allowlist to apply.
# env: SEAMLESS_ALLOWED_HOSTS (comma-separated, e.g. "studio.local,192.168.1.10")
allowed_hosts: []
tls:
# PEM certificate chain and private key. Both set -> the daemon serves https
# (TLS 1.2 floor) instead of http, and the console session cookie is marked
# Secure. One without the other is refused at load: it cannot serve TLS. The
# certificate's SANs must cover the host of server_url, which is what
# `seamlessd doctor` checks. A leading ~ is expanded.
# env: SEAMLESS_TLS_CERT_FILE / SEAMLESS_TLS_KEY_FILE
cert_file: ""
key_file: ""
# CLIENT-side extra root CA: the certificate the `seam` CLI adds to the system
# trust store when dialing an https server_url, for a private CA or a
# self-signed server. It is the client half of the pair above -- setting
# cert_file together with role: client is refused.
# env: SEAMLESS_TLS_CA_FILE
ca_file: ""
mcp:
# Static bearer key protecting /api/mcp and the console. On a true first run
# (no config file anywhere) `seamlessd serve` generates one into
# ~/.config/seamless/seamless.yaml; a file you author yourself needs one set
# by hand:
# openssl rand -hex 32
# An empty key is reported as a warning by `seamlessd doctor`.
# env: SEAMLESS_MCP_API_KEY
api_key: ""
budgets:
# Hard token budget for the SessionStart briefing.
# env: SEAMLESS_MAX_BRIEFING_TOKENS
max_briefing_tokens: 1500
# Default token budget for a recall response.
# env: SEAMLESS_RECALL_BUDGET_TOKENS
recall_budget_tokens: 1000
# Per-field cap (in runes) on captured Interactions transport events (tool-call
# args/result, hook prompt, session findings). 0 = unlimited: content is stored
# in full and the tool-event retention prune is the growth control. Set a
# positive value only as an opt-in guard against pathological payloads.
# env: SEAMLESS_TOOL_EVENT_MAX_CHARS
tool_event_max_chars: 0
# Briefing injection tunables: what the SessionStart <seam-briefing> auto-injects.
# Defaults reproduce the built-in behavior; every knob is also editable live in
# the console (Settings -> Briefing injection), which stores a runtime override
# in the database that WINS over this file and env until reset. Constraints and
# active-plan rollups are never dropped by these knobs (constraint_max_full only
# shapes how constraints render); a pinned stage is exempt too while its Status
# header marks a live gate.
briefing:
# Top-ranked constraints rendered as full bullets in the briefing's
# Constraints section; the rest collapse into one compact "+N more, equally
# binding" line that still names every one. 0 = no tiering (all full).
# env: SEAMLESS_BRIEFING_CONSTRAINT_MAX_FULL
constraint_max_full: 4
# Top-ranked conventions rendered as full bullets in the budget-competing
# Conventions section; the rest stay behind the section's count line
# ("recall kind=convention"). Starred conventions always render full.
# 0 = no tiering, like constraint_max_full.
# env: SEAMLESS_BRIEFING_CONVENTION_MAX_FULL
convention_max_full: 4
# Drop memory-index lines not updated within this many days. 0 = no filter.
# env: SEAMLESS_BRIEFING_MEMORY_MAX_AGE_DAYS
memory_max_age_days: 0
# Cap on memory-index lines before budget packing. 0 = budget-only.
# env: SEAMLESS_BRIEFING_MEMORY_MAX_ITEMS
memory_max_items: 0
# Recent findings injected. 0 hides the section.
# env: SEAMLESS_BRIEFING_FINDINGS_COUNT
findings_count: 3
# Drop findings older than this many days. 0 = no filter.
# env: SEAMLESS_BRIEFING_FINDINGS_MAX_AGE_DAYS
findings_max_age_days: 0
# Ready-task titles listed in the "Ready tasks" section. 0 hides the section.
# env: SEAMLESS_BRIEFING_READY_TASKS_SHOWN
ready_tasks_shown: 3
# Days a captured, unapproved Claude Code plan earns an "awaiting approval"
# line. 0 = no age cutoff.
# env: SEAMLESS_BRIEFING_PENDING_PLAN_MAX_DAYS
pending_plan_max_days: 7
# Grace window (days since last update) a stage memory stays pinned when its
# Status header is missing or not a live gate (open|in_progress|blocked).
# 0 = pin forever.
# env: SEAMLESS_BRIEFING_STAGE_UNKNOWN_MAX_AGE_DAYS
stage_unknown_max_age_days: 7
# Times budgets.max_briefing_tokens = the absolute truncation ceiling. 0 = 2.
# env: SEAMLESS_BRIEFING_HARD_CAP_MULTIPLIER
hard_cap_multiplier: 2
# A child project inherits its shared parent's active memories.
# env: SEAMLESS_BRIEFING_INCLUDE_PARENT_MEMORIES
include_parent_memories: true
# Recent findings from family-member projects. 0 hides the section.
# env: SEAMLESS_BRIEFING_SIBLING_FINDINGS_COUNT
sibling_findings_count: 2
# Fold family members' memories in as a low-priority "Sibling memories"
# section (their constraints/stages never cross over). Off by default.
# env: SEAMLESS_BRIEFING_INCLUDE_SIBLING_MEMORIES
include_sibling_memories: false
# Utility's share of the memory-index sort key: (1-w)*recency + w*utility,
# where utility is the time-decayed demand score from retrieval_stats.
# 0 = pure recency (legacy). Constraints/stages/favorites stay pinned.
# env: SEAMLESS_BRIEFING_UTILITY_WEIGHT
utility_weight: 0.4
# Gate for the briefing's utility re-ordering: auto = per project once the
# gardener's readiness latch trips; on = everywhere now; off = never. The
# bounded recall/prompt-recall utility boosts are always on.
# env: SEAMLESS_BRIEFING_UTILITY_MODE
utility_mode: auto
# Optional features. These ship OFF: a fresh installation exposes none of them
# until you turn one on -- here, via env, or live in the console (Settings ->
# Features), which stores a runtime override in the database that WINS over this
# file and env until reset. Turning a feature off hides its console screens, its
# MCP tools, and its agent-facing mentions; it never deletes stored data, and
# re-enabling restores every surface. An installation that already holds research
# data keeps the feature on across the upgrade (a one-time seeded override).
features:
# Research labs and trials: the Labs and Trials console screens, the trials
# search scope, and the lab_open, trial_record, trial_query MCP tools.
# env: SEAMLESS_FEATURES_RESEARCH
research: false
# Momentum surfaces woven into existing screens: the plan finish-line card
# (and its agent-briefing emphasis), the activity calendar with capture
# streaks, knowledge payoff moments, and project maturity stages.
# env: SEAMLESS_FEATURES_MOMENTUM
momentum: false
# The arcade layer of the Now screen: the day tape, personal records, the
# hot-streak pulse, and celebration moments. Every number is judged from
# real recorded activity; off, the Now screen carries no trace of it.
# env: SEAMLESS_FEATURES_GAMIFICATION
gamification: false
# The observability console. Presentation only: nothing here changes what your
# agents receive -- briefings, MCP tools, hooks, recall, and the gardener are
# identical at every level.
console:
# How much of the console you see. One of: basic | standard | advanced.
# basic -- the essentials: what your agents remember (Memories, Notes),
# what they did (Sessions), cleanup suggestions (Gardener).
# standard -- adds following the work: Now, Projects, Plans, Tasks.
# advanced -- every screen and knob, including Interactions and Retrieval.
# Hidden screens stay reachable by URL (they show a banner), so no link ever
# breaks. The console's own picker (Settings -> Experience, or the welcome
# card on Home) stores a choice in the database that WINS over this file and
# env until reset. A fresh installation starts at basic; an installation that
# already recorded sessions before levels existed starts at advanced (a
# one-time seeded choice), so an upgrade never hides a screen you were using.
# env: SEAMLESS_CONSOLE_LEVEL
level: basic
# Console search (the full page's fused semantic+lexical retrieval). Agent
# recall is not affected by these knobs.
search:
# Minimum cosine similarity a semantic-only hit needs to appear in results;
# hits the keyword leg also matched are exempt. Without a floor the cosine leg
# is pure nearest-neighbor, so any query -- including nonsense -- fills the
# page to its limit. 0 disables the floor. Useful values depend on the
# embedding model; 0.3 suits OpenAI text-embedding-3-*.
# env: SEAMLESS_SEARCH_SEMANTIC_FLOOR
semantic_floor: 0.3
llm:
# Primary provider for chat (session digests) and embeddings. OpenAI is the
# default and the first-class provider. One of: openai | ollama | anthropic.
# env: SEAMLESS_LLM_PROVIDER
provider: "openai"
openai:
# env: SEAMLESS_OPENAI_API_KEY
api_key: ""
base_url: "https://api.openai.com/v1"
# Any OpenAI chat model; set to your preferred one.
chat_model: "gpt-4o"
embedding_model: "text-embedding-3-large"
# Native dimensionality of the embedding model. 0 = auto-detect from the
# first embedding response (the store records dims per row regardless).
# Maximum: 65536.
embedding_dims: 3072
ollama:
base_url: "http://127.0.0.1:11434"
chat_model: "llama3.3:latest"
embedding_model: "qwen3-embedding:8b"
# 0 = auto-detect; maximum 65536.
embedding_dims: 0
anthropic:
# env: SEAMLESS_ANTHROPIC_API_KEY
api_key: ""
# env: SEAMLESS_ANTHROPIC_BASE_URL
base_url: "https://api.anthropic.com"
chat_model: "claude-sonnet-5"
# Anthropic has no embeddings API; when provider=anthropic, embeddings fall
# back to OpenAI if its api_key is set, else Ollama.
gardener:
# The gardener only ever proposes; it never mutates memory without an explicit
# apply. Safe to leave enabled.
# env: SEAMLESS_GARDENER_ENABLED
enabled: true
# Minutes between full gardener passes (dedup, staleness, stale-stage,
# dead-weight, digest, stale-plan, memory-wanted).
# env: SEAMLESS_GARDENER_INTERVAL_MINUTES
interval_minutes: 60
# Cosine similarity at/above which two active memories are proposed for a merge.
# env: SEAMLESS_GARDENER_DEDUP_THRESHOLD
dedup_threshold: 0.88
# Days of no activity (no update, injection, or read) before a memory is
# proposed for archiving. Constraints and stages are never archived this way
# (dead stages are handled by stale_stage_days below).
# env: SEAMLESS_GARDENER_STALENESS_DAYS
staleness_days: 90
# Trailing window of completed sessions rolled into a monthly digest proposal.
# env: SEAMLESS_GARDENER_DIGEST_DAYS
digest_days: 30
# Age (days) beyond which transport-level Interactions events (tool.call,
# hook.prompt) are pruned. 0 disables the prune. Domain events are never pruned.
# env: SEAMLESS_TOOL_EVENT_RETENTION_DAYS
tool_event_retention_days: 30
# Days after which a captured Claude Code plan still in draft/presented is
# proposed for abandonment (retag plan-status:abandoned). 0 disables the pass.
# env: SEAMLESS_GARDENER_STALE_PLAN_DAYS
stale_plan_days: 14
# Days without an update after which a stage memory that is not a live gate
# (Status done, missing, or unrecognized) is proposed for archiving.
# 0 disables the pass.
# env: SEAMLESS_GARDENER_STALE_STAGE_DAYS
stale_stage_days: 14
# Minutes of no activity before an active session counts as dead: the reaper
# expires it and the console stops showing it as live. One knob drives both,
# so they never drift. Must comfortably exceed a long single agent turn.
# Must be positive; omit the key to retain the built-in 45m default.
# env: SEAMLESS_GARDENER_SESSION_IDLE_MINUTES
session_idle_minutes: 45
capture:
# Destination ports the capture_url tool may dial, enforced in the dialer on
# the initial URL and on every redirect hop (alongside the private-IP and
# scheme guards). This is an SSRF control: widen it only for a host you trust,
# and never to reach anything on your own network. An empty list or a missing
# key means the 80/443 default -- it does NOT mean "any port". Ports outside
# 1-65535 are rejected at startup.
# env: SEAMLESS_CAPTURE_ALLOWED_PORTS (comma-separated, e.g. "80,443,8080")
allowed_ports: [80, 443]
plan_capture:
# Capture Claude Code plan-mode activity via the PostToolUse/SubagentStop/
# PermissionRequest hooks: plan-file saves upsert cc-plan notes, planning
# subagents cache as cc-agent notes, approval flips the plan note's status.
# env: SEAMLESS_PLAN_CAPTURE_ENABLED
enabled: true
# Create an "Implement plan: ..." tracking task when a plan is approved.
# env: SEAMLESS_PLAN_CAPTURE_AUTO_TASK
auto_task: true
# On a session's first captured plan iteration, return related prior plans and
# memories to the planning agent as additionalContext.
# env: SEAMLESS_PLAN_CAPTURE_INJECT_RELATED
inject_related: true
# Update check and automatic updates. The daemon asks GitHub's releases API
# whether a newer Seamless release exists -- one anonymous request that carries
# no identifiers -- and tells you when one does. auto, max_defer and min_age
# decide whether and when it also installs one by itself, which only an install
# the curl or PowerShell installer made ever does: Homebrew, builds from source,
# client machines and layouts the daemon cannot vouch for are only told.
# Set auto, max_defer and min_age here, not in the daemon's environment: any
# SEAMLESS_* variable besides SEAMLESS_CONFIG there makes the install
# notify-only, so their env variables below can turn automatic updates off but
# never on.
update:
# Unset (the default) means on for release builds and off for builds from
# source. false means zero update traffic: no request to GitHub at all, and so
# no automatic update either. The key is left commented out because seamlessd
# releases before v0.7.0 reject config keys they do not know, so setting it
# pins this file to v0.7.0 or newer.
# env: SEAMLESS_UPDATE_CHECK
# check: false
# Time between checks, as a Go duration (6h, 90m, 1h30m) from 1h to 720h. A
# bare number is refused rather than guessed at: write 6h, not 6.
# env: SEAMLESS_UPDATE_CHECK_INTERVAL
check_interval: 6h
# Whether the daemon installs a newer release by itself. Unset (the default)
# means on. false keeps the check and its notices and leaves installing to you
# (seamlessd update); a false here or in the environment is final, and the
# console cannot turn it back on. Commented out for the same reason as check.
# env: SEAMLESS_UPDATE_AUTO
# auto: false
# How long a pending automatic update waits for a quiet daemon -- no live
# agent session, no request in flight -- before it takes the next lull in
# requests instead. A Go duration from 1m to 720h.
# env: SEAMLESS_UPDATE_MAX_DEFER
max_defer: 24h
# How long a release must have been out before an automatic update takes it,
# so a bad one can be pulled first. A Go duration from 0 (no wait) to 720h.
# Installing by hand (seamlessd update) does not wait.
# env: SEAMLESS_UPDATE_MIN_AGE
min_age: 24h