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. It enrols the machine in VeilNet's public realm, starts an anchor and registers a boot service. Joining takes one command, and coming back after a reboot takes none.
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. 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 downloads and verifies whenever a TUN machine needs it. conflux proxy needs no driver. |
| FreeBSD, OpenBSD | Everything works as on Linux. The boot service is an rc script: enabled with sysrc and run under daemon(8) on FreeBSD, enabled with rcctl on OpenBSD. |
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 restarts the service onto it. Nothing is re-enrolled and the identity is untouched. Until the restart, conflux status reports the binaries as stale.
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
Share it. Any machine that runs
conflux up --taint brhk-2mq9-tzva-6pjs
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
credential valid until 2026-09-13T06:35:43Z (6d 23h)
service active (systemd: conflux.service, enabled at boot)$ sudo conflux up --taint brhk-2mq9-tzva-6pjs --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 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 $ conflux status $ conflux peers
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.
Concepts
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 TLS handshake, before any data flows. Membership is a signature, not a list on a server.
A realm can delegate: one root hands a realm to another key, which can hand one on again. That is how an organisation running its own guardian holds its own root and admits its own machines. Data never leaves the realm it belongs to. An anchor in a realm above can carry that traffic, but cannot read it or send any into it.
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.
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 alone decide which machines 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.
| 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.
- 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.
conflux status prints the taint, and 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.
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 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 |
| 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 | no |
| Can serve or use an internet exit | yes, --serve-exit, --use-exit | no |
| Can run over an uplink | yes, --uplink | yes, --uplink |
| Boot service | yes | yes |
Switching modes keeps the identity, and every setting that does not depend on the mode: the IPv4, the taints, the uplink, the peers and the port. Moving to proxy clears the subnets and both exits. Moving to up clears the proxy specs.
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 |
The network defaults to tcp and can be tcp or udp. A duplicate overlay port 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. With an IPv4, peers reach these ports at it as well as at the IPv6 address.
To add or remove a proxy on an anchor that is already running, use anchorctl's own proxy command. It is a different thing from conflux proxy, which decides the mode the machine boots into.
$ conflux anchorctl proxy # what it is serving $ conflux anchorctl proxy -add 9000=127.0.0.1:9000 # one more, now
Uplink — join over a cable
An anchor normally binds a UDP socket for its QUIC transport, so it needs a host IP network underneath. An uplink replaces that: a file descriptor becomes the medium, and no socket is bound at all.
$ sudo conflux up --uplink /dev/ttyUSB0:115200 --taint brhk-2mq9-tzva-6pjs --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
Uplinks are Unix only. anchor has no way to open a link on Windows, so conflux refuses --uplink there.
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, kill and the rest.
| 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 a self-hosted guardian issued, instead of drawing one |
conflux down | stop the anchor now; the boot service and configuration stay |
conflux start | start it again now, from the configuration already on disk |
conflux renew | fetch a fresh credential and install it on the running anchor, without a restart |
conflux status | conflux's state, with anchorctl status beneath it |
conflux install | register the boot service, and start it if a configuration exists |
conflux uninstall | remove the boot service, the configuration and the identity |
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.
kill is not what the word suggests
It is one of the passed-through names, and it has nothing to do with conflux down. conflux kill -target ANCHORID -days N stops nothing on this machine. It makes another anchor a target across the whole realm for N days, and it needs a credential issued with -admin. To stop the anchor here, use conflux down. Why that authority needs guarding is covered under security.
Credentials and renewal
On VeilNet's public realm, enrolment is one unauthenticated POST that takes no body and needs no account. The API mints an identity inline, signs a credential for it, 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 seven days, and conflux renews it at two thirds of the window, day 4.67. That leaves fifty-six hours of retry budget for a machine having a bad week. 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.
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 self-hosted guardian
A guardian is an organisation's own control plane, and it works the other way round. It serves no enrolment route. Each machine is commissioned in advance, its operator is handed one file, and conflux enrol installs it. conflux up then starts from that file without enrolling.
$ sudo conflux enrol --manifest node-14.b64 $ sudo conflux up
Renewal authenticates with a secret minted for that one machine, and revocation exists, because the guardian can refuse to renew. The guardian also chooses how long a credential lasts, and can allocate the machine's IPv4 and compartment. Enrol copies those into the configuration once, where they can be changed like any other setting.
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 threat model is documented separately.
Protect the manifest
The manifest holds the 32-byte identity seed, and anyone who has it is that anchor. One issued by 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.
The one command that reaches past this machine
Everything else conflux does stops at the machine it is typed on. conflux kill does not: -target ANCHORID -days N makes another anchor a target across the whole realm for N days. It needs a credential issued with -admin, so an admin credential is not one more copy of a machine's identity. It is the authority to do that to any anchor in the realm. Keep it somewhere that reflects the difference, and issue it to as few machines as the work allows.
- It is not revocation, and on the public realm nothing is. A leaked manifest is a permanent loss of that identity: the holder keeps the seed and can go on renewing a credential for it.
killchanges what the realm does about the anchor, not who holds it. - It cannot be called back.
-days Nis chosen once and then waited out, so a mistyped AnchorID takes out a machine that did nothing, for the full N days. Read the target back before running it. - It spreads the way gossip does, peer by peer, so the realm converges on it rather than switching at an instant. A partitioned peer learns late.
- It is unrelated to
conflux down, which stops the anchor on this machine and leaves the registration in place. The two share a connotation and nothing else.
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 security documentation.
- Anything a taint appears to promise. Taints limit the blast radius inside a realm whose members are all trusted as members. They are not a tenant boundary.
Troubleshooting
Conflux decides the mode, the taints, the IPv4, the subnets, the proxies, where the files live and when to renew. That is the whole list, and it is what the configuration file holds. The anchor underneath decides everything else: every packet, every route, every credential check, every peer. 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 | the DATA column in conflux peers. It is almost always a taint set that is not contained. |
| A machine came back at a different address | the identity was replaced. A second up never re-enrols, so something removed the state directory. |
| conflux status says the credential is EXPIRED | renewal is failing, and the next line names the error. Renewal needs outbound HTTPS to the API; once it succeeds, the machine keeps its address. |
| conflux refuses to start, naming the clock | the clock is more than an hour from the API's. Fix NTP first; nothing connects until then. |
| up stops in a script, asking for an IPv4 | there is no terminal to ask at. Pass --ipv4 ADDRESS or --no-ipv4. |
| conflux status says the binaries are stale | conflux was upgraded. conflux start restarts onto the new build. |
| Nothing appears on the host | that is proxy mode. up is the one that creates an interface. |
Where the logs are
| Platform | Logs |
|---|---|
| Linux | journalctl -u conflux -n 50 |
| macOS, FreeBSD | /var/log/conflux.log |
| OpenBSD | /var/log/daemon |
| Windows | %ProgramData%\conflux\logs\conflux.log |
When the service starts and the anchor does not, 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.
Anything this page does not answer is worth an email. We would rather hear it during a pre-release than after.
sovereign@veilnet.com.au