aboutsummaryrefslogtreecommitdiffstats
path: root/docs/configuration.md
blob: d3f7e566755132d55ece896f1072a71813b8226e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# Configuration

Every option can be given on the command line, in configuration files, or both.
Precedence, lowest first:

1. built-in defaults;
2. configuration files, in the order of `--config` (repeatable; later files win);
3. 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:

```toml
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`](example.toml) has every option, explained):

```toml
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:

```json
{
  "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:

```toml
# 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:

```toml
# 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`](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`](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`](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.

```sh
continuum --alias 'node-${env:HOSTNAME}'
```

```toml
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:

```toml
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`.

```sh
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.

```sh
continuum --config /etc/continuum.toml --print-config | jq .options.seeds
```