Networking & subnets
Zero-config, no-central-server connectivity between your machines. Join two
nodes into a subnet once with a six-digit code; from then on, the same
spt send sergey works whether sergey is local or three networks away.
The model
- Node identity — each machine holds an Ed25519 keypair; the public key
is its network identity. Connections are mutually authenticated QUIC,
end-to-end encrypted, peer-to-peer with NAT hole-punching and public-relay
fallback (you can self-host the relay, or disable it for LAN/air-gapped
use — the default relays carry only encrypted traffic they cannot read).
Nodes also carry a human label (the hostname by default): views render
HFENDULEAM (bcead52b…), and@nodequalifiers accept the label or a key prefix — several machines sharing a label are never guessed between. - Subnets — machines join into named groups. A subnet shares: the endpoint registry (who exists, where, what state), context sync for its endpoints, notifications, and staged self-updates. Nothing is shared with nodes outside the subnet, ever.
- Joining — a one-time, code-authenticated ceremony. On a member
machine,
spt subnet show-codeprints the current six digits (and anotpauth://URI — put the seed in your authenticator app); on the new machine,spt subnet join <name>finds a member over LAN + relay, then prompts for the code and runs the exchange. Finding a member happens before you enter the code, so the code you type is always fresh at the moment of pairing — a slow search never causes a just-read code to be rejected, and re-entering a code after a typo retries the pairing only (it does not restart the search). The search shows elapsed time while it runs, and on failure reports why (add--verbosefor a full diagnostic dump);--code <digits>skips the prompt for non-interactive use. The code bootstraps a PAKE key exchange — the code is never the key, and a wrong guess learns nothing. Both sides pin each other’s node keys on success (trust-on-first-use; key changes warn and never auto-apply). Every member machine answers join attempts automatically — no arming step on the existing fleet. (two-phase join since v0.17.0) - Elevation gates —
subnet create(reveals a fresh subnet’s joining secret) andsubnet join(enrolls the whole machine) require an elevated terminal;subnet statusis read-only and ungated, and never prints secrets. - Visibility & sync scope — per endpoint, per subnet: an endpoint can be hidden from a subnet (neither advertised nor routable) and its mind syncs only to subnets on its membership list. Both default conservative; unconfigured means not shared.
- Anchor subnet — an endpoint is anchored to exactly one subnet when it
is created, and that anchor is permanent (it sets where the endpoint’s
identity lives and its default sync scope). On a node in a single subnet the
anchor is chosen automatically; on a node in more than one subnet,
spt endpoint runrequires--subnet <name>— interactively it proposes a most-recently-used default and asks you to confirm, and non-interactively it refuses with the subnet list rather than guessing. (since v0.14.0) - Resource registry — endpoints may advertise a free-text service blurb
(
spt endpoint description setto author;spt endpoint list --detailto browse) — an agent yellow-pages over visible rows only.
The walkthrough
# Machine 1 (elevated): mint the subnet — prints the code, an otpauth://
# URI, and a terminal QR.
spt subnet create home
# Machine 2 (elevated): join it — searches LAN + relay, prompts for the code.
spt subnet join home
# Either side: who's in, and who's online.
spt subnet status --nodes
The quickstart’s pairing section runs this same flow inside the two-agent demo.
Two keys, and what each one is for
subnet create shows you two secrets, minutes apart, and each screen leads
with the label of the key it is showing — so you always know which one you
are scanning.
--- ADMIN KEY (create) --- comes first, and it is shown once, here, and
never again. It is what reaches any spt endpoint engine-room linked to the
subnet, so you can tell that engine room to update the node’s security mode,
manage the access rules for the node and its endpoints, and grant other
agents the rules they need. It also doubles as a member key — you can
join new nodes with it. You prove you captured it by typing the current code
back before anything is written.
--- MEMBER KEY --- comes last. It joins new nodes to the subnet from
anywhere, it is the one to share with people whose nodes need to join, and
unlike the admin key you can see it again at any time with spt subnet show-code [<name>] — or by scanning the QR on that screen into your
authenticator app, which lets you pair again later without the command.
spt subnet show-code prints the member key alone and carries no header —
there is only one key on that screen, and no admin material is ever shown by
it.
Troubleshooting a join
A join searches for a member over every IP family your machine can actually reach. At startup the daemon probes IPv4 and IPv6 once and uses only the families that work — so a network that resolves IPv6 addresses but cannot reach them (a common half-broken setup) no longer silently consumes the whole search window. (since v0.17.0)
- See what happened.
spt subnet join <name> --verboseprints, on failure, which IP families were usable, the time window it searched, how many attempts it made, and the last concrete error — enough to tell a dead subnet from a wrong code from a network problem. - Force an IP family off. Set
SPT_DISABLE_IPV6=1(orSPT_DISABLE_IPV4=1) to make the daemon skip that family regardless of the probe — a deterministic override for a misbehaving network. Setting both is an error. The probe is automatic; reach for these only to pin behaviour. - Quick discriminator. If a join hangs only over the wider internet, check whether IPv6 reaches the relay: a working IPv4 path with a dead IPv6 one is the classic case the per-family probe handles for you.
What rides it
Cross-machine send/ring, registry replication, two-tier mind sync,
remote attach, remote suspend/wake, file transfer, notification replication,
and peer-propagated self-update — all over the same subnet substrate.
Commands
spt subnet (status · create · join · show-code · notify ·
attach/detach · leave · prune) · spt endpoint list --detail ·
spt endpoint description · the qualified addressing forms
([subnet:]id[@node], where @node is a label or key prefix) —
CLI reference.