Multi-Connect (Phone)

Multi-Connect is the current, recommended way to reach MultiTerminal from your phone: an in-process gateway that serves a mobile PWA, published securely over your private Tailscale network. There is no separate companion app and no public web server — just MultiTerminal and your tailnet.

1. Prerequisites

Multi-Connect publishes MultiTerminal over your private Tailscale network rather than the public internet, so before you start you need:

  • A Tailscale account with both this PC and your phone signed in to the same tailnet. The phone reaches the PC over that shared private network — nothing is exposed publicly.
  • MagicDNS enabled on the tailnet (Tailscale admin console → DNS). The whole https://<host>.ts.net scheme depends on it — without MagicDNS there is no friendly hostname for the phone to open, only a raw tailnet IP.
  • MultiTerminal installed and running on the PC, with a phone username and password set (see the settings tab — login is disabled until both are changed from their defaults).

2. How it works

MultiTerminal hosts the phone experience itself. A second, independent ASP.NET Kestrel listener — the MultiRemoteGatewayHost — runs in-process on a loopback port (default 5100) and serves a Progressive Web App (PWA — an installable web app that gets a home-screen icon and can receive push notifications) plus the API the phone needs. It is completely separate from MultiTerminal's unauthenticated :5050 REST API, so it can carry its own session-auth pipeline without contaminating the desktop API.

Public reachability comes from Tailscale serve, which terminates TLS on your tailnet and forwards requests to the loopback gateway. The gateway trusts the X-Forwarded-Proto=https header from the loopback proxy and issues Secure session cookies accordingly.

Request path

Phone (PWA over Tailscale)
   → Tailscale serve (HTTPS, on <host>.ts.net)
      → MultiRemoteGatewayHost (loopback :5100, in MultiTerminal.exe)
         → MultiTerminal services (in-process — no extra HTTP hop)

Because the gateway calls MultiTerminal's services in-process, the phone gets the same task board, project list, team roster, chat/inbox, interactive terminal console (over WebSocket), and Web Push notifications that the desktop uses — with no proxy round-trips. The only remaining outbound hop is the off-box Cloudflare permission relay — a small Cloudflare-hosted message relay that forwards agent permission prompts to your phone and your tap-to-approve answer back, for cases where the phone is off the tailnet. It is optional and off-box: if you do not configure a relay base URL, MultiTerminal simply does not use it.

3. Quickstart (the setup skill)

