Put a machine on the overlay, then get on with the work
Conflux is one binary that carries the anchor daemon and its control tool. On VeilNet's public network it enrols the machine itself; on a network of your own it installs the file your root node minted. Either way it starts an anchor and registers a boot service. Joining takes one command, and coming back after a reboot takes none.
On this page
Install
Download the build for the platform from the pre-release page, check it against SHA256SUMS, and install it somewhere that survives a reboot.
$ sha256sum -c SHA256SUMS --ignore-missing conflux-linux-amd64: OK $ sudo install -m 0755 conflux-linux-amd64 /usr/local/bin/conflux $ conflux version
Per-platform notes
| Platform | What else is needed |
|---|---|
| Linux | Nothing. TUN is in the kernel, and the service gets it through CAP_NET_ADMIN. Where SELinux refuses to run the extracted binaries, conflux detects it and prints the commands that fix it. |
| macOS | Apple silicon only; there is no Intel build. utun is in the kernel, so there is no driver to install. Releases are not notarised: if macOS refuses a downloaded binary, clear the quarantine attribute with xattr -d com.apple.quarantine conflux-darwin-arm64. |
| Windows | Run PowerShell as Administrator. TUN mode also needs wintun.dll, which conflux fetches and checks; conflux proxy needs no driver. See Windows. |
| FreeBSD, OpenBSD | As on Linux, except forwarding: for --subnet or --serve-exit with an interface, anchor prints the commands for the host rather than running them, as it does on macOS and Windows. The boot service is an rc script: enabled with sysrc and run under daemon(8) on FreeBSD, enabled with rcctl on OpenBSD. |
Windows
- Every command that changes the machine needs Administrator, and the service runs as
LocalSystem. - A TUN interface needs Wintun, which conflux does not ship: it is GPLv2, and a driver extracted from a program's own resources is the shape of a DLL hijack. The first time a TUN machine needs it, conflux fetches wintun 0.14.1 over HTTPS, checks the archive against a pinned SHA-256, and places
wintun.dllbeside the extracted anchor. A start that finds it missing, as after an upgrade, fetches it again. If the download fails, conflux prints where to put the file by hand. - Each release carries a build-provenance attestation, and
conflux versionprints the digests of what it extracts. --uplinkis refused: anchor has no way to open a link on Windows.
Running as a service
The service manager runs one process, conflux serve, the supervisor. At every boot it reads conflux.json, starts anchord, waits until it answers, renews the credential if it is past two thirds of its window, and starts the anchor. A start that fails is retried with a backoff up to thirty seconds. One that no retry changes, such as a configuration conflux refuses, taints the credential does not grant or an expired credential with nowhere to renew it, stops the supervisor after three attempts with exit 70; nothing to start at all is exit 78. While it runs, the supervisor asks every thirty seconds whether the anchor is still there and rebuilds it if not. A blocked anchor is left as it is: it is still running, outside its realm.
| Platform | Registered as | On exit 70 or 78 |
|---|---|---|
| Linux | systemd unit conflux.service, Type=notify, with ExecStart=/var/lib/conflux/conflux serve and Restart=on-failure | stays down: RestartPreventExitStatus=78 70, and the unit shows as failed |
| macOS | launchd job org.veilnet.conflux, in /Library/LaunchDaemons | started again after launchd's throttle interval, since launchd cannot exempt an exit code |
| FreeBSD, OpenBSD | rc script conflux, run under daemon(8) on FreeBSD and by rc.subr on OpenBSD | stays down: nothing restarts the supervisor, which restarts anchord itself |
| Windows | service conflux, displayed as VeilNet Conflux, as LocalSystem, automatic at boot, recovery after 5, 10 and 30 seconds | stays down: the service reports itself stopped, which the recovery actions do not count as a failure |
Two pairs of verbs look alike and are not. start and down act on the running anchor now, and a reboot behaves the same either way. install and uninstall act on the boot registration, and uninstall takes the identity with it.
| down | start | install | uninstall | |
|---|---|---|---|---|
| Running anchor | stopped | (re)started | (re)started | stopped |
| Boot service | kept | kept | registered | removed |
conflux.json | kept | kept | kept | deleted |
manifest.b64 | kept | kept | kept | deleted, permanently |
| Extracted binaries | kept | kept | kept | deleted |
Upgrading to a newer build
New builds of a pre-release are published under the same tag, so the version number alone does not tell two of them apart. conflux version also prints the commit a binary was built from, and that is the part to compare. To move a machine onto a newer build, replace the binary and run conflux start.
$ conflux version conflux 1.0.0-pre (f47de0fac83d24489664ca15fc6a317ac9c9f683) linux/amd64 $ sudo install -m 0755 conflux-linux-amd64 /usr/local/bin/conflux $ sudo conflux start
The new build's anchor extracts into a directory of its own rather than over the one the running daemon has open, and start copies the new conflux to where the service runs it from, then restarts the service onto it. Nothing is re-enrolled and the identity is untouched. Until the restart, conflux status reports the binaries as stale.
Removing
$ sudo conflux uninstall $ sudo rm /usr/local/bin/conflux
uninstall removes the boot service, the configuration and the identity. There is no other copy of the identity anywhere, so it asks first.
Quick start
Run conflux up on one machine, then run it on every other machine with the taint it printed. There is no account to create and no key to manage.
$ sudo conflux up
An IPv4 lets the other machines on this network reach this one by a v4 address.
A private address is advertised to them; give each machine that should be
reachable on its own a different one. The IPv6 address is derived from this
machine's identity and needs no answer.
IPv4 [e.g. 10.128.0.7/24, blank for none]: 10.128.0.1/24
Minted a taint for this network:
brhk-2mq9-tzva-6pjs-k4xe-nw7d-qf
This machine's credential is issued in it, for the life of its identity. Share it:
any machine that runs
conflux up --taint brhk-2mq9-tzva-6pjs-k4xe-nw7d-qf
is issued a credential in it too, and joins this network and nothing else.
Starting.
anchor anchor6btpa3gn6w4stipba4hekzho7caw6srfyy5puvbz7mfanaiept5a
overlay fd80:c4b9:99ae:4411:c531:fd4f:754f:f08a/48
ipv4 10.128.0.1/24
interface anchor0
taint brhk-2mq9-tzva-6pjs-k4xe-nw7d-qf
credential valid until 2026-10-06T06:35:43Z (29d 23h)
service active (systemd: conflux.service, enabled at boot)
Reachable from any machine that runs: conflux up --taint brhk-2mq9-tzva-6pjs-k4xe-nw7d-qf$ sudo conflux up --taint brhk-2mq9-tzva-6pjs-k4xe-nw7d-qf --ipv4 10.128.0.2/24
Passing --taint and --ipv4 answers the prompt in advance, so this is safe to run from a provisioning script. The taint is what the machine's enrolment asks for, so it counts the first time: once a machine holds a credential its taints are fixed, and a later --taint may only repeat them. The IPv4 question is asked once in a machine's life. The answer is kept, blank included, and running up again without the flag leaves the address alone. With nobody at a terminal to answer and neither --ipv4 nor --no-ipv4 given, conflux exits rather than wait.
$ ping 10.128.0.1 $ sudo conflux status $ sudo conflux peers
Give it a minute: peers find each other by gossip, which takes anywhere from fifteen to sixty seconds. A reboot needs nothing typed. The service is registered and enabled, the credential renews itself, and the machine comes back at the same overlay address.
Next, expose a service: a database, a dashboard or a self-hosted app, reachable from every machine that shares the taint and from nowhere else.
Concepts
Conflux runs an anchor, and the anchor protocol's model is covered in full in its own documentation. Three parts of it explain most of what conflux does.
An anchor is an identity
An anchor's name is its public key. The AnchorID conflux prints is derived from a 32-byte seed, and so is the overlay IPv6 address beside it. Nothing assigns either. The address stays the same for as long as the seed survives, which is why conflux enrols exactly once. It also cannot be changed, which is why losing the seed loses the machine's place on the network.
A realm is admission control
A realm is a root key and a credential per member. An anchor presents a chain proving it descends from the root, and the chain is checked during the handshake, before any data flows. Membership is a signature, not a list on a server. The public network's machines join ghost realm alpha; an organisation's machines join a tree of its own, with its own root node at the root. The architecture shows both.
Overlay addresses are derived, not assigned
Every anchor has an IPv6 address computed from its identity and its realm. It needs no coordination and cannot collide.
IPv4 is different, which is why up asks for one. Thirty-two bits is too few to derive without collisions, so a machine's IPv4 is chosen by its operator. It never crosses the overlay as IPv4: anchor translates traffic sent from it into IPv6 from the derived address. So nothing has to arbitrate it, and two machines may share one, though a machine that should be reachable at its IPv4 needs an address of its own. Any unicast address will do except one inside 198.18.0.0/15, where anchor answers the IPv4 peers it translates for, and which it will not send from.
A private address is advertised, and peers reach the machine at it. A public one is sent from and not advertised. A length, as in 10.128.0.7/24, routes the rest of that range to the peers answering there. Blank is a valid answer, and a common one: the machine still reaches IPv4 peers, and is reached at its IPv6 address.
Taints
A taint is a compartment label. Inside a realm, taints decide which members can exchange data. The rule is containment, not overlap: two anchors may exchange data only if one of them carries every taint the other does. Try it in the playground.
| A carries | B carries | Exchange data? | Why |
|---|---|---|---|
{x} | {x} | yes | each set contains the other |
{x} | {x, y} | yes | A's set is contained in B's |
{x, y} | {x, z} | no | neither contains the other, despite sharing x |
{x} | {y} | no | neither contains the other |
| none | {x} | no | an anchor with none carries the default compartment, which {x} does not contain |
Everything else still works across a taint boundary: separated anchors still connect, bootstrap from each other and relay for each other. They just cannot exchange data.
- Three machines with three generated taints are three disconnected machines. A taint has to be shared deliberately; nothing propagates it. And since a credential's taints are fixed when it is issued, putting them together afterwards means drawing new identities for all but one.
- A hub with
{a}and spokes with{a,b}and{a,c}works as a hub. The hub reaches both spokes, and the spokes cannot reach each other. That is the arrangement containment exists to express. - Two machines with
{office,laptop}and{office,desktop}cannot talk, which surprises people. Give them the same set. - Pass
--taintonce per label. A comma-separated list is refused rather than read as one label. A label is 1 to 64 bytes of UTF-8 with no space of any kind, no character that does not print, and no@or+, which bind a forwarded network to compartments. A machine carries at most 32.
conflux status prints the set in conflux.json, which mirrors what the credential grants, or none: the realm's default compartment for an issued credential that grants none. A Taints order may have moved the running anchor since, and that does not show there. conflux peers has a DATA column that says yes or no for each peer. When two machines are up and cannot see each other, that column is the first thing to check.
Granted by the credential
An anchor does not claim its taints; its issuer grants them. The enrolment asks for a set, the credential that comes back commits to it, anchor starts the identity under no other, and a renewal restates it. So a machine's taints are fixed for the life of its identity, and on a machine that already holds a credential a later --taint may only repeat them.
- Changing them is a new identity, with a new AnchorID and a new overlay address:
sudo conflux uninstall --yes, thensudo conflux up --taint NEW. - The
taintsinconflux.jsonare checked against the credential at every start, and a set it does not grant is refused for good rather than retried. - A member whose credential grants the
taintpower can move a running machine to other compartments with a Taints order, which anchor keeps and reapplies at every start. An alpha credential grants that power to nobody.
The two modes
The mode is what this machine gets out of the realm. up and proxy are exclusive, because one daemon holds one anchor. The medium is what the anchor reaches the realm over, and either mode works over either medium.
conflux proxy keeps the overlay inside the daemon. Nothing appears on the host. The way in is the ports you publish, each connection handed to a backend you name, and if it routes, the way out is a subnet or an exit served from the anchor's own process.
The service stays on loopback, unchanged, and sees every connection arrive from there.
conflux up gives the host a real interface. Anything on the machine can use the overlay, and publishing a service is an ordinary bind.
A client is always a machine running up: a proxy machine has no interface to connect out from.
| conflux up | conflux proxy | |
|---|---|---|
| What the host gets | a TUN interface: ping, ssh, everything | nothing on the host |
| What the realm gets | this machine, at an overlay address | the named services, at overlay ports, and any subnet or exit it routes |
| Privilege to run | root / CAP_NET_ADMIN / Administrator | root, only to register the boot service |
| An IPv4 | yes, owned by the host's kernel | yes, held by the anchor's own stack |
| Needs wintun.dll on Windows | yes | no |
| Can forward a subnet | yes, --subnet: the host forwards | yes, --subnet: the anchor forwards from its own process |
| Can serve or use an internet exit | yes, --serve-exit, --use-exit | yes; --use-exit covers only the anchor's own traffic |
| Can run over an uplink | yes, --uplink | yes, --uplink |
| Boot service | yes | yes |
Switching modes keeps the identity, and every setting either mode can use: the subnets and both exits, the IPv4, the taints, the uplink, the peers, the port and low latency. Only switching to up clears something, the proxy specs, which only userspace serves. --no-subnet, --no-serve-exit and --no-use-exit are the way back from routing on either verb.
Reverse proxy
On a machine where CAP_NET_ADMIN is out of reach, skip the interface and publish only the ports that need to be reachable. A peer that connects to this machine's overlay port 8080 gets a fresh connection to 127.0.0.1:3000. Nothing appears on the host, and the anchor itself needs no privilege. sudo is only for registering the boot service.
$ sudo conflux proxy 8080=127.0.0.1:3000 53/udp=127.0.0.1:53
| Spec | Means |
|---|---|
8080=127.0.0.1:3000 | TCP on overlay port 8080 → 127.0.0.1:3000 |
53/udp=127.0.0.1:53 | UDP on overlay port 53 → 127.0.0.1:53 |
5432=[::1]:5432 | an IPv6 backend, bracketed |
80=nas.lan:8080 | a backend on another host the machine can reach, by name |
- The network defaults to
tcpand can betcporudp. The same port on both is two specs,53=…and53/udp=…; a port repeated on one network is refused. - The backend is
host:port, dialled fresh for each connection rather than resolved up front, so a name that does not resolve yet is fine, and so is a named port. It can be loopback or any host this machine can already reach. - The overlay port and the backend port are independent, and nothing is bound on the host, so
80=127.0.0.1:3000clashes with nothing. - With a private IPv4, peers reach these ports at it as well as at the IPv6 address.
A userspace router
A proxy can also forward for the realm. With --subnet or --serve-exit, every connection a peer sends through it ends inside the anchor's own process, which dials the destination from an ordinary socket of its own, TCP and UDP, so the far end sees this host's address, as it would behind a translating router. Nothing on the host is set up, and no privilege is needed for that either. With either flag the port specs may be left out, and a proxy given no port, no subnet and no exit is refused, since it would have nothing to offer.
$ sudo conflux proxy --subnet 192.168.1.0/24 --serve-exit
To change a running anchor without touching its configuration, use anchorctl's own proxy command. Its changes last until the anchor restarts, when conflux starts it again from the configured list.
$ sudo conflux anchorctl proxy # what it is serving $ sudo conflux anchorctl proxy -add 9000=127.0.0.1:9000 # one more, until restart $ sudo conflux anchorctl proxy -rm 9000 # and gone again
Publishing a service in TUN mode
On a machine running conflux up, the kernel owns the overlay addresses, so a service published on the overlay is an ordinary bind. There is no proxy to configure.
- Bind to
0.0.0.0or::, or to the overlay address itself so the service is reachable only over the overlay. A service bound to127.0.0.1is not reachable from peers. - A service that binds the overlay address at boot should start after conflux. Under systemd,
After=conflux.servicewaits until the anchor is up. - A peer arriving over IPv6 appears from its own derived address. A peer arriving over IPv4 appears from the
198.18.0.0/15range. Write allow-lists against the IPv6 address, which is bound to the peer's identity. - Broadcast and multicast do not cross the overlay, so discovery by mDNS or SSDP does not either. Connect to services by address.
- There are no names for peers, only addresses.
conflux peerslists every peer's, and an entry in/etc/hostsgives one a name of your own.
Subnets, exits and other settings
Forwarding a subnet
conflux up --subnet 192.168.1.0/24, or the same on conflux proxy, offers that network to the rest of the realm, so machines on it with no conflux at all become reachable. An entry is a private prefix with no host bits set, an interface name, which follows whatever private networks are on it, or '*', every private network on every interface, quoted so the shell leaves it alone. Private means RFC 1918, 100.64.0.0/10 and unique local IPv6; reaching the public internet is what an exit is for.
An entry can be bound to some of this machine's own taints, as SPEC@a+b. 192.168.1.0/24@office is offered only to peers whose taints also pass the containment rule against {office}, and eth1@office+lab binds an interface to {office, lab}. A binding names only taints this machine is in. A list holds at most 64 entries, each at most 255 bytes, with at most 16 distinct compartments bound across it.
$ sudo conflux up --subnet 192.168.1.0/24 --subnet eth1@office $ sudo conflux proxy --subnet '*'
The last list set wins. A member whose credential grants the subnets power can replace the list while the anchor runs, with a Subnets order, and anchor serves that list from every start, a reboot included, until an up or proxy with --subnet or --no-subnet sets it again. While it does, conflux status says whose it is:
forwarding 10.9.0.0/24@lab — set by an order from anchor… at 2026-10-06T09:00:00Z, in place of conflux.json's; --subnet or --no-subnet sets it again
Exits
$ sudo conflux up --serve-exit # be a way out to the public internet for the realm $ sudo conflux up --use-exit # send this machine's own internet over the overlay
The two are alternatives, both are off unless asked for, and both work on up and on proxy. On a proxy, --use-exit covers only the anchor's own traffic, since nothing else on the host sees the overlay. Conflux never takes either from an enrolment file: an anchor that became an internet exit because a document said so is the worst kind of surprise. --no-serve-exit and --no-use-exit are the way back.
Other settings
| Flag | What it does |
|---|---|
--low-latency | Carry frames as datagrams: no head-of-line blocking between flows, and a lost frame stays lost. For voice, video and games. --no-low-latency goes back to streams. |
--lan-discovery yes|no|auto | Probe the local network for members of the same tree. Turning it off is a privacy choice, not a connectivity one: it announces that an anchor is here. auto, the default, leaves it to enrolment. An explicit yes is refused beside --uplink. |
--port N | The UDP port to listen on, for a firewall rule or a port forward. Omit it and the system picks one; --no-port goes back to that. Refused beside --uplink, which binds no socket. |
--interface NAME | The interface name on Linux and Windows. macOS and the BSDs number their own. |
--peers HOST:PORT | Where to start looking for the realm; ANCHORID@host:port also names the anchor expected there. Enrolment supplies this; name one only to override it. --no-peers forgets the override. |
--no-subnet | Forward nothing for the realm: the way back from --subnet, and from a member's Subnets order. |
--no-uplink | Go back to the host's network on a machine configured for a link. |
--api URL | The enrolment API base. The default is https://api.veilnet.com.au. |
Uplink — join over a cable
An anchor normally binds a UDP socket for its transport, so it needs a host IP network underneath. An uplink replaces that: a serial line becomes the medium, and no socket is bound at all.
$ sudo conflux up --uplink /dev/ttyUSB0:115200 --taint brhk-2mq9-tzva-6pjs-k4xe-nw7d-qf --no-ipv4 anchor anchor6btpa3gn6w4stipba4hekzho7caw6srfyy5puvbz7mfanaiept5a overlay fd80:c4b9:99ae:4411:c531:fd4f:754f:f08a/48 uplink /dev/ttyUSB0:115200 — no socket bound, no address advertised interface anchor0 taint brhk-2mq9-tzva-6pjs-k4xe-nw7d-qf
Uplinks are Unix only, and conflux refuses --uplink on Windows. One link carries one peer. Conflux watches the device: a link that disappears, or carries nothing for ninety seconds, is reopened for you.
A link binds no socket and has no host network to probe, so --port and an explicit --lan-discovery yes are refused beside it, naming both flags. So is fd:N, the form that adopts a descriptor: anchor takes it, but conflux's supervisor starts anchord with none to adopt. Name the device instead.
| Line speed | Handshake | |
|---|---|---|
| 115200 baud | about 2 s | comfortable |
| 57600 baud | about 4 s | fine |
| 19200 baud | about 12 s | usable; conflux warns below this |
| 9600 baud | about 25 s | marginal against a 60-second idle timeout |
| LoRa and other duty-cycled radios | hours | no |
The limit comes from the identity model, not the link: post-quantum certificates in both directions are what make a handshake that size.
Configuration
Conflux keeps one configuration per machine, root-owned, with no home directory anywhere in it. Every directory is 0700 and every file 0600.
| Configuration | State, identity and binaries | Socket and token | |
|---|---|---|---|
| Linux | /etc/conflux/ | /var/lib/conflux/ | /run/conflux/ |
| macOS | /Library/Application Support/conflux/ | /Library/Application Support/conflux/ | /var/run/conflux/ |
| Windows | %ProgramData%\conflux\ | %ProgramData%\conflux\ | %ProgramData%\conflux\run\ |
| FreeBSD, OpenBSD | /usr/local/etc/conflux/ | /var/db/conflux/ | /var/run/conflux/ |
| File | What it holds |
|---|---|
conflux.json | What you asked for, in the configuration directory. The rest are in the state directory. |
manifest.b64 | The identity: the seed, the credential chain, the taints it grants and the bootstrap list, in one line as the issuer wrote it; a renewal rewrites the chain and the list inside it. Recoverable from nowhere. |
state.json | What conflux worked out: the AnchorID, the credential's window, the last clock skew and the uplink reopen tally. Delete it and the next start learns it again. |
anchord.json | The daemon's own configuration, rendered from conflux.json on every start. Editing it does nothing lasting. |
bin/ | The extracted anchor pair, one directory per build, and wintun.dll on Windows. |
conflux | The copy of conflux the boot service runs (conflux.exe on Windows). |
conflux.json is what you asked for, and the whole of what a reboot needs. Every up and proxy rewrites it.
{
"version": 1,
"mode": "proxy",
"taints": ["brhk-2mq9-tzva-6pjs-k4xe-nw7d-qf"],
"ipv4": "10.128.0.5/24",
"proxies": ["5432=127.0.0.1:5432", "80=127.0.0.1:3000"],
"lowLatency": false
}Editing it by hand is supported. sudo conflux start restarts from whatever it says, and anything anchor would refuse on the file's face is refused by conflux first, naming the field, and not retried. What depends on the host is anchor's to find at start: a subnet whose interface is not up yet, which is retried, and TLS material under export that will not load. An export block names where telemetry goes; without one, the machine exports nothing.
taints is the one field an edit cannot change once the machine has enrolled. It mirrors what the credential grants, and a set the credential does not grant is refused at every start.
Commands
Conflux keeps a small, fixed set of command names for itself. Everything else is passed to anchorctl unchanged: peers, routes, events, metrics, send, inspect and the rest, and the realm's controls over its members, block, unblock, blocks, taints, subnets and telemetry. Run them with sudo: the commands that change the machine need root, and the rest talk to the running anchor through a token only root can read.
| Command | What it does |
|---|---|
conflux up | enrol if needed, start in TUN mode, register the boot service |
conflux proxy | enrol if needed, start in userspace mode serving those backends |
conflux enrol | install a credential minted for this machine on a network of your own, instead of drawing one; --manifest - reads it from stdin. It refuses to replace a manifest already there |
conflux down | stop the anchor now; the boot service and configuration stay |
conflux start | start it again now, from the configuration already on disk, after copying this conflux to where the service runs it from |
conflux renew | fetch a fresh credential and install it on the running anchor, without a restart; exits 69 on a fixed-term credential, which has nowhere to renew |
conflux status | conflux's state, with anchorctl status beneath it; exits 78 with no configuration |
conflux install | register the boot service, and start it if a configuration exists |
conflux uninstall | remove the boot service, the configuration and the identity; --yes skips the question |
conflux version | conflux's version and commit, and the anchor build it carries |
conflux help [COMMAND] | conflux's usage with anchorctl's beneath it, or one command's own usage |
conflux anchorctl … | run the embedded anchorctl, uninterpreted |
Four names exist on both sides: start, proxy, renew and status. Each collision is resolved rather than guessed. Bare conflux status is conflux's and prints anchorctl's beneath it, while conflux status -watch 5s goes to anchorctl because it was given arguments. stop and restart are refused, with a pointer to down and start. Passing them through would leave conflux's configuration describing an anchor that is not the one running.
Realm controls
Six of the passed-through commands are the realm's control over its members. None of them is a conflux verb, and every one but blocks acts on another member of the same realm, never on this machine.
| Command | What it does | Needs the power |
|---|---|---|
block -target ID [-days N] [-reason TEXT] | shuts another member out of the realm, on every member, until the block ends or is lifted; without -days it lasts until a member unblocks it | block |
unblock -target ID | lifts a block, whoever issued it | block |
blocks | lists the blocks in force | none |
taints -peer ID [-set A,B] | reads another member's compartments, or moves it to others | taint |
subnets -peer ID [-set A,B | -clear] | reads what another member forwards for the realm, or replaces its list | subnets |
telemetry -from ID | asks another member how it is | telemetry |
- Each needs its power in this machine's credential, granted by its issuer as taints are:
anchorctl issue -caps block, and so on. An alpha credential grants none, so on the public network these orders are refused with anchor's explanation. On a network of your own the root node holds them, and they reach the machines in its own realm. - They take the full 58-character AnchorID,
anchor…, asconflux statusprints it, not the ten-character short form in the tablespeersandblocksprint. For every private network on a member, writesubnets -set '*'; anchorctl's help saysall, which anchor does not accept. - A block does not stop anything. The blocked anchor keeps running, an outsider to its realm until the block ends or is lifted, and shows
DATA noon every peer. To stop the anchor on this machine, useconflux down. Why the power needs guarding is covered under security.
Exit codes
| Code | Meaning |
|---|---|
| 0 | success |
| 1 | failure |
| 2 | usage |
| 13 | needs root or Administrator |
| 69 | nothing is running here, or nothing to act on: down and start where conflux is not installed, renew with no manifest or on a fixed-term credential |
| 70 | a configuration conflux or anchor refuses for good; the systemd unit does not restart on it |
| 78 | no configuration; the systemd unit does not restart on that either |
| other | for a passed-through command, anchorctl's own code, verbatim |
Environment
| Variable | Effect |
|---|---|
CONFLUX_DIR | roots a whole separate installation in one directory, configuration, identity, socket and a boot service named after the root, so it cannot replace the machine's own |
CONFLUX_DEBUG=1 | passes -v to anchord |
CONFLUX_ALLOW_INSECURE_API=1 | permits a plain-http API base, for tests only |
ANCHOR_SOCKET, ANCHORD_TOKEN | if already set, conflux leaves them alone and a passed-through command uses yours |
Credentials and renewal
On VeilNet's public network, enrolment is one unauthenticated call that needs no account: POST /ghosts/alpha with the taints to put the anchor in, {"taints": ["brhk-2mq9-tzva-6pjs-k4xe-nw7d-qf"]}, at most 32 of them and never none. The API mints an identity inline, signs a credential for it in those taints, returns both, and forgets them. There is no sign-up, no session and no record. The free tier is free of us, not merely free of charge.
A credential lasts thirty days, and conflux renews it at two thirds of the window, day twenty. That leaves ten days of retry budget for a machine having a bad week, and a failed renewal is retried every minute. Renewal runs on every start, before the anchor is built, and on a timer while it runs. It is hot: the running anchor takes the new credential without dropping a session. The request restates the taints the manifest names, which a renewal cannot change, and the answer's bootstrap list, when it carries a usable one, replaces the stored one for the next start.
A machine that was switched off past its expiry renews on its next start and keeps its AnchorID and its overlay address. There is no window to miss.
- A failed renewal never falls back to enrolling. That fallback would silently change the machine's overlay address and orphan every peer that knew it.
- Re-running conflux up does not enrol; it reads the identity that is already there.
- Switching modes does not enrol: up after proxy keeps the identity.
On a network of your own
A private sovereign network works the other way round. There is no enrolment route. Each machine's credential is minted in advance by the network's root node, its operator is handed one file, and conflux enrol installs it. conflux up then starts from that file without enrolling.
$ sudo conflux enrol --manifest gateway-3.b64 $ sudo conflux up
Where the network runs a guardian, the machine renews itself through it, authenticating with a secret minted for that one machine. Without one, the credential the root node mints with its CLI carries no renewal fields at all, so it is fixed-term: it runs to its expiry, and a replacement is minted and installed before then. A credential that does not renew says what that means for the machine.
Revocation exists: the machine's credential can go unrenewed and lapse. There is also a block order, which needs the block power, shuts the machine out now and can be lifted. Whoever mints the credential also chooses how long it lasts and the taints it grants, and may allocate the machine's IPv4. Enrol copies the IPv4 and any export block into the configuration once, where they can be changed like any other setting. The taints are not copied to be edited: they are the credential's, and every start checks conflux.json against them. See commissioning a node.
A credential that does not renew
A document with no renewal fields, no renewalUrl and no renewalAuth, is fixed-term rather than broken. It runs to its expiry and is then replaced, not renewed.
$ sudo conflux enrol --manifest gateway-3.b64 credential installed from gateway-3.b64 renewal none: it runs to its expiry and is then replaced expires 2026-11-07 09:00 UTC taint site-a bootstrap genesis-1.example.net:4700, genesis-2.example.net:4700 Next: sudo conflux up
The root node's CLI mints for thirty days unless told otherwise. The replacement is installed before the last day: past it, the next start is refused for good.
conflux enrolinstalls it with no--api, since there is no renewal URL to read a base from.- Nothing asks to renew it. A start makes no call, the timer stops rather than retrying every minute, and
conflux renewexits 69. None of this is reported as a failing renewal, because nothing failed. - An expired one is refused at enrol. Past its expiry on a machine already running, the next start is refused for good and the unit shows as failed until it is replaced.
enrolrefuses to replace an existing manifest, so a replacement goes in as a fresh install, with whatever identity the new file carries:sudo conflux uninstall --yes, thensudo conflux enrolwith the new file.exit: truein the document is reported, not obeyed. The machine serves an exit only afterconflux up --serve-exit, or the same onproxy.- A document that names a
renewalAuthbut norenewalUrlis not fixed-term. It says how to authenticate a renewal and not where to send one, so it is refused at enrol as a mistake by its issuer.
Security
Conflux adds exactly two things the protocol does not have: an executable written to disk, and a private key in a file. The protocol's own guarantees are in the anchor documentation.
Protect the manifest
The manifest holds the 32-byte identity seed, and anyone who has it is that anchor. One issued through a guardian also holds the machine's renewal secret, so whoever has it can keep that identity's credential current as well. Treat it as a private key: keep it off shared storage, out of backups that could leave the machine, and at exactly the permissions conflux sets.
How the file is held
0600, with the mode set on the descriptor before any content is written. Writing first and chmod'ing after leaves a window at the umask's mode.0700on the directory. A0600file in a traversable directory is one rename away from being replaced.- On Windows, where mode bits mean nothing, conflux gives
%ProgramData%\confluxto SYSTEM and Administrators, with a protected DACL that everything beneath it inherits. It refuses a directory another account created. - Never in an argv. It travels on stdin, so it never appears in ps or /proc/*/cmdline. Never in a log either, because the types that carry it redact themselves.
What the boot service runs
The service runs as root, or SYSTEM on Windows, and starts whatever file it names at every boot. So it names the copy of conflux in the state directory, which only root can change and which up, proxy, install and start refresh, and never the file conflux was run from: a home directory or a download is a place another account could swap in a program of its own, to be run as root at the next start.
The commands that reach past this machine
Everything else conflux does stops at the machine it is typed on. Five passed-through orders do not: block, unblock, taints, subnets and telemetry act on another member of the realm, each gated by its own power in this machine's credential. blocks only reads. A credential granting block is therefore not one more copy of a machine's identity. It is the authority to shut any anchor of its realm out of it. Keep it somewhere that reflects the difference, issue it to as few machines as the work allows, and issue it short: its lifetime is the whole of how long a stolen one can act.
- A block is not a revocation. It shuts a machine out on every member it reaches and leaves its credential untouched; removing a machine for good is still expiry. On the public network there is no revocation of either kind: an alpha credential grants no
block, and renewal there is open to whoever holds the seed, so a leaked manifest is a permanent loss of that identity. How expiry and blocks work together. - It can be lifted. Any holder of
blockmay unblock, whoever issued the block, and a block given-days Nends on every member at the instant it says. A mistyped AnchorID is oneunblockaway from being undone. Read the target back before running it all the same. - It spreads member to member, in a handful of hand-overs, and every member holds it and hands it to members that were away. The realm converges on it rather than switching at an instant, and a member it has not reached yet still admits the machine.
- It is unrelated to
conflux down, which stops the anchor on this machine and leaves the registration in place. A blocked anchor is not stopped at all: it goes on running, outside its realm.
What conflux does not defend against
- A root-equivalent local attacker. They can read the manifest, and that is the end of it.
- A compromised release channel, beyond what the digests printed by conflux version and the release attestation provide.
- Traffic analysis, which belongs to the protocol and is covered in its own documentation.
- Anything a taint appears to promise beyond its issuing. A taint is granted by the credential, so it is an authorisation boundary as strong as the issuing behind it. And a member whose credential grants the
taintpower can move a machine to other compartments while it runs.
Troubleshooting
Start with sudo conflux status. It prints conflux's own state and anchorctl's beneath it, and most of what follows is a way of reading that output. Conflux decides the mode, the taints, the IPv4, the subnets, the proxies, the exits, the medium, where the files live and when to renew. The anchor underneath decides everything else: every packet, every route, every credential check. Knowing which of the two is in play usually tells whether a problem is a setting or a network condition.
| Symptom | Where to look |
|---|---|
| Two machines are up and cannot reach each other | Give it a minute; peers take 15 to 60 seconds to find each other. Then the DATA column in conflux peers: it is almost always a taint set that is not contained. DATA no on every peer can also mean this machine is blocked; conflux blocks on another member lists the blocks in force. |
| conflux status says the credential is EXPIRED | Renewal is failing, and the next line names the error. Renewal needs outbound HTTPS to the API. A restarted anchor does not start on an expired credential, so the supervisor keeps retrying, renewing first each time, and once a renewal gets through the machine keeps its address. A fixed-term credential that has expired is replaced, not renewed. |
| The service started but no anchor came up within 90s | Stop the service and run the supervisor in the foreground with sudo conflux serve --foreground. anchord's own lines are prefixed anchord:, so its reason for a refusal is printed in its own words. |
| conflux refuses to start, naming the clock | The clock is more than ten minutes from the API's. Fix NTP first; nothing connects until then. |
| Anything else | The logs: journalctl -u conflux -n 50 on Linux, /var/log/conflux.log on macOS and FreeBSD, /var/log/daemon on OpenBSD, and %ProgramData%\conflux\logs\conflux.log on Windows. |
Anything those pages do not answer is worth an email. We would rather hear it during a pre-release than after: sovereign@veilnet.net.