Go to file
Wayne Hayes c349fe488c Homescale — a roaming home for your fleet
SSH into one stable Tailscale node and reach your whole tailnet from it via
short per-host functions + sshfs mounts. A riced Arch dev container with sshd,
a stateful home, and declarative .env auth. See README.md and ROAMING.md.
2026-06-29 00:17:18 -04:00
dotfiles Homescale — a roaming home for your fleet 2026-06-29 00:17:18 -04:00
scripts Homescale — a roaming home for your fleet 2026-06-29 00:17:18 -04:00
 Homescale — a roaming home for your fleet 2026-06-29 00:17:18 -04:00
.env.example Homescale — a roaming home for your fleet 2026-06-29 00:17:18 -04:00
.gitignore Homescale — a roaming home for your fleet 2026-06-29 00:17:18 -04:00
docker-compose.yml Homescale — a roaming home for your fleet 2026-06-29 00:17:18 -04:00
Dockerfile Homescale — a roaming home for your fleet 2026-06-29 00:17:18 -04:00
entrypoint.sh Homescale — a roaming home for your fleet 2026-06-29 00:17:18 -04:00
nvm.zsh.snippet Homescale — a roaming home for your fleet 2026-06-29 00:17:18 -04:00
README.md Homescale — a roaming home for your fleet 2026-06-29 00:17:18 -04:00
roaming-zshrc.snippet Homescale — a roaming home for your fleet 2026-06-29 00:17:18 -04:00
ROAMING.md Homescale — a roaming home for your fleet 2026-06-29 00:17:18 -04:00

Homescale

Your fleet, one word away · a Tailscale-native roaming home

"I'd far rather be happy than right any day." And you're far more likely to be both when your whole fleet answers from a single container you ssh into.

Homescale turns a riced Arch dev container into a roaming home for your fleet: one stable Tailscale node you SSH into, from which every host on your tailnet is a single word away. It holds the keyring; you carry nothing but a terminal. Run a coding agent inside it and it becomes a fleet control plane with hands.

The IDE is still under the hood — colorscheme, LSP, a git-snapshotted home. It's just no longer the point. The point is home.

(Tailscale today → Headscale later: the "home" you scale.)


Use it

ssh <user>@<node>.<your-tailnet>.ts.net

You land in your full stateful home. Each registered host is then just a verb:

web uptime                      # runs on the host named "web", over the tailnet
db  sudo systemctl status pg    # remote sudo prompts — the boundary holds
edge journalctl -u caddy        # any registered host
mount_host db                   # sshfs its filesystem → /workspace/db
roaming-status                  # what's registered

Each host becomes a shell function. Pipes/redirects run locally, exactly like plain ssh — quote the whole pipeline to run it remote.


Onboard a host

add_new_machine.sh NAME NAME.<your-tailnet>.ts.net <user> /
roaming-reload

Three things that bite, up front:

  • Key-only hosts (PasswordAuthentication no) can't be bootstrapped by ssh-copy-id. Pre-install Homescale's ~/.ssh/id_ed25519.pub via a path the host already trusts — keyless tailscale ssh is ideal.
  • Custom ssh port? Add a Host … Port <PORT> block to ~/.ssh/config (the roaming engine uses plain ssh).
  • Aliases don't travelNAME <cmd> is non-interactive ssh. Use real binaries or ~/bin scripts.

How it's built

Piece What
ts-nvimide Tailscale sidecar — stable node (no ?ephemeral), tag:nvimide
nvimide the dev image sharing the sidecar's netns, running sshd as init
home volume stateful home (snapshots, tools, .ssh)
auth declarative via .env (NVIMIDE_USER / NVIMIDE_PASSWORD / NVIMIDE_SSH_PUBKEY)
cp .env.example .env      # fill TS_OAUTH_CLIENT_SECRET, NVIMIDE_SSH_PUBKEY, TS_TAILNET …
docker compose up -d --build

The committed compose uses a managed home volume so it works out of the box. To pin it to an existing/migrated volume, override nvimide-home in a local (gitignored) docker-compose.override.yml.

Tailnet ACL (lives in your Tailscale console, not this repo)

A grant for plain-TCP reach + two ssh rules — a separate section from grants, needed in both directions:

{ "src": ["tag:nvimide"], "dst": ["*"], "ip": ["*"] }                                 // reach (plain ssh)
{ "src": ["tag:nvimide"], "dst": ["*"], "users": ["<user>"], "action": "accept" }     // tailscale ssh OUT
{ "src": ["*"], "dst": ["tag:nvimide"], "users": ["<user>"], "action": "accept" }     // tailscale ssh IN — "Go Home!"

Deeper

  • ROAMING.md — the roaming engine: registry format, host-functions, sshfs mounts, the gotchas (host-key persistence, the CAP_SYS_CHROOT sshd needs, the sidecar healthcheck that must not gate on the app's :22).

MIT — fork it, name your node something silly, make it home.