← back to Tailscale Home Access

index.html

120 lines

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>SSH home from the office — Tailscale guide</title>
<style>
  :root { color-scheme: light dark; --fg:#111; --bg:#fff; --muted:#666; --code-bg:#f4f4f4; --accent:#0a7; --warn:#c40; --rule:#e5e5e5; }
  @media (prefers-color-scheme: dark) {
    :root { --fg:#eee; --bg:#111; --muted:#999; --code-bg:#1d1d1d; --rule:#2a2a2a; }
  }
  * { box-sizing: border-box; }
  body { font: 16px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; color: var(--fg); background: var(--bg); margin: 0; }
  main { max-width: 760px; margin: 0 auto; padding: 32px 24px 80px; }
  h1 { font-size: 28px; margin: 0 0 4px; letter-spacing: -0.01em; }
  .sub { color: var(--muted); margin: 0 0 32px; font-size: 14px; }
  h2 { font-size: 20px; margin: 36px 0 12px; padding-bottom: 6px; border-bottom: 1px solid var(--rule); }
  h3 { font-size: 16px; margin: 24px 0 8px; }
  code, pre { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: 14px; }
  code { background: var(--code-bg); padding: 1px 6px; border-radius: 4px; }
  pre { background: var(--code-bg); padding: 14px 16px; border-radius: 6px; overflow-x: auto; line-height: 1.45; }
  pre code { background: none; padding: 0; }
  .ok { color: var(--accent); font-weight: 600; }
  .warn { color: var(--warn); font-weight: 600; }
  table { border-collapse: collapse; width: 100%; margin: 12px 0; font-size: 14px; }
  th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--rule); vertical-align: top; }
  th { font-weight: 600; }
  ul, ol { padding-left: 22px; }
  li { margin: 4px 0; }
  .callout { border-left: 3px solid var(--accent); padding: 10px 14px; background: var(--code-bg); border-radius: 0 6px 6px 0; margin: 16px 0; font-size: 14px; }
  .callout.warn { border-left-color: var(--warn); }
  hr { border: 0; border-top: 1px solid var(--rule); margin: 32px 0; }
  .footer { font-size: 12px; color: var(--muted); margin-top: 48px; }
</style>
</head>
<body>
<main>
  <h1>SSH home from the office &mdash; Tailscale guide</h1>
  <p class="sub">Home box: <code>stevestudio2s-mac-studio</code> (<code>100.65.187.120</code>) &middot; tailnet: <code>designerwallcoverings.com</code> &middot; generated 2026-05-14</p>

  <h2>How Tailscale gets you in</h2>
  <p>Tailscale is a WireGuard mesh. Every device signed in as <code>steve@designerwallcoverings.com</code> gets a stable <code>100.x.y.z</code> address (CGNAT range) and a MagicDNS short name. Those addresses route over encrypted tunnels regardless of which network either side is on &mdash; home Wi-Fi, office, hotspot, hotel. No port forwarding, no public IP, no DDNS. NAT traversal is automatic; if direct UDP fails it falls back to Tailscale&apos;s DERP relays.</p>

  <h2>What&apos;s ready on the home box (verified 2026-05-14)</h2>
  <table>
    <tr><th>Check</th><th>State</th></tr>
    <tr><td><code>tailscaled</code> running, signed in</td><td><span class="ok">OK</span> &mdash; 100.65.187.120</td></tr>
    <tr><td>macOS Remote Login (sshd)</td><td><span class="ok">ON</span> &mdash; live <code>sshd-session</code> process verified</td></tr>
    <tr><td><code>~/.ssh/authorized_keys</code></td><td><span class="ok">4 keys</span> already trusted</td></tr>
    <tr><td>SSH-over-tailnet loopback test</td><td><span class="ok">PASS</span> &mdash; <code>ssh stevestudio2@100.65.187.120</code> returns hostname/uptime/uname</td></tr>
    <tr><td>Tailnet RTT to kamatera (proxy for office)</td><td><span class="ok">11 ms direct</span></td></tr>
    <tr><td>Tailscale SSH server</td><td><span class="warn">disabled</span> &mdash; sandboxed GUI build can&apos;t host it (see Option B)</td></tr>
  </table>

  <h2>Option A &mdash; Standard SSH over Tailscale (works today)</h2>
  <p>From the office machine, once it&apos;s on the tailnet:</p>
  <pre><code>ssh stevestudio2@stevestudio2s-mac-studio
