homescale/README.md

99 lines
3.5 KiB
Markdown
Raw Permalink Normal View History

# 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
```bash
ssh <user>@<node>.<your-tailnet>.ts.net
```
You land in your full stateful home. Each registered host is then just a verb:
```bash
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
```bash
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 travel** — `NAME <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`) |
```bash
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**:
```jsonc
{ "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.*