diff --git a/DEPLOYING-NIXOS.md b/DEPLOYING-NIXOS.md new file mode 100644 index 00000000..e498b95e --- /dev/null +++ b/DEPLOYING-NIXOS.md @@ -0,0 +1,194 @@ +# Deploying nanobot on NixOS + +This documents what it took to get nanobot running on Camellia (NixOS 26.05). + +See `/etc/nixos/configuration.nix` for the full config — every block below has a matching section there. + +## 1. Base system + +```nix +# Hostname, timezone, SSH, fail2ban +networking.hostName = "Camellia"; +time.timeZone = "Asia/Shanghai"; +services.openssh = { enable = true; ... }; +services.fail2ban = { enable = true; }; +``` + +## 2. Required packages + +```nix +environment.systemPackages = with pkgs; [ + git curl wget vim tmux htop jq fd ripgrep + python313 uv # nanobot runtime + nodejs_22 # for some skills/tools +]; +``` + +## 3. Users + +Create a dedicated user for nanobot with lingering enabled: + +```nix +users.users.claw = { + extraGroups = ["sudo"]; + isNormalUser = true; + linger = true; # user services survive logout +}; +security.sudo.extraRules = [ + { users = ["claw"]; commands = [{ command = "ALL"; options = ["NOPASSWD"]; }]; } +]; +``` + +## 4. FHS symlinks + +nanobot's `exec` tool (and many scripts) expect `/bin/bash`: + +```nix +systemd.tmpfiles.rules = [ + "L+ /bin/bash - - - - /run/current-system/sw/bin/bash" + "L+ /usr/bin/env - - - - /run/current-system/sw/bin/env" +]; +``` + +## 5. nanobot systemd user service + +```nix +systemd.user.services.nanobot = { + description = "nanobot AI assistant gateway"; + wantedBy = ["default.target"]; + serviceConfig = { + Type = "simple"; + ExecStart = "/home/claw/nanobot-src/.venv/bin/python -m nanobot gateway --foreground --port 18790"; + Restart = "always"; + RestartSec = 5; + WorkingDirectory = "/home/claw/.nanobot/workspace"; + Environment = "HOME=/home/claw"; + }; +}; +``` + +Use a git checkout (`/home/claw/nanobot-src`) so you can `git checkout known-good` to rollback. + +## 6. Watchdog timer + +Covers the case where systemd misses a crash: + +```nix +systemd.user.services.nanobot-watchdog = { + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.systemd}/bin/systemctl --user start nanobot"; + }; +}; +systemd.user.timers.nanobot-watchdog = { + timerConfig = { OnUnitActiveSec = "60s"; }; +}; +``` + +## 7. Reverse proxy (Caddy) + +nanobot's gateway binds to `127.0.0.1` by default and rejects non-local connections. For a dedicated VM this is over-cautious, so we use Caddy to reverse-proxy the WebSocket UI externally: + +```nix +services.caddy = { + enable = true; + virtualHosts."http://192.168.122.194".extraConfig = '' + reverse_proxy 127.0.0.1:8765 + ''; +}; +``` + +## 8. Proxy for outbound traffic (v2rayA) + +If the LLM API and Telegram are blocked: + +```nix +services.v2raya = { enable = true; }; +``` + +Then configure proxy env vars in `~/.bashrc`: + +```bash +export http_proxy=http://127.0.0.1:20171 +export https_proxy=http://127.0.0.1:20171 +export all_proxy=socks5://127.0.0.1:20170 +``` + +## 9. OOM protection + +nanobot can spike memory. Without this, the kernel OOM killer takes it down: + +```nix +services.earlyoom = { + enable = true; + freeMemThreshold = 5; + freeSwapThreshold = 10; +}; +swapDevices = [{ device = "/swapfile"; size = 2048; }]; # 2 GB +``` + +## 10. Firewall + +```nix +networking.firewall.allowedTCPPorts = [ + 22 # SSH + 80 # Caddy HTTP + 2017 # v2rayA web UI + 20170 # v2rayA SOCKS5 + 20171 # v2rayA HTTP proxy +]; +``` + +## 11. Kernel hardening + +```nix +boot.kernel.sysctl = { + "net.ipv4.tcp_syncookies" = 1; + "net.ipv4.conf.all.rp_filter" = 1; + "net.ipv4.conf.all.accept_redirects" = 0; + "net.ipv6.conf.all.accept_redirects" = 0; + "kernel.kptr_restrict" = 1; + "kernel.dmesg_restrict" = 1; +}; +``` + +## 12. Nix GC + +```nix +nix.gc = { automatic = true; dates = "weekly"; options = "--delete-older-than 30d"; }; +``` + +## 13. Mirrors (for China) + +```nix +nix.settings.substituters = ["https://mirrors.tuna.tsinghua.edu.cn/nix-channels/store"]; +``` + +## 14. Install nanobot itself + +```bash +# As claw user +git clone https://github.com/HKUDS/nanobot.git ~/nanobot-src +cd ~/nanobot-src +uv sync +uv run nanobot onboard # interactive setup wizard +# Populate workspace: SOUL.md, USER.md, memory/MEMORY.md +``` + +## 15. Apply + +Use the safe-switch wrapper to rebuild with health checks: + +```bash +sudo /home/claw/.nanobot/workspace/scripts/safe-switch.sh +``` + +This runs `nixos-rebuild switch`, waits, then verifies the gateway is reachable. +If the health check fails, it automatically rolls back. + +## Known quirks + +- **sudo path**: Use `/run/wrappers/bin/sudo` inside scripts, not the default PATH's `/run/current-system/sw/bin/sudo` (lacks setuid). +- **systemctl from cron/timers**: Need `XDG_RUNTIME_DIR=/run/user/1000 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus`. +- **Gateway self-restart**: Never `systemctl --user restart nanobot` from within nanobot's own process — it kills the parent. Use `nohup bash -c 'sleep 2 && ...' &`. +- **Rollback**: `cd ~/nanobot-src && git checkout known-good && systemctl --user restart nanobot`.