From b6230a1d291993c5d8336cb4102f9baaf4bded92 Mon Sep 17 00:00:00 2001 From: 0xrsydn Date: Wed, 22 Oct 2025 13:38:59 +0700 Subject: [PATCH] fix: dev vm setup --- AGENTS.md | 42 ++++++++------- flake.nix | 7 ++- modules/home/rsydn/base.nix | 3 +- modules/nixos/containerization.nix | 85 ++++++++++++++++++++++++++++++ modules/nixos/hosts/dev-vm.nix | 32 +++++++++++ modules/nixos/network.nix | 4 ++ modules/nixos/ssh.nix | 17 ++++++ modules/nixos/system.nix | 18 ++++--- modules/nixos/users.nix | 10 ++++ modules/nixos/virtualization.nix | 31 +++++++++++ 10 files changed, 218 insertions(+), 31 deletions(-) create mode 100644 modules/nixos/containerization.nix create mode 100644 modules/nixos/hosts/dev-vm.nix create mode 100644 modules/nixos/network.nix create mode 100644 modules/nixos/ssh.nix create mode 100644 modules/nixos/users.nix create mode 100644 modules/nixos/virtualization.nix diff --git a/AGENTS.md b/AGENTS.md index 7dd30c3..70c4c94 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,33 +1,35 @@ # Repository Guidelines ## Project Structure & Module Organization -- `flake.nix`/`flake.lock` drive the entire nix-darwin + Home Manager configuration; `macbook-pro` is the only declared host. -- `modules/darwin/` contains device-level modules (`system.nix`, `homebrew.nix`, `devtools.nix`). Keep OS services and hardware toggles here. -- `modules/home/rsydn/` provides the Home Manager profile. Subdirectories `programs/`, `shell/`, and `devtools/` house user apps, Nushell/theming, and language/AI bundles. -- Secrets belong under `secrets/*.sops.yaml`; decrypted payloads are emitted into `~/.config/secrets/` at activation. The cached build outputs in `.cache/` stay untracked. +- `flake.nix` defines shared helpers plus Darwin (`macbook-pro`) and NixOS (`dev-vm`) hosts; `flake.lock` pins inputs. +- `modules/darwin/` holds macOS system modules; `modules/nixos/` mirrors that for Linux (base `system.nix` imports `users.nix`, `network.nix`, `ssh.nix`, `containerization.nix`). +- Host overlays live under `modules/nixos/hosts/`—e.g., `dev-vm.nix` adds virtio tooling and host-only packages. +- User configuration is in `modules/home/rsydn/` with subdirectories for `programs/`, `shell/`, and `devtools/`; Darwin layers on `shell/nushell.nix` while Linux sticks to `shell/fish.nix`. +- Secrets stay in `secrets/*.sops.yaml`; decrypted files appear under `~/.config/secrets/` at activation and must not be committed. ## Build, Test & Development Commands -- `nix develop` — enter the flake dev shell with `git`, `nixfmt-classic`, and Age/SOPS helpers. -- `nix fmt` — format every Nix file via the flake’s formatter; run after edits to modules or overlays. -- `XDG_CACHE_HOME=$PWD/.cache nix flake check` — lint and type-check all modules without touching the global cache. -- `darwin-rebuild --dry-run --flake .#macbook-pro` — validate activation steps and surface breaking changes. -- `darwin-rebuild switch --flake .#macbook-pro` — apply the configuration once checks succeed. +- `nix develop` – enter the flake dev shell with `git`, `nixfmt-classic`, SOPS/Age helpers. +- `nix fmt` – format all Nix sources; run before commits touching modules or overlays. +- `XDG_CACHE_HOME=$PWD/.cache nix flake check` – lint and eval every host without polluting the global cache. +- `darwin-rebuild --dry-run --flake .#macbook-pro` / `darwin-rebuild switch --flake .#macbook-pro` – preview or apply macOS changes. +- `nix build .#nixosConfigurations.dev-vm.config.system.build.toplevel` – make sure the Linux VM evaluates and builds before switching. ## Coding Style & Naming Conventions -- Nix sources use two-space indents, trailing commas, and lower-kebab filenames (`shell/nushell.nix`). -- Custom options live in the `rsydn.*` namespace (`rsydn.aiTools`, `rsydn.devTools`). Extend existing option sets instead of scattering ad-hoc attributes. -- Prefer declarative package toggles over ad-hoc installers; Nushell, AI CLIs, and prompts are all Home Manager managed. +- Use two-space indentation, trailing commas, and lower-kebab filenames (`shell/nushell.nix`). +- Declare custom options under the `rsydn.*` namespace (e.g., `rsydn.containerization`, `rsydn.devTools`). +- Prefer declarative package toggles and shared modules over ad-hoc host tweaks; keep host-specific overrides in `hosts/`. ## Testing Guidelines -- Run `nix flake check` before every PR or local switch; treat failures as blockers. -- Capture key outputs (e.g., `darwin-rebuild --dry-run`) for substantial module changes and share in review threads. -- If you add reusable logic, colocate regression tests beside the module using the `.nix` pattern (e.g., `rsydn-ai-tools.nix`). +- Treat `nix flake check` as mandatory before PRs or `switch` operations. +- Capture key activation output: `darwin-rebuild --dry-run` for macOS, `nix build .#nixosConfigurations.dev-vm…` for the VM, and attach summaries in reviews. +- Co-locate regression tests with the option/module they guard using the `.nix` pattern when practical. ## Commit & Pull Request Guidelines -- Use short, imperative commit subjects (`add nushell env path hook`); keep each commit scoped to one logical change. -- Document validation steps in the PR body (`nix flake check`, dry-run logs) and reference related issues or TODOs. -- Include screenshots or terminal snippets when modifying shells, prompts, or visual tooling to show the resulting UI. +- Write short, imperative subjects (`add dev-vm virtualization module`); keep each commit scoped to one change. +- In PRs, list validation steps (`nix flake check`, host-specific builds) and link related issues or TODOs. +- Provide screenshots or terminal snippets when altering shell prompts, Tailscale/SSH flows, or other UX-facing pieces. ## Security & Configuration Tips -- Age keys live at `~/.config/sops/age/keys.txt`; Home Manager generates them if absent. Do not commit decrypted files from `~/.config/secrets/`. -- To access a secret, read the managed file (e.g., `open ~/.config/secrets/openai-api-key | str trim`) and scope the value with `with-env` instead of exporting it globally. +- Age keys live at `~/.config/sops/age/keys.txt`; regenerate via Home Manager if missing. +- Access secrets by reading the managed files (`open ~/.config/secrets/openai-api-key | str trim`) and scope them with `with-env` instead of exporting globally. +- Tailscale and OpenSSH run by default on Linux; rotate auth keys regularly and audit `services.tailscale.extraUpFlags` when enabling exit nodes. diff --git a/flake.nix b/flake.nix index ad02f5a..3c84956 100644 --- a/flake.nix +++ b/flake.nix @@ -94,7 +94,12 @@ }; in { darwinConfigurations = { macbook-pro = mkDarwin { }; }; - nixosConfigurations = { }; + nixosConfigurations = { + dev-vm = mkNixos { + system = "x86_64-linux"; + extraModules = [ ./modules/nixos/hosts/dev-vm.nix ]; + }; + }; devShells = forEachSystem (system: let diff --git a/modules/home/rsydn/base.nix b/modules/home/rsydn/base.nix index bd8e61a..66951cb 100644 --- a/modules/home/rsydn/base.nix +++ b/modules/home/rsydn/base.nix @@ -11,8 +11,6 @@ ./secrets.nix ]; - programs.nushell.enable = true; - programs.git.enable = true; home.packages = with pkgs; [ docker docker-compose ]; @@ -39,6 +37,7 @@ ripgrep tmux tree + curl vim wget yq diff --git a/modules/nixos/containerization.nix b/modules/nixos/containerization.nix new file mode 100644 index 0000000..909268e --- /dev/null +++ b/modules/nixos/containerization.nix @@ -0,0 +1,85 @@ +{ config, lib, pkgs, ... }: +let + inherit (lib) mkEnableOption mkOption mkIf types mkMerge mkAfter optionals; + cfg = config.rsydn.containerization; +in { + options.rsydn.containerization = { + docker = { + enable = mkOption { + type = types.bool; + default = true; + description = + "Enable the Docker daemon with sane defaults for development."; + }; + + autoPrune = mkOption { + type = types.submodule { + options = { + enable = mkOption { + type = types.bool; + default = true; + description = + "Automatically prune unused Docker data on a schedule."; + }; + dates = mkOption { + type = types.str; + default = "weekly"; + description = + "Systemd calendar expression for docker system prune."; + }; + flags = mkOption { + type = types.listOf types.str; + default = [ "--all" "--volumes" ]; + description = "Extra flags passed to docker system prune."; + }; + }; + }; + default = { }; + description = "Settings for automatic Docker pruning."; + }; + }; + + k3s = { + enable = + mkEnableOption "Run a lightweight Kubernetes control plane with k3s."; + + role = mkOption { + type = types.enum [ "server" "agent" ]; + default = "server"; + description = "Choose whether this node runs as a k3s server or agent."; + }; + + extraFlags = mkOption { + type = types.listOf types.str; + default = [ "--write-kubeconfig-mode=0644" ]; + description = "Additional flags to pass to the k3s service."; + }; + }; + }; + + config = mkMerge [ + (mkIf cfg.docker.enable { + virtualisation.docker = { + enable = true; + package = pkgs.docker; + enableOnBoot = true; + autoPrune = { + enable = cfg.docker.autoPrune.enable; + dates = cfg.docker.autoPrune.dates; + flags = cfg.docker.autoPrune.flags; + }; + }; + }) + + (mkIf cfg.k3s.enable { + services.k3s = { + enable = true; + role = cfg.k3s.role; + extraFlags = cfg.k3s.extraFlags; + }; + + networking.firewall.allowedTCPPorts = + mkAfter (optionals (cfg.k3s.role == "server") [ 6443 ]); + }) + ]; +} diff --git a/modules/nixos/hosts/dev-vm.nix b/modules/nixos/hosts/dev-vm.nix new file mode 100644 index 0000000..7576d7e --- /dev/null +++ b/modules/nixos/hosts/dev-vm.nix @@ -0,0 +1,32 @@ +{ lib, pkgs, ... }: { + networking.hostName = "dev-vm"; + # Set the baseline NixOS release used for stateful data; bump on rebuilds. + system.stateVersion = lib.mkDefault "24.05"; + + # Boot loader configuration for VM + boot.loader.grub.enable = lib.mkDefault true; + boot.loader.grub.device = lib.mkDefault "/dev/vda"; + + # Root filesystem configuration for VM + fileSystems."/" = lib.mkDefault { + device = "/dev/vda1"; + fsType = "ext4"; + }; + + environment.systemPackages = with pkgs; [ + coreutils + ast-grep + ripgrep + bat + tree + zip + unzip + htop + jq + psmisc # provides killall and friends + gh + tree-sitter + ]; + + imports = [ ../virtualization.nix ]; +} diff --git a/modules/nixos/network.nix b/modules/nixos/network.nix new file mode 100644 index 0000000..a5933c2 --- /dev/null +++ b/modules/nixos/network.nix @@ -0,0 +1,4 @@ +{ lib, ... }: { + networking.useDHCP = lib.mkDefault true; + networking.networkmanager.enable = lib.mkDefault true; +} diff --git a/modules/nixos/ssh.nix b/modules/nixos/ssh.nix new file mode 100644 index 0000000..efb8028 --- /dev/null +++ b/modules/nixos/ssh.nix @@ -0,0 +1,17 @@ +{ lib, ... }: { + services.openssh = { + enable = lib.mkDefault true; + openFirewall = lib.mkDefault true; + settings = { + PermitRootLogin = "no"; + PasswordAuthentication = false; + }; + }; + + # Run tailscale so the VM can be reached over the mesh network for SSH. + services.tailscale = { + enable = lib.mkDefault true; + useRoutingFeatures = lib.mkDefault "client"; + extraUpFlags = lib.mkDefault [ "--ssh" ]; + }; +} diff --git a/modules/nixos/system.nix b/modules/nixos/system.nix index 74a6cc2..217695e 100644 --- a/modules/nixos/system.nix +++ b/modules/nixos/system.nix @@ -1,10 +1,12 @@ -{ config, pkgs, lib, user, ... }: { - users.users.${user} = { - isNormalUser = lib.mkDefault true; - extraGroups = lib.mkDefault [ "wheel" "networkmanager" ]; - home = lib.mkDefault "/home/${user}"; - shell = lib.mkDefault pkgs.fish; - }; +{ lib, ... }: { + imports = [ ./users.nix ./network.nix ./ssh.nix ./containerization.nix ]; - programs.fish.enable = lib.mkDefault true; + nix = { + settings.auto-optimise-store = lib.mkDefault true; + optimise.automatic = lib.mkDefault true; + gc = { + automatic = lib.mkDefault true; + dates = lib.mkDefault "weekly"; + }; + }; } diff --git a/modules/nixos/users.nix b/modules/nixos/users.nix new file mode 100644 index 0000000..820290e --- /dev/null +++ b/modules/nixos/users.nix @@ -0,0 +1,10 @@ +{ lib, pkgs, user, ... }: { + users.users.${user} = { + isNormalUser = lib.mkDefault true; + extraGroups = lib.mkDefault [ "wheel" "networkmanager" "docker" ]; + home = lib.mkDefault "/home/${user}"; + shell = pkgs.fish; + }; + + programs.fish.enable = lib.mkDefault true; +} diff --git a/modules/nixos/virtualization.nix b/modules/nixos/virtualization.nix new file mode 100644 index 0000000..73afbad --- /dev/null +++ b/modules/nixos/virtualization.nix @@ -0,0 +1,31 @@ +{ lib, pkgs, ... }: { + # QEMU guest agent provides graceful shutdowns and metadata exchange. + # Disabled by default - enable in host overlay if needed. + services.qemuGuest.enable = lib.mkDefault false; + + # Spice vdagent handles clipboard sync, display resize, and mouse integration. + services.spice-vdagentd.enable = lib.mkDefault true; + + # Keep the VM clock in sync if timesyncd is not already enabled elsewhere. + services.timesyncd.enable = lib.mkDefault true; + + # Ensure essential virtio drivers are available early during boot. + boot.initrd.kernelModules = lib.mkDefault [ + "virtio_pci" + "virtio_blk" + "virtio_net" + "virtio_scsi" + "virtio_mmio" + ]; + + boot.kernelModules = + lib.mkDefault [ "virtio_balloon" "virtio_console" "virtio_rng" ]; + + # Provide udev rules to improve virtio device behaviour. + services.udev.extraRules = lib.mkDefault '' + ACTION=="add", SUBSYSTEM=="virtio", ATTR{device/driver_override}="" + ''; + + # Pull in a few helper tools useful when tuning the guest. + environment.systemPackages = with pkgs; [ spice-vdagent ]; +}