# nvimide — roaming dev IDE (arch-dev v3). Its OWN thing; follows the # Tailscale sidecar pattern and rides your tailnet, but is # not part of that stack. # # You SSH into this node from any device; it holds the fleet keyring + roaming # engine and reaches every other host. It is a STABLE tailnet node (not # ephemeral) — always reachable, clean MagicDNS name (no -N tombstones) — so # TS_AUTHKEY omits ?ephemeral=true. # # Declarative auth (NixOS-style): user, password, and ssh pubkey all come from # .env — nothing baked into the image, nothing mounted. Copy .env.example to # .env and fill it in. # # Bring up: # 1. ACL: add `tag:nvimide` + authorize the OAuth client to mint it. # 2. cp .env.example .env && fill NVIMIDE_SSH_PUBKEY / NVIMIDE_PASSWORD / TS_* # 3. docker compose up -d --build # 4. ssh ${NVIMIDE_USER}@nvimide name: nvimide services: ts-nvimide: image: tailscale/tailscale:${TAILSCALE_VERSION:-v1.98.4} hostname: ${NVIMIDE_MAGIC_NAME:-nvimide} environment: TS_AUTHKEY: ${TS_OAUTH_CLIENT_SECRET} # NO ?ephemeral=true → stable node TS_EXTRA_ARGS: --advertise-tags=tag:nvimide TS_HOSTNAME: ${NVIMIDE_MAGIC_NAME:-nvimide} TS_ACCEPT_DNS: "true" TS_AUTH_ONCE: "true" TS_USERSPACE: "false" TS_ENABLE_HEALTH_CHECK: "true" TS_LOCAL_ADDR_PORT: "127.0.0.1:9009" dns: [1.1.1.1, 1.0.0.1] devices: - /dev/net/tun:/dev/net/tun cap_add: [NET_ADMIN, NET_RAW] healthcheck: # green only once the IDE's sshd is accepting on :22 in this netns test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:9009/healthz"] interval: 10s timeout: 15s retries: 6 start_period: 30s restart: unless-stopped nvimide: build: context: . args: USER_UID: ${UID:-1000} USER_GID: ${GID:-1000} image: nvimide:latest network_mode: "service:ts-nvimide" # share the sidecar's tailnet netns user: root # sshd needs root (privsep + bind :22); drops to the user on login environment: - TERM=xterm-256color - MOBILE=0 - NVIMIDE_USER=${NVIMIDE_USER:-dev} - NVIMIDE_PASSWORD=${NVIMIDE_PASSWORD:-} - NVIMIDE_SSH_PUBKEY=${NVIMIDE_SSH_PUBKEY:-} # entrypoint seeds /home, then exec's this: provision auth declaratively from # env (password enables sudo + fallback; pubkey is the real login), host # keys, sshd in foreground. command: - /bin/bash - -lc - | u="${NVIMIDE_USER:-dev}" [ -n "$$NVIMIDE_PASSWORD" ] && echo "$$u:$$NVIMIDE_PASSWORD" | chpasswd if [ -n "$$NVIMIDE_SSH_PUBKEY" ]; then install -d -o "$$u" -g "$$u" -m 700 "/home/$$u/.ssh" printf '%s\n' "$$NVIMIDE_SSH_PUBKEY" > "/home/$$u/.ssh/authorized_keys" chown "$$u:$$u" "/home/$$u/.ssh/authorized_keys"; chmod 600 "/home/$$u/.ssh/authorized_keys" fi # Persist host keys on the home volume so client known_hosts stays valid hk="/home/$$u/.ssh/host_keys" install -d -o "$$u" -g "$$u" -m 700 "$$hk" if [ ! -e "$$hk/ssh_host_ed25519_key" ]; then ssh-keygen -q -t ed25519 -f "$$hk/ssh_host_ed25519_key" -N "" ssh-keygen -q -t rsa -b 4096 -f "$$hk/ssh_host_rsa_key" -N "" fi exec /usr/bin/sshd -D -e \ -h "$$hk/ssh_host_ed25519_key" \ -h "$$hk/ssh_host_rsa_key" volumes: - nvimide-home:/home/dev # stateful home (roaming engine, history, dotfiles). Phase 2: rclone-sync to object storage. - nvimide-workspace:/workspace # sshfs roaming mount points land here cap_drop: [ALL] cap_add: - SETUID # sudo / sshd privsep - SETGID - AUDIT_WRITE - CHOWN - DAC_OVERRIDE - FOWNER - SYS_ADMIN # sshfs / FUSE roaming mounts - NET_BIND_SERVICE # sshd binds :22 - SYS_CHROOT # sshd privilege-separation chroot (/usr/share/empty.sshd) devices: - /dev/fuse:/dev/fuse security_opt: - apparmor:unconfined depends_on: ts-nvimide: condition: service_healthy restart: unless-stopped volumes: nvimide-home: external: true name: nvimide-ts_arch-dev-home nvimide-workspace: