# Remotehost

> 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.

- Skill: `earthtojake/remotehost` (Agent Skill)
- Install (CLI): `npx skillmds@latest add earthtojake/remotehost`
- Raw SKILL.md: https://api.skillmd.com/api/skills/earthtojake/remotehost/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: earthtojake (https://skillmd.com/u/earthtojake)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/earthtojake/remotehost

---


# 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

1. 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.

2. 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.

3. Share the localhost port privately.
   - Use the port as the target and default to a one-hour expiry:

```bash
scripts/remotehost.sh share localhost:PORT
```

   - If the user specifies a different duration, pass it explicitly:

```bash
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:

```bash
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.

4. 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:

```text
Tailscale: https://tailscale.com/download
App: PRIVATE_SERVE_URL
```

5. Handle dev-server host checks.
   - For Vite and similar dev servers, verify the remote hostname is accepted before reporting success:

```bash
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`.

6. 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.

7. Clean up when finished.
   - The wrapper schedules cleanup after one hour by default. Still clean up immediately when the user is done:

```bash
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.

