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.