ADR 24: Installer post-MVP — mesh modes, explicit roles, and cloud relay
Status: Proposed
Date: 2026-05-14
Last Updated: 2026-05-23
Context
The current MVP desktop installer (apps/installer-gui) provisions a Personal Device Triangle: three libp2p identities, shared swarm/JWT material, and per-device substratum-triangle.json v1 artifacts (substratum-triangle-v1). Discovery may use Tailscale where available; local install and Android export today rely on brittle heuristics (compile-time OS and hostname substring matching) to guess which generated identity is “this machine” or the Android row.
That MVP is intentionally narrow. The product direction beyond MVP is:
- Exactly three user-owned nodes define the personal mesh (the triangle). Topology and validation should enforce this contract explicitly.
- Optional Substratum cloud connectivity adds a relay / home-base path so the same three nodes can reach each other (or the broader private swarm) via infrastructure Substratum operates — not a fourth personal triangle owner, unless we later choose a different product definition.
- Private hardware-only deployments should work without cloud: bootstrap and pinning remain among the three identities; reachability is the operator’s problem (overlay VPN, LAN, public addresses, etc.).
This ADR records intent and boundaries for the phase after the structured-triangle MVP so implementation does not entrench guesswork or conflate “discovery transport” with “mesh membership,” hosting topology (who runs the API/catalog), or mesh relay (how nodes reach each other).
Decision (directional)
The following are target properties for a post-MVP installer and kit; exact schema names and file layout are specified in substratum-installer-profile-v1 where noted.
1. Non-brittle role assignment
- No inferring “local device” or “Android export target” from
cfg(target_os)plus hostname substrings. - The wizard must expose explicit controls: e.g. “This row is the machine I am configuring now” for self-hosted install, and “Build APK for device: …” chosen from the three rows (or equivalent stable
device_id).
2. Enforce the personal mesh invariant
- Three provisioned identities for the personal mesh (unless we explicitly redefine “personal mesh” in a later ADR).
- Validation lives in the orchestrator (and optionally mirrored in exported artifacts): distinct addressing inputs where required, consistent pinning/bootstrap graph among the three.
3. Separate “triangle crypto” from “deployment profile”
substratum-triangle.jsonv1 remains the per-device cryptographic and peer-list contract among the three nodes (ADR 23, ADR 03).- A distinct, versioned installer profile (
substratum-installer-profile-v1) carries:hosting_topology:self_hosted(operator runs gateway + Postgres + UI on their hardware) vscloud_connected(Substratum-hosted API; local nodes only).mesh_mode:private_hardwarevscloud_relay(optional Substratum-operated relay bootstrap — formerly namedcloud_homebasein early drafts).- Discovery metadata: which provider was used (Tailscale, manual-only, future LAN/QR, …) without making any single provider mandatory.
- Cloud path (when
mesh_modeiscloud_relay): references or opaque handles for relay / rendezvous material that sidecar or home-base resolves into libp2p bootstrap / PSK namespace config — not duplicating triangle secrets unless security review demands otherwise. - Platform install paths:
postgres_port,postgres_data_dir,install_root.
Runtime (sidecar, home-base, gateway) merges triangle + profile (or consumes a merged config produced at install time). The gateway triangle file format need not absorb cloud flags if we keep separation of concerns clear.
4. Hosting topology vs mesh mode (do not conflate)
| Axis | Field | Question answered |
|---|---|---|
| Hosting | hosting_topology | Who runs the gateway API and Postgres catalog? |
| Mesh | mesh_mode | Do nodes use only private hardware reachability, or also a Substratum relay? |
Installer Step 0 (before device discovery) presents this choice to operators. User-facing copy lives in Lingui catalogs, not in this ADR.
hosting_topology | Operator runs | Typical use |
|---|---|---|
self_hosted | Gateway, Postgres, static file-explorer, edge proxy on home server | No Substratum SaaS; family owns the server |
cloud_connected | Node agents only; API at Substratum Cloud | Lower ops burden (UI placeholder until account linking ships) |
5. Semantics of private_hardware vs cloud_relay
| Mode | Personal triangle | Cloud / relay |
|---|---|---|
| Private hardware | Three nodes; bootstrap/pinning among them; no Substratum-operated relay in profile. | None in profile. |
| Cloud relay | Still exactly three issued identities and triangle manifests. | Profile supplies relay / rendezvous (and related) so nodes can use Substratum infrastructure for connectivity while retaining the same three-way ownership model. |
Relay peers are connectivity helpers under ADR 03; pinning policy among the three user nodes remains a product decision (typically still the three identities, not “pin everything to relay” unless explicitly designed).
6. Nonstandard personal-install ports
Personal/self-hosted installs use dedicated ports that avoid clashing with common local services:
| Service | Port | Rationale |
|---|---|---|
| PostgreSQL | 35432 | Default 5432 is often taken (Postgres.app, Homebrew). 35432 is distinct from dev Compose host 15432 (docker-compose.yml) so Docker and the installer can run on the same machine. |
| Edge (UI + proxied API) | 35480 | Default 8080 is widely used by dev servers. Self-hosted Caddy listens here; gateway API stays internal on 38080. |
| Gateway API (internal) | 38080 | Edge reverse-proxies /api and /.well-known here; distinct from Compose gateway host 18080. |
The installer provisions a dedicated Postgres data directory under the install root (e.g. {install_root}/postgres/) and never repoints an existing system cluster. PostgreSQL binaries come from the OS (Express install on Step 4 may invoke Polkit on Linux, winget on Windows, or Homebrew on macOS). Encryption at rest is the operator’s responsibility: enable OS full-disk encryption (FileVault, BitLocker, LUKS); the installer does not create encrypted volumes or Postgres TDE in v1.
substratum-installer-profile.json records postgres_port, gateway_bind_port, edge_public_port, and postgres_source: "os".
Note: Dev Compose continues to use nginx edge on host 8080 for contributor ergonomics; that is separate from the self-hosted installer contract above.
7. Cross-platform self-hosted install matrix
| Component | macOS | Windows | Linux |
|---|---|---|---|
| Install root | ~/.substratum/ | C:\ProgramData\Substratum\ | ~/.substratum/ (user scope v1) |
| PostgreSQL | OS packages (apt/dnf, winget, Homebrew) or PG 16+ on PATH; dedicated cluster port 35432 | same | same |
| Gateway | LaunchAgent (internal 38080) | Windows Service SubstratumGateway | systemd user unit substratum-gateway.service |
| Edge (Caddy) | LaunchAgent (35480) | Windows Service SubstratumEdge | systemd user unit substratum-edge.service |
| Autostart | launchctl load | Start-Service | systemctl --user enable --now (+ loginctl enable-linger for home servers) |
| PDS | Deferred v1 | Deferred v1 | Deferred v1 |
Install layout (logical; paths vary by OS):
{install_root}/
substratum-triangle.json
substratum-gateway.json
substratum-installer-profile.json
postgres/
web/
data/blocks/
edge/Caddyfile
bin/
logs/Orchestrator commands: get_install_plan_cmd (Step 0 preview), run_system_package_phase_cmd + install_self_hosted (Step 4 Express install). Supersedes triangle-only install_local for the self-hosted path.
8. Discovery as a plug-in concept
- Treat Tailscale (or any overlay) as one discovery provider that suggests addresses for the three slots.
- The core installer flow assumes manual multiaddr / IP inputs remain valid without any overlay.
Out of scope for the current MVP
- Replacing all MVP heuristics (tracked as follow-up implementation work, not blocked by this ADR).
cloud_connectedaccount linking and hosted API provisioning UI.mesh_mode: cloud_relayrelay attestation and bootstrap fetch.- Local PDS without Docker on personal installs.
- Native mobile file explorer (phone uses web UI against server URL).
- system-wide Linux install (
/var/lib/substratum) — user-scoped~/.substratumonly for v1.
Consequences
- Positive: Clear path from MVP triangle files to a stable, testable model: three identities + explicit UI + hosting choice + optional relay profile; aligns with ADR 04 as the merge point for runtime config.
- Negative: Extra persisted artifact and versioning; orchestrator and mobile exporters must stay in sync; cross-platform service registration increases test surface.
- Neutral: MVP artifacts remain valid; post-MVP work extends orchestration without invalidating v1 triangle JSON.
Related
- ADR 03: Network Behavior — private swarm, PSK, modular libp2p.
- ADR 04: Sidecar Design — local agent merges policy and connectivity.
- ADR 23: Personal Unified Installer and Cross-Platform Kit — orchestrator vs runtime, kit manifest direction.
substratum-triangle-v1— per-device triangle manifest v1.substratum-installer-profile-v1— installer deployment profile v1.