04 — Confluxconflux 1.0.0-pre

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 guardian of your own it installs the file the guardian issued. 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

PlatformWhat else is needed
LinuxNothing. 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.
macOSApple 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.
WindowsRun PowerShell as Administrator. TUN mode also needs wintun.dll, which conflux downloads whenever a TUN machine needs it and checks against a pinned digest. If the download fails, it prints where to put the file by hand. conflux proxy needs no driver.
FreeBSD, OpenBSDEverything 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.

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.

1 — the first machine
$ 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-10-06T06:35:43Z (29d 23h)
  service      active (systemd: conflux.service, enabled at boot)
2 — every other machine
$ 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.

3 — check that it worked
$ 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 guardian 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.

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. Try it in the playground.

Containment, not overlap
A carriesB carriesExchange data?Why
{x}{x}yeseach set contains the other
{x}{x, y}yesA's set is contained in B's
{x, y}{x, z}noneither contains the other, despite sharing x
{x}{y}noneither contains the other
none{x}noan 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 --taint once per label. A comma-separated list is refused rather than read as one label. A label is at most 64 bytes, with no spaces, and a machine carries at most 32.

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.

Fig. 01 — Publishing a service, two ways01 / 02
CLIENTlaptopCONFLUX UPanchor0OVERLAYSERVER · CONFLUX PROXYdb-1ANCHOR · NO INTERFACE[fd80:…]:543210.128.0.5:5432SERVICEpostgres · 127.0.0.1:5432NO HOST SOCKET · SEES 127.0.0.1

conflux proxy keeps the overlay inside the daemon. Nothing appears on the host, and the only way in is the ports you publish: each connection is handed to a backend you name.

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 upconflux proxy
What the host getsa TUN interface: ping, ssh, everythingnothing on the host
What the realm getsthis machine, at an overlay addressthe named services, at overlay ports
Privilege to runroot / CAP_NET_ADMIN / Administratorroot, only to register the boot service
An IPv4yes, owned by the host's kernelyes, held by the anchor's own stack
Needs wintun.dll on Windowsyesno
Can forward a subnetyes, --subnetno
Can serve or use an internet exityes, --serve-exit, --use-exitno
Can run over an uplinkyes, --uplinkyes, --uplink
Boot serviceyesyes

Switching modes keeps the identity, and every setting that does not depend on the mode: the IPv4, the taints, the uplink, the peers, the port and low latency. 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
Each spec is OVERLAYPORT[/NETWORK]=BACKEND
SpecMeans
8080=127.0.0.1:3000TCP on overlay port 8080 → 127.0.0.1:3000
53/udp=127.0.0.1:53UDP on overlay port 53 → 127.0.0.1:53
5432=[::1]:5432an IPv6 backend, bracketed
80=nas.lan:8080a backend on another host the machine can reach, by name
  • The network defaults to tcp and can be tcp or udp. The same port on both is two specs, 53=… and 53/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:3000 clashes with nothing.
  • With a private IPv4, peers reach these ports at it as well as at the IPv6 address.

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.0 or ::, or to the overlay address itself so the service is reachable only over the overlay. A service bound to 127.0.0.1 is not reachable from peers.
  • A service that binds the overlay address at boot should start after conflux. Under systemd, After=conflux.service waits 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/15 range. 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 peers lists every peer's, and an entry in /etc/hosts gives one a name of your own.

Subnets, exits and other settings

Forwarding a subnet

conflux up --subnet 192.168.1.0/24 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 or an interface name, and an interface name follows whatever networks are on it.

$ sudo conflux up --subnet 192.168.1.0/24 --subnet eth1

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 are up's alone. 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

FlagWhat it does
--low-latencyCarry frames as datagrams: no head-of-line blocking between flows, and a lost frame stays lost. For voice, video and games.
--lan-discovery yes|no|autoProbe 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.
--port NThe UDP port to listen on, for a firewall rule or a port forward. Omit it and the system picks one.
--interface NAMEThe interface name on Linux and Windows. macOS and the BSDs number their own.
--peers HOST:PORTWhere to start looking for the realm. Enrolment supplies this; name one only to override it.

