# Vaultwarden Module Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Add Vaultwarden password manager as a NixOS module with Podman container, Nginx reverse proxy, and SOPS secrets. **Architecture:** Podman container running vaultwarden/server image with HTTP and WebSocket ports, integrated with existing Nginx module for reverse proxy and TLS, using SOPS for admin token secret management. **Tech Stack:** NixOS, Podman, Nginx, SOPS-nix, Vaultwarden --- ## File Structure | File | Purpose | |------|---------| | `modules/nginx.nix` | Modify - add `extraLocations` option for WebSocket support | | `modules/vaultwarden.nix` | New module - defines options and container config | | `modules/default.nix` | Add import for vaultwarden module | | `configuration.nix` | Enable module, declare SOPS secrets/templates | --- ### Task 1: Extend Nginx Module for Extra Locations **Files:** - Modify: `modules/nginx.nix` - [ ] **Step 1: Add `extraLocations` option to domain submodule** Add a new option after `extraConfig` (around line 46): ```nix extraLocations = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule { options = { proxyPass = lib.mkOption { type = lib.types.str; description = "Proxy target URL"; }; extraConfig = lib.mkOption { type = lib.types.lines; default = ""; description = "Extra Nginx config for this location"; }; }; }); default = { }; description = "Additional location blocks to add to this virtual host"; }; ``` - [ ] **Step 2: Update virtualHosts generation to include extraLocations** Replace the `virtualHosts` block (lines 76-87) with: ```nix virtualHosts = lib.mapAttrs' (domain: opts: { name = domain; value = { enableACME = true; forceSSL = true; locations = { "/" = { proxyPass = "http://127.0.0.1:${toString opts.port}"; extraConfig = opts.extraConfig; }; } // lib.mapAttrs' (locPath: locOpts: { name = locPath; value = { proxyPass = locOpts.proxyPass; extraConfig = locOpts.extraConfig; }; }) opts.extraLocations; }; }) cfg.domains; ``` - [ ] **Step 3: Verify syntax** Run: `nix-instantiate --parse modules/nginx.nix` Expected: No parse errors - [ ] **Step 4: Commit** ```bash git add modules/nginx.nix git commit -m "feat(nginx): add extraLocations option for WebSocket support Co-Authored-By: Claude Opus 4.6 " ``` --- ### Task 2: Create Vaultwarden Module File **Files:** - Create: `modules/vaultwarden.nix` - [ ] **Step 1: Create module file** ```nix # Vaultwarden Module (Podman) # Provides: Bitwarden-compatible password manager # # Usage: # myModules.vaultwarden = { # enable = true; # port = 8222; # websocketPort = 3012; # domain = "vault.example.com"; # }; { config, lib, pkgs, ... }: let cfg = config.myModules.vaultwarden; in { options.myModules.vaultwarden = { enable = lib.mkEnableOption "Vaultwarden password manager"; domain = lib.mkOption { type = lib.types.str; example = "vault.example.com"; description = "Public domain for Vaultwarden"; }; port = lib.mkOption { type = lib.types.port; default = 8222; description = "HTTP port for Vaultwarden web interface"; }; websocketPort = lib.mkOption { type = lib.types.port; default = 3012; description = "WebSocket port for real-time sync"; }; }; config = lib.mkIf cfg.enable { # Enable podman myModules.podman.enable = true; # Vaultwarden container virtualisation.oci-containers.containers."vaultwarden" = { image = "vaultwarden/server:latest"; ports = [ "127.0.0.1:${toString cfg.port}:80" "127.0.0.1:${toString cfg.websocketPort}:3012" ]; environmentFiles = [ config.sops.templates."vaultwarden.env".path ]; environment = { SHOW_PASSWORD_HINT = "false"; SIGNUPS_ALLOWED = "true"; }; volumes = [ "vaultwarden-data:/data" ]; }; # Register with Nginx (using extraLocations for WebSocket) myModules.nginx.domains."${cfg.domain}" = { port = cfg.port; extraLocations."/notifications/hub" = { proxyPass = "http://127.0.0.1:${toString cfg.websocketPort}"; extraConfig = '' proxyHttpVersion 1.1; proxySetHeader Upgrade $http_upgrade; proxySetHeader Connection "upgrade"; ''; }; }; }; } ``` - [ ] **Step 2: Verify file was created** Run: `cat modules/vaultwarden.nix | head -20` Expected: Module header and options visible - [ ] **Step 3: Commit module file** ```bash git add modules/vaultwarden.nix git commit -m "feat: add Vaultwarden module (Podman) Co-Authored-By: Claude Opus 4.6 " ``` --- ### Task 3: Register Module in default.nix **Files:** - Modify: `modules/default.nix` - [ ] **Step 1: Add vaultwarden import** Edit `modules/default.nix` to add `./vaultwarden.nix` to imports: ```nix # Module exports { imports = [ ./system.nix ./podman.nix ./nginx.nix ./searxng.nix ./openclaw-podman.nix ./vaultwarden.nix ]; } ``` - [ ] **Step 2: Verify syntax** Run: `nix-instantiate --parse modules/default.nix` Expected: No parse errors - [ ] **Step 3: Commit** ```bash git add modules/default.nix git commit -m "feat: register Vaultwarden module in default.nix Co-Authored-By: Claude Opus 4.6 " ``` --- ### Task 4: Configure Vaultwarden in configuration.nix **Files:** - Modify: `configuration.nix` - [ ] **Step 1: Add Vaultwarden module configuration** Add after the OpenClaw section (around line 95): ```nix # === Vaultwarden === myModules.vaultwarden = { enable = true; port = 8222; websocketPort = 3012; domain = "vault.ashisgreat.xyz"; }; ``` - [ ] **Step 2: Add SOPS secret declaration** Add after the OpenClaw secrets (around line 100): ```nix sops.secrets.vaultwarden_admin_token = { }; ``` - [ ] **Step 3: Add SOPS template** Add after the openclaw.env template (around line 108): ```nix sops.templates."vaultwarden.env" = { content = '' ADMIN_TOKEN=${config.sops.placeholder.vaultwarden_admin_token} ''; }; ``` - [ ] **Step 4: Verify configuration builds** Run: `nixos-rebuild build --flake .#nixos` Expected: Build succeeds without errors - [ ] **Step 5: Commit** ```bash git add configuration.nix git commit -m "feat: enable Vaultwarden with secrets configuration Co-Authored-By: Claude Opus 4.6 " ``` --- ### Task 5: Add Admin Token Secret (Manual) **Files:** - Modify: `secrets/secrets.yaml` (encrypted) - [ ] **Step 1: Edit secrets file** Run: `nix-shell -p sops --run "sops secrets/secrets.yaml"` - [ ] **Step 2: Add vaultwarden_admin_token** Add this entry to the YAML file: ```yaml vaultwarden_admin_token: "your-secure-admin-token-here" ``` Replace `your-secure-admin-token-here` with a strong password/token. - [ ] **Step 3: Save and exit** The file will be re-encrypted automatically. --- ### Task 6: Deploy and Verify - [ ] **Step 1: Deploy configuration** Run: `sudo nixos-rebuild switch --flake .#nixos` Expected: Deployment succeeds - [ ] **Step 2: Verify container is running** Run: `sudo podman ps | grep vaultwarden` Expected: Container listed as running - [ ] **Step 3: Verify web interface** Run: `curl -I http://127.0.0.1:8222` Expected: HTTP 200 response - [ ] **Step 4: Final commit (if any changes)** ```bash git status # If secrets.yaml was modified (git tracks encrypted version): git add secrets/secrets.yaml git commit -m "chore: add Vaultwarden admin token secret Co-Authored-By: Claude Opus 4.6 " ``` --- ## Summary After completing all tasks: 1. Nginx module supports extra locations for WebSocket endpoints 2. Vaultwarden will be accessible at `https://vault.ashisgreat.xyz` 3. WebSocket sync enabled for real-time updates 4. Admin panel accessible at `/admin` with the configured token 5. SQLite database persisted in Podman volume `vaultwarden-data`