Architecture decision records

These record decisions that are expensive to reverse, together with what we gave up to make them. If you are new to the codebase, read them in order — they explain more about meshp than the code does.

A decision here is not permanent. It is recorded, so that changing it is a deliberate act with a written reason rather than a drift nobody noticed.

ADR Decision
0001 Route groups, not exit pools
0002 Relay-first, with opportunistic direct upgrade
0003 Desired state carries candidate lists, not single assignments
0004 A device may hold memberships in several networks at once
0005 IPv6 ULA primary, configurable IPv4, never a hardcoded 100.64/10
0006 Device identity key is separate from rotatable WireGuard keys
0007 ACLs are enforced at the destination, in the agent
0008 Desired state is delivered as incremental, lazily scoped deltas
0009 Apache 2.0 core; the commercial layer is API-separated, not a fork
0010 Mobile clients wrap the official WireGuard tunnel libraries and are relay-only
0011 Full-tunnel devices fail closed
0012 Presence and pub/sub sit behind an interface with in-memory and Redis implementations
0013 Generated protobuf and database code is committed
0014 A hand-written migration runner that checksums what it applied
0015 Kernel WireGuard where it exists, userspace where it does not, and say which
0016 One relay port, multiplexed, with the agent carrying relayed packets
0017 An agent asks for its relay credential; the server does not push it
0018 A mechanism is not done until something running reaches it
0019 Egress is a routing problem; overlapping prefixes are an addressing one
0020 Colliding prefixes are mapped, by the control plane that can see them
0021 Names resolve on the device, from desired state
0022 The web view ships inside the control plane and polls one snapshot
0023 The overview says what is wrong, not only what is true
0024 Local user accounts, scoped API tokens, and what becomes of the admin token
0025 The bus is PostgreSQL LISTEN/NOTIFY, not Redis
0026 Fail-closed egress on macOS lives in a pf anchor under com.apple
0027 A guessed-at account slows down; it is never locked out
0028 The Windows data plane is WinTun, shipped beside the binary
0029 Windows split DNS is NRPT, and the resolver moves to port 53 there
0030 Fail-closed egress on Windows is meshp's own WFP layer
0031 meshp login mints an API token and keeps it for one person
0032 The UI and the CLI are clients of the API, and parity is measured against it

Format

Copy 0000-template.md. Keep them short. The Consequences section is the one that earns its keep — write down what this makes harder, not only what it makes possible.

Rendered from docs/adr/README.md, which is where it is edited.