# An example configuration with every option, explained (`configuration.md` has the # rules). Use it with `continuum --config example.toml`; `continuum --print-config` # shows what a configuration amounts to, and `continuum --help` lists the options. # # Keys are the long option names. Nested tables join their keys with `-`: `key-file` # under `[mycelium]` is `mycelium-key-file`. Options left out (or commented out, as most # are here) take their defaults, shown in the comments. Command-line arguments win over # files. # # Any value can come from elsewhere instead of being written out: # { env = "NAME" }, { file = "PATH" }, { command = "COMMAND" } or # { command = ["program", "argument"] }, with format = "json" to parse it as JSON; # and text can mix in such values: "node-${env:HOSTNAME}" (`$$` for a `$`). # Imported values are never logged or shown by --print-config. # --- Other files ----------------------------------------------------------------------- # Load these first (a path or a list), in this order; relative to this file. This file's # own values win over its includes, later includes over earlier ones. An entry # { path = "...", optional = true } is skipped where the file is missing. # include = ["/run/secrets/continuum.toml", { path = "local.toml", optional = true }] # Options to set back to their defaults here (e.g. one an include sets). # unset = ["alias"] # --- Who this node is ------------------------------------------------------------------ # This node's key (Ed25519, 32 raw bytes): who the node is, whatever transport reaches # it. Keep it with the node: a new key is a new node. Required (but with relay-only). node-key-file = "/var/lib/continuum/node.key" # Create a new node key at node-key-file if there is no file there (a new node). Without # it, a missing key file is an error, so a mistyped path never makes a new node. Best # given once on the command line (--node-key-create) rather than kept here. # node-key-create = false # A name for this node, shown next to its node ID (/peers, metrics). Published in the # member list; not unique, never an identity. # alias = "node-${env:HOSTNAME}" # Only relay (libp2p circuit relay), without being a node: no document, no swarm, no # node key. Needs libp2p alone (libp2p's enable, no other), its listen and # key-file; options about a swarm or a document (node-key-file, seeds, genesis, # state-file, ...) are errors with it. The HTTP API, with htpasswd, has /health, # /admin/stats and /admin/log only. List such relays in the document # (_continuum/relays) or in libp2p's relays. # relay-only = false # --- The HTTP API ------------------------------------------------------------------------ # Address of the HTTP API. Plain HTTP: to expose it, put a TLS proxy in front. # bind-addr = "127.0.0.1:3000" # Users of the HTTP API (an htpasswd file with Argon2 hashes). Required (but with # relay-only: without it, no HTTP API). htpasswd = "/etc/continuum/htpasswd" # HTTP API users (from htpasswd) who may write protocol data under /store/_continuum # (remake records, members, forget records), e.g. to repair a stuck remake. Such writes # act on the whole swarm and are logged. Epoch metadata and the swarm ID are never # writable. # protocol-writers = ["alice"] # How log lines look: text (timestamped), journal (for systemd's journal: no timestamp or # colours, the level as each line's priority, for journalctl -p), or auto: journal when # stdout goes to the journal, else text. # log-format = "auto" # Log node IDs and transport identities as they are. Without it, a member's read # `:` wherever they appear (`continuum:`, `libp2p:`), as the # member list names it. # log-raw-ids = false # Enable the /debug endpoints (tests only). # debug-api = false # --- The swarm --------------------------------------------------------------------------- # How to reach a node of the swarm to join: an address (`mycelium:` or # `libp2p:`, as GET /admin/stats lists them), a Mycelium public key or IPv6 # address, a host name with an AAAA record for one, or a libp2p multiaddr (with or # without /p2p/). A node keeps trying until it has joined. # seeds = ["node1.example.org", "libp2p:/ip6/fd00::1/tcp/9661"] # Start a new swarm, as its genesis node, instead of joining one through seeds. Every # start with it starts another new swarm: drop it once the swarm exists, and give the # node seeds instead. Can't be combined with seeds. With a state file that remembers a # swarm, it starts that swarm again instead (empty; nodes of it still running merge with # this one). Best given once on the command line (--genesis). # genesis = false # With genesis: make the new swarm's document as a remake of this kind would, instead of # starting it empty: import:PATH (an Automerge document, e.g. a backup, without its # history and protocol data) or replace:PATH (a JSON document, the store root); compact # changes nothing. # remake = "import:/var/backups/continuum.automerge" # Start the swarm again from a backup (as GET /admin/document saves it): this node loads # it, on the swarm and epoch it names, and is ready at once; other nodes join it. # Members of the swarm still running merge with it, or it rejoins them if they moved on # to a newer epoch (the backup is then dropped). For one start: command line. # recover-from = "/var/backups/continuum.automerge" # Pre-shared swarm key: only nodes that know it may join (raw bytes, at least 16; e.g. # `head -c 32 /dev/urandom`). Without it, any node that can reach a member may join. # swarm-key-file = "/etc/continuum/swarm.key" # Create a new swarm key at swarm-key-file if there is no file there: on the swarm's # first node, as the others need a copy of it (a key of their own keeps them out). # Without it, a missing key file is an error. # swarm-key-create = false # Not in files: `force` (override checks that refuse to start: swarmId, # badDocumentFile, replaceDocumentFile) is for one start, on the command line only. # --- Files of this node ------------------------------------------------------------------ # Where this node remembers its swarm across restarts: the swarm ID and some members to # rejoin through (not the document). Written once ready, then rarely. Without it, a # restarted node knows only its configured seeds. # state-file = "/var/lib/continuum/state.json" # This node's document on disk. Read at start: the node resumes with it, ready at once. # Kept up to date while ready: changes are appended every document-append, and the file # is rewritten after a switch, a rejoin or a restart, and when the appended changes # outgrew it. # document-file = "/var/lib/continuum/document.automerge" # How often changes are appended to the document file. # document-append = "5s" # Shortest time between rewrites of the document file because its appended changes # outgrew its full save (a new epoch or a restart rewrites it at once). # document-rewrite = "1h" # --- Timings and sizes ------------------------------------------------------------------- # Durations: seconds (30, 0.5) or with units (500ms, 30s, 1m, 1h30m, 7d). # Sizes: bytes or with units (64KiB, 4MiB). # Keepalive interval; dormancy, resync and probe timings derive from it. 15–30s on a # LAN or between servers, 1–2m over WAN, mobile or metered links. # keepalive = "1m" # Longest wait between HELLOs to a member that doesn't answer: they start at 10 # keepalives and double up to this, so it bounds how long a returning member, or the # other side of a partition, takes to be noticed. Default: 60 keepalives (1h at 1m). # dormant-probe-max = "15m" # Neighbours each node keeps (at least 2). Swarms of up to this many + 1 nodes are a # full mesh; larger ones sync over several hops. With several transports, about this # many per transport. # neighbours = 12 # Members that renewed their alive stamp within this time vote on remakes (a majority of # them must agree). At least 7h. Members renew their stamps every seventh of the # shortest window any of them publishes, hourly at most, so nodes may differ. Matters # on the node that coordinates a remake. # electorate-window = "7d" # What becomes of a write a remake or rejoin would carry over into the new epoch though # a newer one touched its path (last writer wins, by the writers' clocks): "drop" leaves # it out (logged), "quarantine" keeps it for an operator (GET /admin/quarantine). # superseded-writes = "drop" # Warn in the log when this node's clock differs from most of its peers' by more than # this, for sure (beyond half the round trip it was measured over, from their PONGs); 0: # never. # clock-skew-warn = "2s" # Chunk size when serving large transfers to other nodes (e.g. an epoch's snapshot to a # rejoining node), 1KiB to 4MiB. # blob-chunk = "64KiB" # --- Transports -------------------------------------------------------------------------- # The transports' order of preference, whether enabled or not (each is enabled in its # own table: enable = true). A peer is reached on the first enabled one both have until # the paths are measured, then on the fastest; nodes on several carry the traffic # between nodes that share none. Options of a transport not enabled are ignored. # transport-order = ["mycelium", "libp2p", "tor"] [mycelium] # Use this transport (in builds with the mycelium feature). A node needs one at least; # none is on by default. # enable = false # Peers of the supervised Mycelium daemon (overlay links), not continuum peers. Public # Mycelium nodes are used only if listed here. # peers = ["tls://127.0.0.1:9651"] # Private key of the supervised Mycelium daemon (its identity, and so its address); a # new one each start if absent. # key-file = "/var/lib/continuum/mycelium.key" # Create a new key at key-file if there is no file there (a new Mycelium address). # Without it, a missing key file is an error. # key-create = false # TCP port the supervised daemon listens on for overlay peers; a free port if absent. # tcp-listen-port = 9651 # A private Mycelium network for the supervised daemon: its key and its name, together. # They keep other Mycelium nodes from reaching ours at all. # network-key-file = "/etc/continuum/mycelium-network.key" # network-name = "example" # Use a Mycelium daemon running elsewhere (its REST API) instead of supervising one. # Excludes the options above except peers. Secret: a URL may carry credentials. # rest-url = "http://[::1]:8989" [libp2p] # Use this transport (in builds with the libp2p feature). # enable = false # Where to listen. Without, the node only connects out (peers answer over its # connections, and a relay makes it reachable). Its listen addresses are published in # the member list, loopback and link-local ones left out. # listen = ["/ip4/0.0.0.0/tcp/9661", "/ip4/0.0.0.0/udp/9661/quic-v1", "/ip6/::/tcp/9661"] # The addresses to publish instead of those listened on, e.g. a public address forwarded # to this node's listen port. # announce = ["/ip4/203.0.113.7/tcp/9661"] # Its key (Ed25519, 32 raw bytes), which makes its peer ID; a new one each start if # absent (the node ID stays the same either way). Worth keeping on a relay, so others' # addresses through it stay the same. # key-file = "/var/lib/continuum/libp2p.key" # Create a new libp2p key at key-file if there is no file there (a new peer ID). Without # it, a missing key file is an error. # key-create = false # Relay for nodes of the swarm that can't be dialled (bounded: 1024 circuits, 64 per # peer, each up to an hour and 1 GiB), and say so in the member list, where nodes that # need a relay find it. Needs a listen address others can dial. # relay = false # Relays to use when this node can't be dialled (no listen address others could dial, # or AutoNAT says so): multiaddrs, with or without the relay's peer ID. It picks up to # two relays, these first, then members that relay. Any libp2p node serving circuit # relay v2 will do; most keep circuits short (2 minutes, 128 KiB), enough to punch holes. # relays = ["/ip4/203.0.113.7/tcp/9661"] # Find nodes on the LAN (multicast DNS, IPv4). Those that speak continuum are seeds # while joining, so nodes on one LAN find each other without seeds; a swarm key keeps a # new node from joining another swarm on the same LAN. # mdns = false # Ask the router to forward this node's listen ports (UPnP), and publish where, so that # nodes elsewhere can dial it without a relay or hole punching. Opens those ports on the # router for as long as the node runs. # upnp = false # Address ranges libp2p leaves out (VPNs, say): neither published nor offered for hole # punching nor dialled, and connections there are refused. Announced addresses are # published anyway. # ignore = ["10.244.0.0/16", "42::/16"] # Hosts on the internet (names or addresses), only to tell whether this node has a # route to the internet, per IPv4 and IPv6 (the routing table is asked; nothing is sent # to them). Where it has none, its own public addresses of that family aren't # published. Without, it isn't asked. # internet-hosts = ["one.one.one.one"] # Use public libp2p infrastructure: join the IPFS DHT (through the IPFS bootstrap nodes) # to find peers' addresses and public relays, picked after members that relay. Public # relays only keep circuits for minutes and some KiB: enough to punch holes through # NATs. Makes this node's peer ID and addresses known to the DHT. Without it, nothing # public is used. # public = false [tor] # Use this transport (in builds with the tor feature). # enable = false # Where arti (Tor, embedded) keeps its state and cache, among them the onion service's # key, which makes its address: keep it with the node. Required with tor. # state-dir = "/var/lib/continuum/tor" # The port of this node's onion service. # port = 9662