The fastest path is the /multi-connect-setup skill. It is mostly automatic — the only manual step is a single browser login to Tailscale. Run it and it will:

  1. Detect (or install) Tailscale on this machine.
  2. Bring the node up — you complete one browser sign-in to your tailnet.
  3. Publish the loopback gateway over HTTPS with tailscale serve (forwarding to http://localhost:5100).
  4. Detect this node's MagicDNS hostname and write it back into the Multi-Connect config via the loopback POST /api/multi-connect/config.
  5. Verify the gateway answers on /health and print the final phone URL.

Before the phone can log in: set a phone username and password (see the settings tab below). Login is deliberately disabled — the gateway returns 503 — until both auth credentials are changed away from their default placeholder values, so the app never sits open on your tailnet with changeme/changeme.

4. On your phone

Once the skill (or the settings tab) has published the gateway and you have a phone username/password set, the phone side is a one-time setup in the mobile browser:

  1. On your phone, open https://<your-host>.ts.net in the browser (use the exact URL the skill printed, or the one the Copy button puts on your clipboard). The phone must be signed in to the same tailnet.
  2. Log in with the username and password you configured in the Multi-Connect settings tab. (If login returns 503, the credentials are still at their defaults — set them first.)
  3. Add to Home Screen to install the PWA — on iOS Safari via the Share menu → Add to Home Screen; on Android Chrome via the ⋮ menu → Install app / Add to Home screen. You then get a home-screen icon that launches the app full-screen.
  4. Allow notifications when prompted. This is what lets agent questions, permission prompts, and task/error alerts reach you as Web Push while you are away from the desk.

For push to work end-to-end the gateway also needs a VAPID keypair configured (VAPID is the Voluntary Application Server Identification keypair that authorizes Web Push to your phone — the VAPID subject and keys in the config are what identify MultiTerminal as the legitimate sender). The skill and settings tab manage these; you normally only set the VAPID subject.

5. The Multi-Connect settings tab

Everything the skill writes can also be configured by hand in Settings → Multi-Connect. The tab is the self-service surface — no JSON editing required:

ControlWhat it does
Detect TailscaleProbes Tailscale (runs tailscale status --json) and fills in this node's hostname, install/running state, and backend state.
Test connectionChecks that the in-process gateway answers on its /health endpoint.
CopyCopies the computed phone URL (https://<host>.ts.net) to the clipboard.
Load / Save (OK)Loads the effective config and persists changes. Each field shows its effective value and source (settings / appsettings / default) so you never edit on top of a hidden value.

Clicking OK restarts the gateway in-process so that restart-required fields (gateway port, VAPID subject, notification secret, relay base URL) take effect without relaunching MultiTerminal.

6. Configuration surface

Each per-install value resolves settings.txt (the Multi-Connect tab) → appsettings.Local.json → committed appsettings.json → built-in default. Per-install values and secrets belong in the tab or in the gitignored appsettings.Local.json — never in the committed appsettings.json.

FieldDefaultPurpose
Gateway port5100Loopback port the in-process gateway listens on. Keep tailscale serve aligned to it. (restart-required)
Tailscale enabledfalseWhether Tailscale fronting is in use for this install.
Tailscale hostnameunsetThis node's MagicDNS name (e.g. desktop.tailXXXX.ts.net). Filled by Detect / the skill.
Tailscale serve port443The HTTPS port Tailscale serve publishes on. Drives the phone URL.
Phone auth usernamechangemePhone-login username. Login disabled until non-default.
Phone auth passwordchangemeSecret (DPAPI-protected at rest). Login disabled until non-default.
Notification secretemptySecret shared with the push endpoint; when set, MultiTerminal attaches the matching X-MT-Secret on every forward. (restart-required)
VAPID subjectunsetThe sub claim for Web Push (a mailto: address or an http(s) URL). (restart-required)
Relay base URLunsetBase URL of the Cloudflare permission relay used for phone approvals. (restart-required)
Relay API keyemptySecret (DPAPI-protected) sent as the relay X-API-Key.

The three secrets — phone password, notification secret, relay API key — are DPAPI-protected on this machine. The config API never returns them in cleartext (see below).

For the full key reference, fallback sources, and deploy/migration steps, see docs/multiremote-gateway.md.

7. Security model

  • Loopback-only config. The /api/multi-connect/* endpoints are mounted only on the :5050 loopback REST host and are explicitly excluded from the :5100 phone gateway, so they are never reachable over Tailscale. Each action also rejects any non-loopback remote IP.
  • Secrets are never returned raw. GET /api/multi-connect/config reports each secret only as an isSet boolean plus its source (settings / appsettings / unset) — the cleartext value is never sent to any caller.
  • CSRF / cross-origin guard. Both the GET and the POST require a local, non-browser caller (an Origin / Sec-Fetch-Site check). A malicious page in a loopback browser is refused, while local tooling and the setup skill (which send no Origin) pass.
  • Fail-closed phone login. The gateway returns 503 for login until both auth username and password are set to non-default values, so it never accepts the committed placeholders behind Tailscale.
  • Secure cookies via forwarded headers. The gateway trusts X-Forwarded-Proto only from loopback (the Tailscale serve proxy), and issues HttpOnly, SameSite=Strict, Secure session cookies.

8. REST endpoints

The loopback configuration surface (used by the settings tab and the setup skill) lives under /api/multi-connect on :5050:

Method & pathPurpose
GET /api/multi-connect/configEffective per-install config. Secrets reported as is-set booleans with their source; carries a stable schemaVersion.
POST /api/multi-connect/configValidates then persists. Cleared fields are removed (never stored as ""); returns 400 on bad input with no partial write. Echoes the new effective config plus applied/restartRequired markers.
GET /api/multi-connect/tailscale-statusTyped Tailscale probe backing the tab's Detect button (installed / running / backend state / hostname / error).

Per-field POST semantics: null (omitted) leaves a value unchanged, an empty string clears it, and any other value sets it. This lets the tab send an untouched secret as null so it never overwrites a stored secret with a placeholder.

9. Troubleshooting

The most common phone-setup snags and where to look:

SymptomLikely cause & fix
Phone can't reach https://<host>.ts.net at allTailscale/MagicDNS: confirm the phone is signed in to the same tailnet and up, and that MagicDNS is enabled. Verify tailscale serve is running on the PC and the hostname matches what the Detect button reports.
503 at loginAuth is still default/disabled. The gateway fails closed until both the phone username and password are changed away from changeme. Set them in the settings tab, then retry.
Logged in, but no push notificationsCheck that you tapped Allow on the notification permission prompt (re-grant it in the phone's site settings if you dismissed it), and that a VAPID subject/keys are configured on the gateway.

For the broader FAQ, see Troubleshooting / FAQ.

For the deep configuration reference and hosting model, see docs/multiremote-gateway.md.