Skip to content

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:

  1. Exactly three user-owned nodes define the personal mesh (the triangle). Topology and validation should enforce this contract explicitly.
  2. 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.
  3. 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.json v1 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) vs cloud_connected (Substratum-hosted API; local nodes only).
    • mesh_mode: private_hardware vs cloud_relay (optional Substratum-operated relay bootstrap — formerly named cloud_homebase in early drafts).
    • Discovery metadata: which provider was used (Tailscale, manual-only, future LAN/QR, …) without making any single provider mandatory.
    • Cloud path (when mesh_mode is cloud_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)

AxisFieldQuestion answered
Hostinghosting_topologyWho runs the gateway API and Postgres catalog?
Meshmesh_modeDo 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_topologyOperator runsTypical use
self_hostedGateway, Postgres, static file-explorer, edge proxy on home serverNo Substratum SaaS; family owns the server
cloud_connectedNode agents only; API at Substratum CloudLower ops burden (UI placeholder until account linking ships)

5. Semantics of private_hardware vs cloud_relay

ModePersonal triangleCloud / relay
Private hardwareThree nodes; bootstrap/pinning among them; no Substratum-operated relay in profile.None in profile.
Cloud relayStill 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:

ServicePortRationale
PostgreSQL35432Default 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)35480Default 8080 is widely used by dev servers. Self-hosted Caddy listens here; gateway API stays internal on 38080.
Gateway API (internal)38080Edge 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

ComponentmacOSWindowsLinux
Install root~/.substratum/C:\ProgramData\Substratum\~/.substratum/ (user scope v1)
PostgreSQLOS packages (apt/dnf, winget, Homebrew) or PG 16+ on PATH; dedicated cluster port 35432samesame
GatewayLaunchAgent (internal 38080)Windows Service SubstratumGatewaysystemd user unit substratum-gateway.service
Edge (Caddy)LaunchAgent (35480)Windows Service SubstratumEdgesystemd user unit substratum-edge.service
Autostartlaunchctl loadStart-Servicesystemctl --user enable --now (+ loginctl enable-linger for home servers)
PDSDeferred v1Deferred v1Deferred v1

Install layout (logical; paths vary by OS):

text
{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_connected account linking and hosted API provisioning UI.
  • mesh_mode: cloud_relay relay 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 ~/.substratum only 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.