Self-hosted installer troubleshooting (alpha)
Last Updated: 2026-06-02
Runbook for the Substratum Installer Express install (apps/installer-gui): home server on your machine, Substratum desktop app, and edge on port 35480. For Compose dev OAuth origins, see OAuth and PDS origins. For HTTPS production topology, see Production deployment.
Install layout
| Platform | Install root |
|---|---|
| macOS | ~/.substratum/ |
| Linux | ~/.substratum/ |
| Windows | C:\ProgramData\Substratum\ |
Express install copies binaries under {install_root}/bin/, static UI under {install_root}/web/, Caddy config under {install_root}/edge/, Postgres data under {install_root}/postgres/, and blockstore data under {install_root}/data/blocks/. See Catalog vs blockstore storage for how catalog size compares to file bytes.
Express install also writes manifests in the install root:
| File | Purpose |
|---|---|
substratum-gateway.json | Gateway operational config (DB, bind 38080, public_base_url, CORS, OAuth/PDS URLs, rust_log) |
substratum-installer-profile.json | Edge hostname/port, pds_provider, Postgres paths |
substratum-triangle.json | Personal device triangle (mesh) |
Default ports (ADR 24):
| Service | Port |
|---|---|
Edge (Caddy — UI + proxied /api) | 35480 |
| Gateway (internal) | 38080 |
| Postgres | 35432 |
Default edge URL: http://substratum.localhost:35480 (.localhost resolves to loopback on modern browsers). http://127.0.0.1:35480 also works when CORS includes it (installer adds both).
Service logs
After a successful install, the installer message includes Service logs: {install_root}/logs.
Installer app log
The Substratum Installer (Tauri) appends structured logs to:
| Path | Content |
|---|---|
{install_root}/logs/installer.log | Bundled resource resolution, Express install steps, gateway repair, desktop app install, PDS changes |
Before the first install, logs go to ~/.substratum/logs/installer.log (macOS/Linux) or C:\ProgramData\Substratum\logs\installer.log (Windows).
Filter verbosity with RUST_LOG (default info,substratum_installer_gui=debug). In debug builds, logs also print to stderr; set SUBSTRATUM_INSTALLER_LOG_STDERR=1 in release to mirror file output to the terminal.
tail -f ~/.substratum/logs/installer.logmacOS (LaunchAgents)
| Service | stdout | stderr (check first) |
|---|---|---|
| Gateway | {install_root}/logs/gateway.log | gateway.err.log |
| Edge (Caddy) | {install_root}/logs/edge.log | edge.err.log |
| Postgres | {install_root}/logs/postgres.log | (same file) |
Example:
tail -f ~/.substratum/logs/gateway.err.logGateway runs with working directory = install root so it loads ./substratum-gateway.json (substratum-gateway-application-v1).
Restart gateway after config edits:
launchctl kickstart -k gui/$(id -u)/cloud.substratum.gatewayLaunchAgent labels: cloud.substratum.postgres, cloud.substratum.gateway, cloud.substratum.edge.
Linux (user systemd)
Units: substratum-postgres.service, substratum-gateway.service, substratum-edge.service.
journalctl --user -u substratum-gateway.service -fPostgres may also append to {install_root}/logs/postgres.log.
Windows (services)
Services: SubstratumGateway, SubstratumEdge, Postgres registration under the install root. Check Event Viewer and {install_root}\logs\ when present.
Enable verbose gateway logging
Edit rust_log in substratum-gateway.json, then restart the gateway service:
"rust_log": "debug,substratum_auth=debug,substratum_ingress=debug"Reproduce the issue and read gateway.err.log. Release gateway binaries honor the manifest when env does not override (gateway manifest reference).
Health checks
# Edge → static UI + API proxy
curl -sS -o /dev/null -w "%{http_code}\n" http://127.0.0.1:35480/api/v1/health
# Gateway direct (bypass Caddy)
curl -sS -o /dev/null -w "%{http_code}\n" http://127.0.0.1:38080/api/v1/health| HTTP result | Likely issue |
|---|---|
| Edge 502 | Gateway not up or Postgres not ready — see gateway.err.log, postgres.log |
| Edge 200, gateway direct fails | Caddy OK but gateway crashed — gateway.err.log |
| Both fail | Services not started — installer Step 0 plan / LaunchAgents (macOS) |
Substratum desktop app
The installer installs Substratum.app to ~/Applications (macOS). The app webview loads http://127.0.0.1:{edge_public_port} from substratum-installer-profile.json, not the .localhost hostname.
Staging / rebuild:
cd apps/installer-gui && pnpm stage-explorerDebug installer sessions read live apps/installer-gui/src-tauri/resources/bundled/ after staging (restart pnpm dev:tauri). Release installer .app embeds resources at build time — run pnpm build:installer-tauri after staging.
OAuth sign-in failures
Sign-in calls POST /api/v1/oauth/start. A 400 with "OAuth authorize failed: …" comes from the gateway (OAuthStartError); details are usually in gateway.err.log if rust_log is info or higher.
1. Exercise OAuth from the shell
curl -sS -X POST http://127.0.0.1:35480/api/v1/oauth/start \
-H 'Content-Type: application/json' \
-d '{"handle":"YOUR_HANDLE.bsky.social"}'Compare the JSON error with the UI message.
2. public_base_url is loopback (installer default)
Express install sets public_base_url to http://127.0.0.1:35480 (not the edge hostname) so AT Protocol OAuth uses loopback redirect_uri values accepted by Bluesky and other PDSes. cors_allowed_origins still includes both 127.0.0.1 and your edge hostname (e.g. http://substratum.localhost:35480).
Opening the Substratum desktop app at 127.0.0.1 therefore matches OAuth metadata. You can still browse via substratum.localhost when CORS lists it.
Upgrade: Opening installer Step 0 runs repair_gateway_oauth_manifest, which rewrites an older manifest that used substratum.localhost as public_base_url. Restart the gateway after repair (launchctl kickstart -k gui/$(id -u)/cloud.substratum.gateway on macOS).
See OAuth and PDS origins — same-origin rule.
3. PDS provider vs handle
Step 0 Sign-in (AT Protocol PDS) writes OAuth fields into substratum-gateway.json:
| Provider | Use for |
|---|---|
| Bluesky (default) | Existing *.bsky.social handles (public AppView) |
| Substratum | *.substratum.cloud style accounts |
| Local | Loopback PDS on this machine (alice.test, etc.) |
| Custom | Operator-supplied PDS base URL |
Check pds_provider in substratum-installer-profile.json and atproto_appview_url / pds_* in substratum-gateway.json. Opening installer settings runs repair_gateway_oauth_manifest if AppView was incorrectly equal to pds_url.
4. Remote PDS rejects authorize
For federated handles, the user’s home PDS fetches client_id metadata from your edge and validates redirect_uri. Failures often show as OAuth authorize failed: http status: 400 without a local stack trace. Confirm:
- Edge URL is reachable from the machine (for local alpha, same Mac is enough).
GET http://<your-edge>/.well-known/oauth-client-metadata.jsonreturns JSON.
curl -sS "http://substratum.localhost:35480/.well-known/oauth-client-metadata.json" | headAlpha support checklist
When reporting issues, attach:
installer.logandgateway.err.log(last ~100 lines after reproducing sign-in)substratum-gateway.jsonandsubstratum-installer-profile.json(redact passwords)- Handle used and PDS provider from Step 0
- URL in the Substratum window (
127.0.0.1vssubstratum.localhost) - Installer build type (dev
tauri devvs release.app)
Updates (interim)
Long-term design: ADR 31 — Personal kit updates and in-app update checks. Until that ships, refresh components via the installer (Open Substratum for the desktop app; Express install for gateway/UI/edge).