Configuration
Every option can be given on the command line, in configuration files, or both. Precedence, lowest first:
- built-in defaults;
- configuration files, in the order of
--config(repeatable; later files win); - command-line arguments.
An argument that replaces a value from a file is logged (info) with both values and
the file. Imported values are never logged, only where they come from; text with
placeholders is logged as it ends up. Secrets (mycelium-rest-url, which may carry
credentials) are never logged, however they are given.
continuum --help lists all options with their defaults.
Durations and sizes
Durations (--keepalive, --dormant-probe-max, --document-append,
--document-rewrite, --electorate-window, --clock-skew-warn, and the API's
settle, switchDelay and retention age) are a bare number of seconds (30, 0.5)
or components with units in descending order: ms, s, m, h, d, e.g. 500ms,
500s, 1h30m, 1d2h3s, 1.5h. Precision is a millisecond; anything finer is an
error. Sizes (--blob-chunk) are a bare number of bytes or a number with B, KiB,
MiB or GiB, e.g. 64KiB, 1.5MiB; they must come to whole bytes. In configuration
files both may also be numbers (seconds, bytes).
What the node reports (stats, /peers, remake reports) stays numeric, with the unit
in the name: uptimeSecs, dormantMs, savedBytes. Timestamps are Unix
milliseconds, named …At or …Until.
Files
TOML (.toml) for people, JSON (.json) for generated configuration. Keys are the
long option names. Nested tables join their keys with -, so these are the same:
mycelium-peers = ["tls://203.0.113.5:9651"]
[mycelium]
peers = ["tls://203.0.113.5:9651"]
Unknown keys are an error.
An example (example.toml has every option, explained):
include = ["/run/secrets/continuum.json"]
bind-addr = "[::1]:3000"
htpasswd = "/etc/continuum/htpasswd"
node-key-file = "/var/lib/continuum/node.key"
seeds = ["node1.example.org", "node2.example.org"]
alias = { command = ["hostname", "-s"] }
keepalive = "1m"
neighbours = 12
[mycelium]
enable = true
key-file = "/var/lib/continuum/mycelium.key"
peers = ["tls://203.0.113.5:9651", "quic://203.0.113.6:9651"]
The same as JSON, as a system like NixOS would generate it:
{
"include": ["/run/secrets/continuum.json"],
"bind-addr": "[::1]:3000",
"htpasswd": "/etc/continuum/htpasswd",
"node-key-file": "/var/lib/continuum/node.key",
"seeds": ["node1.example.org", "node2.example.org"],
"alias": { "command": ["hostname", "-s"] },
"mycelium": {
"enable": true,
"key-file": "/var/lib/continuum/mycelium.key",
"peers": ["tls://203.0.113.5:9651"]
}
}
Includes
include (a path or a list) loads other files first, in the order listed; an entry
{ path = "…", optional = true } is skipped where the file is missing. Relative paths
are relative to the including file. The including file's own values win over its
includes, later includes over earlier ones (with a warning, since two includes setting
the same option is usually a mistake). A cycle is an error.
This keeps public settings in version control and secrets elsewhere:
# continuum.toml, committed: the secrets, then local settings over them
include = ["/run/secrets/continuum.toml", { path = "local.toml", optional = true }]
seeds = ["node1.example.org"]
Transports
A node uses every transport enabled: --mycelium-enable, --libp2p-enable and
--tor-enable, each in builds with its cargo feature (--features mycelium,libp2p,
none by default). None is on by default: a node needs one at least, and without, the
error says which this build has. As each is its own option, a file that configures a
transport can enable it too, and including it is all it takes:
# continuum.toml
include = ["libp2p.toml"]
# libp2p.toml
[libp2p]
enable = true
listen = ["/ip4/0.0.0.0/tcp/9661", "/ip4/0.0.0.0/udp/9661/quic-v1"]
--transport-order is their order of preference (mycelium,libp2p,tor by default),
whether enabled or not; those it leaves out come after, in the default order. How a node
uses several: transports.md, Several transports. Options
of a transport the node doesn't use (not enabled), or this build doesn't have, are
ignored, and the node says which at start (Options of a transport this node doesn't use are ignored,
with the transport, whether it's built, and the options); a configuration shared
between nodes may carry them on purpose. A transport's options are checked when the
configuration is loaded if the node uses it (Mycelium's always, where it is built), so
--print-config catches a wrong one too.
What each transport is for and its options: transports.md.
Unsetting and conflicting options
A later source can set an option back to its default: --unset OPTION on the command
line (repeatable), unset = ["option", …] in a file (overriding its includes and
earlier files), or null for the option in a JSON file.
Some options can't be combined: mycelium-rest-url (use a Mycelium daemon running
elsewhere) excludes the options of a supervised daemon (mycelium-key-file,
mycelium-key-create, mycelium-network-key-file, mycelium-network-name,
mycelium-tcp-listen-port).
When a later source sets one side, values for the other side from earlier sources are
dropped (and logged), so --mycelium-rest-url on the command line just works with a
file that configures a supervised daemon. Setting both sides in the same source is an
error.
Other combinations contradict each other and are an error wherever their values come
from: genesis with seeds (a new swarm has nobody to join) or with recover-from.
To start a node with --genesis whose configuration file lists seeds, unset them:
--genesis --unset seeds.
Forcing
Some checks refuse to start the node because something looks wrong, but can be
overridden when you know better: --force lists them by name, or is true for all.
An unknown name is an error. The API's ?force= parameters use the same format.
--force is accepted on the command line only: it is meant for the one start it was
given for, and in a configuration file it would apply to every start.
| Name | Check |
|---|---|
swarmId |
the document to start on (--recover-from, --document-file) is of another swarm than the state file remembers; with it, the document's swarm wins |
badDocumentFile |
the document file can't be read; with it, it is moved aside and the node starts without it |
replaceDocumentFile |
--recover-from while the document file holds a document; with it, the backup wins |
Names are camelCase everywhere, on the command line too, like the API's
(switchDelay, supersede, alive).
Importing values
A value can be taken from elsewhere instead of written out:
| In a file | On the command line | Value |
|---|---|---|
{ env = "NAME" } |
env:NAME |
environment variable |
{ file = "PATH" } |
file:PATH |
file contents |
{ command = "COMMAND" } |
cmd:COMMAND |
output of sh -c COMMAND |
{ command = ["prog", "arg"] } |
output of the program, no shell |
Commands run as the node's user, at startup: whoever can write the configuration can run
them (trust.md).
The content is text with trailing line breaks removed, read like a command-line
value: numbers for numeric options, true/false for switches, comma- or
line-separated items for lists. With format = "json" in a file, or +json on the
command line (env+json:SEEDS, file+json:PATH, cmd+json:COMMAND), it is parsed
as JSON instead (e.g. a list as ["a", "b"]).
In files, relative paths are relative to the file, and commands run in its directory.
Any other text value can mix fixed text with imported values: each ${env:NAME},
${file:PATH} or ${cmd:COMMAND} in it is replaced by that content, as text (no
+json in a placeholder). $$ is a $, and any other $ outside a placeholder is
an error (a shell-style $NAME would otherwise silently stay as it is). A placeholder
ends at the } that matches its {: braces inside it balance, and in a command, braces
in shell quotes ('...', "...") or after a backslash don't count, so a command may
hold $, quotes and braces of its own. Placeholders don't nest. literal: on the
command line turns this off too.
continuum --alias 'node-${env:HOSTNAME}'
alias = "${env:SITE}-${cmd:hostname -s}"
# A command with both kinds of quotes: a TOML literal string ('''...''') needs no escapes.
libp2p-listen = ['''/ip6/${cmd:ip -j addr show wireguard | jq -r '.[].addr_info[]|select(.family=="inet6" and .scope=="global").local'}/tcp/9661''']
A command that prints several lines (above: several addresses) doesn't give one item
per line: as a list element the text is one item, line breaks and all, and as a whole
list value it is split at commas and line breaks, the fixed text going with the first
and last item only. Make it print one (| head -n 1).
List elements can be imported one by one:
seeds = ["node1.example.org", { env = "EXTRA_SEED" }]
On the command line, literal: passes a value that happens to start with one of the
prefixes: --alias literal:env:prod.
continuum --config /etc/continuum.toml --alias env:HOSTNAME --seeds cmd+json:'my-inventory --seeds'
Imports are read once, at start.
Checking the configuration
--print-config prints the effective configuration as JSON and exits: every option
with its value and where it came from (default, file with the file, or
argument; unset if it was set back to its default there). Imported values are left
out and their importers listed instead (imported), and secrets are left out however
they were given (secret), so the output can be shared. Text with placeholders is
shown as it ends up, with the text as written under templates: keep secrets of
other options out of placeholders, and import them whole instead. A running node
answers the same at GET /admin/config; with ?values=original, text with
placeholders is shown as written instead.
continuum --config /etc/continuum.toml --print-config | jq .options.seeds
