| name | remotehost |
| description | Expose localhost and dev-server ports from the agent host to a user's phone, tablet, or other remote device using Tailscale. Use only when the user explicitly asks to spin up remotehost, Tailscale sharing, or remote access, or when Codex has been asked to share a website/API/dev-server link and a high-confidence remote-session check confirms the agent is running somewhere remote from the user's browsing device. Do not use for ordinary local browser testing, local previews, or localhost links on the same machine. |
Remotehost
Overview
Make a local service running on the agent host reachable from the user's remote device. Prefer a private Tailscale Serve link inside the user's tailnet; only use Tailscale Funnel when the user explicitly asks for a public internet URL.
Assume the user might not have Tailscale yet. Keep setup calm and guided: explain the minimum steps, let the user handle browser/SSO/mobile app actions, then continue from terminal evidence.
Invocation Gate
Use this skill only when one of these is true:
- The user explicitly asks to spin up this skill, remotehost, Tailscale sharing, remote access, or a phone/mobile-visible localhost link.
- The user asks for a shareable website, API, webhook, preview, or localhost link, and
scripts/remotehost-gate.sh --requested-share exits successfully.
If the gate script exits non-zero, do not install Tailscale, run tailscale up, run tailscale serve, or run tailscale funnel. Offer the normal local URL, or ask one concise question such as: "Do you want me to spin up remotehost so this localhost port is reachable from another device?"
The gate script cannot read the user's intent or prove which physical device they are holding. It only checks for high-confidence indicators that the agent session is remote, such as SSH, Codespaces, Gitpod, Coder, or cloud-workstation environments. Treat weak signals, such as a container marker by itself, as not enough.
Operating Rules
- Use
tailscale serve by default. It is private to devices signed in to the same tailnet.
- Default every Serve share to a one-hour lifetime. Use
scripts/remotehost.sh share unless there is a clear reason to run tailscale serve directly.
- Before doing setup work, satisfy the Invocation Gate. If the user explicitly requested remotehost, the gate is already satisfied.
- Do not use
tailscale funnel unless the user explicitly wants a public link and understands it exposes the service to the internet.
- If the local service exposes workspace files, authenticated app state, private source, generated artifacts, or other private data, state that risk and get the user's explicit approval before starting Tailscale login, daemon setup, Serve, or Funnel.
- Do not promise that a private Serve URL can redirect unsigned-in users. An unsigned-in device cannot reach the private URL, so no HTTP redirect can happen there.
- If a phone is not connected to Tailscale yet, give the user only the Tailscale download link alongside the private app link. Keep this handoff tight and consistent.
- Do not change ACLs, auth keys, device expiry, or tailnet policy unless the user asks.
- Ask for approval before installs, admin/sudo commands, GUI actions, or network downloads when the environment requires it.
- Relay the exact URL that Tailscale prints; do not invent the tailnet domain.
- If Tailscale CLI behavior differs, read
references/tailscale.md and prefer current official Tailscale docs.
Workflow
-
Identify the local service.
- Extract the port from the user's localhost URL or the running dev-server output.
- If no service is running, start it using the repo's normal command.
- Verify locally with
curl -I http://127.0.0.1:PORT or a path that should return quickly.
-
Check whether remotehost is allowed.
- If this was not an explicit remotehost request, run
scripts/remotehost-gate.sh --requested-share and stop unless it exits successfully.
- Run
scripts/remotehost.sh status PORT to inspect Tailscale, Serve config, and local port reachability without starting login or sharing.
- If
tailscale is missing, guide the user through installing Tailscale on the agent host and on the remote device.
-
Share the localhost port privately.
- Use the port as the target and default to a one-hour expiry:
scripts/remotehost.sh share localhost:PORT
- If the user specifies a different duration, pass it explicitly:
scripts/remotehost.sh share --ttl 30m localhost:PORT
scripts/remotehost.sh share --ttl 2h localhost:PORT
- Use
--no-expire only when the user explicitly asks for a persistent share.
- If the original local URL had a path or query, pass it through so the wrapper prints the final app URL:
scripts/remotehost.sh share \
--app-path '/?file=robots/elrobot/elrobot-follower.urdf' \
localhost:PORT
- Prefer the
remotehost app url: line. If the phone reports DNS/MagicDNS trouble, also try the remotehost ip url: HTTP fallback.
- Tell the user when the share will auto-close.
The wrapper uses system Tailscale when a daemon is reachable. On Codex/macOS where the CLI is installed but /var/run/tailscaled.socket is absent, it starts a temporary userspace tailscaled with a statedir for Serve certificates. If that userspace node is stale and tailscale status shows it as offline, the wrapper archives the old state, restarts userspace Tailscale, and reruns login.
- Put the remote device on the same tailnet.
- Have the user install the Tailscale app on the phone/tablet and sign in with the same identity used on the agent host.
- Ask them to confirm the Tailscale mobile app says connected before testing the link.
- If the phone is not connected yet, give this two-line handoff, replacing
PRIVATE_SERVE_URL with the exact Serve URL:
Tailscale: https://tailscale.com/download
App: PRIVATE_SERVE_URL
- Handle dev-server host checks.
- For Vite and similar dev servers, verify the remote hostname is accepted before reporting success:
curl -I -H 'Host: MACHINE.TAILNET.ts.net' http://127.0.0.1:PORT/
- For CAD Explorer, start or restart with
EXPLORER_ALLOWED_HOSTS=MACHINE.TAILNET.ts.net, then verify the same Host header returns 200 OK.
- If Vite dev/HMR loads poorly on a phone after Tailscale reachability is fixed, use a production build/preview and repoint Serve to the preview port. See
references/tailscale.md.
-
Verify access.
- Check that the local service still responds on
127.0.0.1:PORT.
- Ask the user to open the Tailscale Serve URL on a different remote device while Tailscale is connected.
- Do not treat a normal browser on the same host as a valid test when Tailscale is running in CLI userspace mode; macOS/host routing and MagicDNS are not installed for that browser.
- If it fails, compare
scripts/remotehost.sh status PORT on the host with the remote device's app status.
-
Clean up when finished.
- The wrapper schedules cleanup after one hour by default. Still clean up immediately when the user is done:
scripts/remotehost.sh off
Common Fixes
tailscale: command not found: read references/tailscale.md for install paths and platform notes.
- Login pending:
scripts/remotehost.sh share ... runs tailscale up; give the user the browser login URL or GUI instruction and retry after login completes.
- Userspace Tailscale is stale/offline: rerun share with
--fresh to rotate the userspace state explicitly.
- Remote device says "site can't be reached": confirm the phone is signed in to the same tailnet, Tailscale is connected, and the user is opening the exact Serve URL. Try the
remotehost ip url: fallback if MagicDNS is suspect.
- Unsigned-in phone cannot open the private URL: expected; give
https://tailscale.com/download and the private app URL to retry after connecting.
- Same-host browser cannot open the Serve URL after a CLI/userspace Tailscale setup: expected; test locally with
127.0.0.1:PORT, install the full Tailscale app/network extension, or test from a separate device in the tailnet.
- HTTPS Serve shows "This site can't provide a secure connection" and the
tailscaled log says no TailscaleVarRoot: rerun scripts/remotehost.sh share --fresh ...; the wrapper supplies --statedir.
- Dev server rejects the remote hostname or HMR/WebSocket traffic misbehaves: configure the dev server's allowed hosts/origins/public host for the Serve hostname. For CAD Explorer, set
EXPLORER_ALLOWED_HOSTS=MACHINE.TAILNET.ts.net and verify the same Host header returns 200 OK.
- Local backend is HTTPS with a self-signed certificate: use
tailscale serve https+insecure://localhost:PORT directly.
- User needs a URL that works without the remote device joining Tailscale: explain the exposure tradeoff, then use
scripts/remotehost.sh share --public ... only with explicit approval.
Resources
scripts/remotehost-gate.sh: conservative invocation check for explicit requests or high-confidence remote-session sharing cases.
scripts/remotehost.sh: one entrypoint for diagnostics, private Serve sharing, optional Funnel sharing, userspace Tailscale repair, and cleanup.
references/tailscale.md: install/setup notes, CAD Explorer preview fallback, cleanup details, Funnel cautions, and official docs links.