# or by IP (bypasses MagicDNS, useful for debugging)
ssh stevestudio2@100.65.187.120</code></pre>

  <p>If the office machine&apos;s pubkey isn&apos;t yet trusted on home, run from the office once:</p>
  <pre><code>ssh-copy-id stevestudio2@stevestudio2s-mac-studio</code></pre>

  <p>Optional <code>~/.ssh/config</code> entry on the office machine for ergonomics:</p>
  <pre><code>Host home
    HostName stevestudio2s-mac-studio
    User stevestudio2
    ForwardAgent yes
    ServerAliveInterval 60</code></pre>
  <p>Then it&apos;s just <code>ssh home</code>.</p>

  <h2>Option B &mdash; Tailscale SSH (nice-to-have, requires app swap)</h2>
  <p>Tailscale SSH replaces OpenSSH key auth with the tailnet identity layer. No <code>authorized_keys</code> management, full audit trail in admin console, revoke by removing the device.</p>
  <div class="callout warn">
    <strong>Blocker on home box:</strong> the macOS GUI Tailscale (build 1.96.5) is sandboxed and structurally <em>cannot</em> host the SSH server. You&apos;d need to swap to the Homebrew <code>tailscaled</code>:
  </div>
  <pre><code>osascript -e 'quit app "Tailscale"'
brew install tailscale
sudo brew services start tailscale
sudo tailscale up --ssh --accept-routes --operator=stevestudio2 \
     --hostname=stevestudio2s-mac-studio
# re-auth in browser</code></pre>
  <p>After the swap, the menu bar app is gone but <code>tailscale ssh stevestudio2@stevestudio2s-mac-studio</code> works from any tailnet device. <strong>Not done yet</strong> &mdash; Steve to decide.</p>

  <h2>Prerequisite: get the office box on the tailnet</h2>
  <p>As of 2026-05-14 the office machine isn&apos;t in <code>tailscale status</code> output. Active non-home nodes today are <code>kamatera</code> (prod) and <code>stevens-macbook-pro-2021</code> only. Install on the office machine:</p>
  <pre><code>brew install --cask tailscale
open -a Tailscale
# sign in as steve@designerwallcoverings.com</code></pre>
  <p>The office node will appear in the tailnet within ~5 seconds.</p>

  <h2>Test plan (run from the office once enrolled)</h2>
  <ol>
    <li>Confirm tailnet sees home: <code>tailscale status | grep stevestudio2s-mac-studio</code></li>
    <li>Latency check: <code>tailscale ping stevestudio2s-mac-studio</code> (expect &lt; 50&nbsp;ms typical)</li>
    <li>SSH smoke test: <code>ssh -v stevestudio2@stevestudio2s-mac-studio "hostname &amp;&amp; uname -a &amp;&amp; uptime"</code> &mdash; expected output is <code>SteveStacStudio.lan</code> on Darwin 25.3.0</li>
    <li>Round-trip token: <code>ssh stevestudio2@stevestudio2s-mac-studio "echo TS-$(date +%s) &gt; /tmp/ts-test &amp;&amp; cat /tmp/ts-test"</code></li>
  </ol>

  <h2>Likely failures &amp; fixes</h2>
  <table>
    <tr><th>Symptom</th><th>Fix</th></tr>
    <tr><td><code>Permission denied (publickey)</code></td><td>Office pubkey isn&apos;t in home&apos;s <code>authorized_keys</code>. Run <code>ssh-copy-id</code> from office, or paste pubkey to home Claude session.</td></tr>
    <tr><td><code>Could not resolve hostname</code></td><td>MagicDNS off on office. Run <code>tailscale up --accept-dns</code> on office, or use the IP <code>100.65.187.120</code> directly.</td></tr>
    <tr><td><code>Connection refused</code></td><td>Remote Login toggled off. System Settings &rarr; General &rarr; Sharing &rarr; Remote Login = ON.</td></tr>
    <tr><td>Hangs, no error</td><td>Home machine asleep. System Settings &rarr; Lock Screen &rarr; never sleep on AC; Battery &rarr; Prevent automatic sleep = ON.</td></tr>
  </table>

  <h2>Reply with these so home can finish wiring</h2>
  <ul>
    <li>Output of <code>tailscale status | head -2</code> on the office box (confirms enrollment)</li>
    <li><code>cat ~/.ssh/id_ed25519.pub</code> from the office (if not already trusted on home)</li>
    <li>Verbose SSH output (<code>ssh -v &hellip;</code>) if step&nbsp;3 of the test plan fails</li>
  </ul>

  <p class="footer">Local copy: <code>~/Projects/tailscale-home-access/index.html</code> &middot; Inter-session inbox refs: <code>mwq-cd044a9955</code>, <code>mwq-12fecd279b</code> in <code>~/.claude/inter-session-inbox.jsonl</code>.</p>
</main>
</body>
</html>