Configuration

Conflux keeps one configuration per machine, root-owned, with no home directory anywhere in it. Every directory is 0700 and every file 0600.

ConfigurationState, identity and binaries
Linux/etc/conflux//var/lib/conflux/
macOS/Library/Application Support/conflux//Library/Application Support/conflux/
Windows%ProgramData%\conflux\%ProgramData%\conflux\
FreeBSD, OpenBSD/usr/local/etc/conflux//var/db/conflux/

conflux.json is what you asked for, and the whole of what a reboot needs. Every up and proxy rewrites it.

conflux.json
{
  "version": 1,
  "mode": "proxy",
  "taints": ["brhk-2mq9-tzva-6pjs"],
  "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 a field anchor would refuse is refused by conflux first, naming the field. An export block names where telemetry goes; without one, the machine exports nothing.

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

CommandWhat it does
conflux upenrol if needed, start in TUN mode, register the boot service
conflux proxyenrol if needed, start in userspace mode serving those backends
conflux enrolinstall a credential a guardian issued, instead of drawing one; --manifest - reads it from stdin
conflux downstop the anchor now; the boot service and configuration stay
conflux startstart it again now, from the configuration already on disk
conflux renewfetch a fresh credential and install it on the running anchor, without a restart
conflux statusconflux's state, with anchorctl status beneath it
conflux installregister the boot service, and start it if a configuration exists
conflux uninstallremove the boot service, the configuration and the identity; --yes skips the question
conflux versionconflux'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.

Exit codes

CodeMeaning
0success
1failure
2usage
13needs root or Administrator
69nothing is running here
70the anchor itself would not start
78no configuration
otherfor a passed-through command, anchorctl's own code, verbatim

Credentials and renewal

On VeilNet's public network, enrolment is one unauthenticated call 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 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.

Fig. 02 — Thirty days, renewed at day twenty
RETRY BUDGET · 10 DAYSDAY 0DAY 10DAY 20DAY 30RENEWED · NO SESSION DROPSISSUEDWOULD LAPSEEACH RENEWAL STARTS A FRESH THIRTY DAYS

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 guardian of your own

A guardian 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 gateway-3.b64
$ sudo conflux up

Renewal authenticates with a secret minted for that one machine, and revocation exists: the guardian can refuse to renew, and its operator can issue a kill order for immediate removal. 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. See commissioning a node.

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 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.
  • 0700 on the directory. A 0600 file in a traversable directory is one rename away from being replaced.
  • On Windows, where mode bits mean nothing, conflux gives %ProgramData%\conflux to 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 half of revocation. Revocation is expiry and kill orders: a kill order removes an anchor within minutes, for N days, and expiry removes it for good once its credential is no longer renewed. On the public network only the first half exists, because renewal there is open to whoever holds the seed: a leaked manifest is a permanent loss of that identity. How the two work together.
  • It cannot be called back. -days N is 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 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

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.

SymptomWhere to look
Two machines are up and cannot reach each otherGive 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.
A peer is connected, has an RTT, and pings time outThe host's routing table. Nothing is sending overlay addresses to the overlay interface.
A machine came back at a different addressThe identity was replaced. A second up never re-enrols, so something removed the state directory.
conflux status says the credential is EXPIREDRenewal 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 clockThe clock is more than ten minutes from the API's. Fix NTP first; nothing connects until then.
… needs rootRun the same command with sudo, or from an elevated PowerShell.
up stops in a script, asking for an IPv4There is no terminal to ask at. Pass --ipv4 ADDRESS or --no-ipv4.
conflux status says the binaries are staleConflux was upgraded. conflux start restarts onto the new build.
Nothing appears on the hostThat is proxy mode. up is the one that creates an interface.

Where the logs are

PlatformLogs
Linuxjournalctl -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.