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.
On This Page
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.netscheme 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:
- Detect (or install) Tailscale on this machine.
- Bring the node up — you complete one browser sign-in to your tailnet.
- Publish the loopback gateway over HTTPS with
tailscale serve(forwarding tohttp://localhost:5100). - Detect this node's MagicDNS hostname and write it back into the Multi-Connect config via the loopback
POST /api/multi-connect/config. - Verify the gateway answers on
/healthand 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:
- On your phone, open
https://<your-host>.ts.netin 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. - 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.) - 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.
- 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:
| Control | What it does |
|---|---|
| Detect Tailscale | Probes Tailscale (runs tailscale status --json) and fills in this node's hostname, install/running state, and backend state. |
| Test connection | Checks that the in-process gateway answers on its /health endpoint. |
| Copy | Copies 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.
| Field | Default | Purpose |
|---|---|---|
| Gateway port | 5100 | Loopback port the in-process gateway listens on. Keep tailscale serve aligned to it. (restart-required) |
| Tailscale enabled | false | Whether Tailscale fronting is in use for this install. |
| Tailscale hostname | unset | This node's MagicDNS name (e.g. desktop.tailXXXX.ts.net). Filled by Detect / the skill. |
| Tailscale serve port | 443 | The HTTPS port Tailscale serve publishes on. Drives the phone URL. |
| Phone auth username | changeme | Phone-login username. Login disabled until non-default. |
| Phone auth password | changeme | Secret (DPAPI-protected at rest). Login disabled until non-default. |
| Notification secret | empty | Secret shared with the push endpoint; when set, MultiTerminal attaches the matching X-MT-Secret on every forward. (restart-required) |
| VAPID subject | unset | The sub claim for Web Push (a mailto: address or an http(s) URL). (restart-required) |
| Relay base URL | unset | Base URL of the Cloudflare permission relay used for phone approvals. (restart-required) |
| Relay API key | empty | Secret (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:5050loopback REST host and are explicitly excluded from the:5100phone 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/configreports each secret only as anisSetboolean 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-Sitecheck). 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
503for 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-Protoonly from loopback (the Tailscale serve proxy), and issuesHttpOnly,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 & path | Purpose |
|---|---|
GET /api/multi-connect/config | Effective per-install config. Secrets reported as is-set booleans with their source; carries a stable schemaVersion. |
POST /api/multi-connect/config | Validates 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-status | Typed 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:
| Symptom | Likely cause & fix |
|---|---|
Phone can't reach https://<host>.ts.net at all | Tailscale/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 login | Auth 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 notifications | Check 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.