diff --git a/README.md b/README.md index 70b6a25..91f47cf 100644 --- a/README.md +++ b/README.md @@ -49,14 +49,20 @@ just fmt ## layout ``` -hosts/darwin/ - macOS host entrypoint -hosts/netty/ - NixOS VPS entrypoint (disko + hardware) -modules/ - shared system modules + devshells -modules/hosts/ - flake-parts host output definitions -modules/nixpkgs.nix - shared flake context (hosts, args, pkgs helpers) -home/ - Home Manager modules -lib/hosts.nix - host metadata used by the flake -lib/ - shared package sets and theme system -config/ - repo-owned config files (nvim, tmux, etc.) -scripts/ - secret management and utility scripts +hosts/darwin/ - macOS host entrypoint +hosts/netty/ - NixOS VPS entrypoint (disko + hardware + services) +modules/ - shared system modules + devshells +modules/hosts/ - flake-parts host output definitions +modules/nixpkgs.nix - shared flake context (hosts, specialArgs, pkgs) +home/default.nix - unified home entry (conditional on hostConfig) +home/common.nix - modules shared across all hosts +home/xdg.nix - XDG compliance (env vars, config files) +home/security.nix - SSH/GPG permission enforcement +home/ - per-tool home-manager modules +lib/hosts.nix - host metadata + feature flags +lib/theme.nix - centralized color system (gruvbox) +lib/package-sets.nix - shared + host-gated package lists +config/ - repo-owned config files (nvim, tmux, etc.) +scripts/ - secret management and utility scripts +nix-maxxing.txt - architecture and operations guide ``` diff --git a/hosts/netty/configuration.nix b/hosts/netty/configuration.nix index a1492c2..ec08f18 100644 --- a/hosts/netty/configuration.nix +++ b/hosts/netty/configuration.nix @@ -102,12 +102,166 @@ in environment.systemPackages = packageSets.extras ++ [ pkgs.bubblewrap pkgs.pnpm + pkgs.nodejs ]; systemd.tmpfiles.rules = [ "L /usr/bin/bwrap - - - - ${pkgs.bubblewrap}/bin/bwrap" ]; + # --- ACME / Let's Encrypt --- + security.acme = { + acceptTerms = true; + defaults.email = "rathiharivansh@gmail.com"; + }; + + # --- Nginx reverse proxy --- + services.nginx = { + enable = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + clientMaxBodySize = "512m"; + + virtualHosts."sandbox.example.dev" = { + enableACME = true; + forceSSL = true; + locations."/".proxyPass = "http://127.0.0.1:2470"; + }; + + virtualHosts."git.example.dev" = { + enableACME = true; + forceSSL = true; + locations."/".proxyPass = "http://127.0.0.1:3000"; + }; + }; + + # --- Forgejo --- + users.users.git = { + isSystemUser = true; + home = "/var/lib/forgejo"; + group = "git"; + shell = "${pkgs.bash}/bin/bash"; + }; + users.groups.git = { }; + + services.forgejo = { + enable = true; + user = "git"; + group = "git"; + settings = { + server = { + DOMAIN = "git.example.dev"; + ROOT_URL = "https://git.example.dev/"; + HTTP_PORT = 3000; + SSH_DOMAIN = "git.example.dev"; + }; + service.DISABLE_REGISTRATION = true; + session.COOKIE_SECURE = true; + mirror = { + DEFAULT_INTERVAL = "1h"; + MIN_INTERVAL = "10m"; + }; + }; + }; + + # --- Forgejo mirror sync (hourly) --- + systemd.services.forgejo-mirror-sync = { + description = "Sync GitHub mirrors to Forgejo"; + after = [ "forgejo.service" ]; + requires = [ "forgejo.service" ]; + serviceConfig = { + Type = "oneshot"; + EnvironmentFile = "/etc/forgejo-mirror.env"; + }; + path = [ pkgs.curl pkgs.jq pkgs.coreutils ]; + script = '' + set -euo pipefail + + # Fetch all GitHub repos + page=1 + repos="" + while true; do + batch=$(curl -sf -H "Authorization: token $GITHUB_TOKEN" \ + "https://api.github.com/user/repos?per_page=100&page=$page&affiliation=owner") + count=$(echo "$batch" | jq length) + [ "$count" -eq 0 ] && break + repos="$repos$batch" + page=$((page + 1)) + done + + echo "$repos" | jq -r '.[].clone_url' | while read -r clone_url; do + repo_name=$(basename "$clone_url" .git) + + # Check if mirror already exists in Forgejo + status=$(curl -sf -o /dev/null -w '%{http_code}' \ + -H "Authorization: token $FORGEJO_TOKEN" \ + "$FORGEJO_URL/api/v1/repos/$FORGEJO_OWNER/$repo_name") + + if [ "$status" = "404" ]; then + # Create mirror + curl -sf -X POST \ + -H "Authorization: token $FORGEJO_TOKEN" \ + -H "Content-Type: application/json" \ + "$FORGEJO_URL/api/v1/repos/migrate" \ + -d "{ + \"clone_addr\": \"$clone_url\", + \"auth_token\": \"$GITHUB_TOKEN\", + \"uid\": $(curl -sf -H "Authorization: token $FORGEJO_TOKEN" "$FORGEJO_URL/api/v1/user" | jq .id), + \"repo_name\": \"$repo_name\", + \"mirror\": true, + \"service\": \"github\" + }" + echo "Created mirror: $repo_name" + else + # Trigger sync on existing mirror + curl -sf -X POST \ + -H "Authorization: token $FORGEJO_TOKEN" \ + "$FORGEJO_URL/api/v1/repos/$FORGEJO_OWNER/$repo_name/mirror-sync" || true + echo "Synced mirror: $repo_name" + fi + done + ''; + }; + + systemd.timers.forgejo-mirror-sync = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = "hourly"; + Persistent = true; + RandomizedDelaySec = "5m"; + }; + }; + + # --- Sandbox Agent (declarative systemd services) --- + systemd.services.sandbox-agent = { + description = "Sandbox Agent"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "simple"; + User = username; + Group = "users"; + EnvironmentFile = "/home/${username}/.config/sandbox-agent/agent.env"; + ExecStart = "/home/${username}/.local/bin/sandbox-agent"; + Restart = "on-failure"; + RestartSec = 5; + }; + }; + + systemd.services.sandbox-cors-proxy = { + description = "Sandbox CORS Proxy"; + after = [ "sandbox-agent.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "simple"; + User = username; + Group = "users"; + ExecStart = "${pkgs.nodejs}/bin/node /home/${username}/.config/sandbox-agent/cors-proxy.js"; + Restart = "on-failure"; + RestartSec = 5; + }; + }; + system.configurationRevision = self.rev or self.dirtyRev or null; system.stateVersion = "24.11"; } diff --git a/nix-maxxing.txt b/nix-maxxing.txt index e69de29..d2683ce 100644 --- a/nix-maxxing.txt +++ b/nix-maxxing.txt @@ -0,0 +1,171 @@ +Nix Config - Architecture and Operations Guide +================================================ + +1. WHY STATIC IP (NOT DHCP) +---------------------------- +DHCP on a VPS is dangerous. If the DHCP lease expires or the server +reboots while the DHCP server is unreachable, the machine loses its IP +and becomes inaccessible via SSH. This happened on netty (2026-03-30). + +Static config in hosts/netty/configuration.nix: + - IP: 152.53.195.59/22 + - Gateway: 152.53.192.1 + - Interface: ens3 + - DNS: 1.1.1.1, 8.8.8.8 + +Always verify the interface name with `ip link show` before changing +network config. Keep VNC console access available as a fallback. + + +2. HOST ABSTRACTION (hostConfig) +--------------------------------- +lib/hosts.nix defines each machine with: + - isDarwin / isLinux / isNixOS booleans + - features map (rust, go, node, python, aws, claude, docker, tex) + +modules/nixpkgs.nix passes hostConfig via specialArgs so all home-manager +modules can use it. This replaces scattered `pkgs.stdenv.isDarwin` checks. + +To add a new host: + 1. Add entry to lib/hosts.nix with all fields + 2. Create hosts//configuration.nix (NixOS) or add darwin case + 3. Add host output in modules/hosts/.nix + 4. home/default.nix auto-selects modules based on hostConfig flags + +home/default.nix is the unified entry point - no separate per-host home +modules needed. + + +3. XDG COMPLIANCE +------------------ +home/xdg.nix sets environment variables so tools respect XDG dirs: + + CARGO_HOME -> $XDG_DATA_HOME/cargo + RUSTUP_HOME -> $XDG_DATA_HOME/rustup + GOPATH -> $XDG_DATA_HOME/go + GOMODCACHE -> $XDG_CACHE_HOME/go/mod + NPM_CONFIG_USERCONFIG -> $XDG_CONFIG_HOME/npm/npmrc + NODE_REPL_HISTORY -> $XDG_STATE_HOME/node_repl_history + PYTHON_HISTORY -> $XDG_STATE_HOME/python_history + AWS_CONFIG_FILE -> $XDG_CONFIG_HOME/aws/config + DOCKER_CONFIG -> $XDG_CONFIG_HOME/docker + CLAUDE_CONFIG_DIR -> $XDG_CONFIG_HOME/claude + PSQL_HISTORY -> $XDG_STATE_HOME/psql_history + SQLITE_HISTORY -> $XDG_STATE_HOME/sqlite_history + LESSHISTFILE -> "-" (disabled) + +All gated by hostConfig.features so tools only get configured when +the feature flag is set for that host. + + +4. SECURITY MODULE +------------------- +home/security.nix runs activation scripts on every `home-manager switch`: + - ~/.ssh/ dir: 700, private keys: 600, pub/known_hosts/config: 644 + - ~/.gnupg/ dirs: 700, files: 600 + +No manual chmod needed after restoring keys from Bitwarden. + + +5. THEME SYSTEM +---------------- +lib/theme.nix is the single source of truth for colors. + +Shared palette (gruvbox-inspired) used across: + - Ghostty terminal (renderGhostty) + - Tmux status bar (renderTmux) + - fzf color scheme (renderFzf) + - Zsh syntax highlighting (renderZshHighlights) + - Bat (batTheme) + - Git delta (deltaTheme) + +Runtime toggle: `theme toggle` writes "light" or "dark" to +$XDG_STATE_HOME/theme/current, then updates Ghostty, tmux, fzf, +and Neovim (via RPC) live. Bat and delta are static at build time. + + +6. SHELL SETUP +--------------- +oh-my-zsh with agnoster theme (powerline arrows + git branch coloring). +Vim mode via defaultKeymap = "viins" with cursor shape switching +(beam for insert, block for normal). + +History: 50k entries, dedup, ignoreSpace, extended format, stored at +$XDG_STATE_HOME/zsh_history. + +zoxide: declarative via programs.zoxide (no manual eval). + +PATH: managed via home.sessionPath in xdg.nix + initContent block +in zsh.nix for entries that need conditional logic. + + +7. SERVER SERVICES (netty) +--------------------------- +All in hosts/netty/configuration.nix: + +Nginx reverse proxy with ACME SSL: + - sandbox.example.dev -> 127.0.0.1:2470 (sandbox agent) + - git.example.dev -> 127.0.0.1:3000 (forgejo) + +Forgejo: + - Self-hosted git, registration disabled + - Runs as git user on port 3000 + - GitHub mirror sync via hourly systemd timer + - Requires /etc/forgejo-mirror.env with GITHUB_TOKEN, FORGEJO_TOKEN, + FORGEJO_URL, FORGEJO_OWNER + +Sandbox Agent: + - System-level systemd services (not user units) + - sandbox-agent on :2470, env from ~/.config/sandbox-agent/agent.env + - sandbox-cors-proxy on :2468 (Node.js) + - No cloudflared - nginx handles SSL termination + +Garbage collection: 3-day retention (vs 14-day on darwin). +Disk guards: min-free 100MB, max-free 1GB. +Journald: 1-week retention. + + +8. DEPLOY COMMANDS +------------------- +Darwin (local): + just switch + +Netty (from mac): + just switch-netty + +First-time netty install: + nix run github:nix-community/nixos-anywhere -- \ + --flake .#netty --target-host netty --build-on-remote + + +9. ROLLBACK +------------- +Each phase is a separate git commit. + +NixOS rollback: + ssh netty "nixos-rebuild switch --rollback" + +Or boot previous generation from GRUB (3 kept). + +Darwin rollback: + git revert && just switch + +Home Manager rollback: + home-manager generations # list + home-manager switch --flake .# # after git revert + + +10. FEATURE FLAGS REFERENCE +----------------------------- +| Feature | darwin | netty | +|---------|--------|-------| +| rust | yes | yes | +| go | yes | yes | +| node | yes | yes | +| python | yes | yes | +| aws | yes | yes | +| claude | yes | yes | +| docker | yes | no | +| tex | yes | no | + +Set in lib/hosts.nix, consumed by home/xdg.nix and lib/package-sets.nix.