diff --git a/.gitignore b/.gitignore index 24f5739..8369981 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,11 @@ unified-router/ unified_router-nodejs/ .agent/ old/ +openclaw/ + +# Nix build outputs +result +result-* + +# AI assistant artifacts +CLAUDE.md diff --git a/README.md b/README.md index 5718aab..49fe700 100644 --- a/README.md +++ b/README.md @@ -1,319 +1,116 @@ # NixOS Configuration -Personal NixOS configuration with Hyprland, containerized services, and security hardening. +A modular, security-hardened NixOS flake featuring multiple desktop environments (Niri, Cosmic), sophisticated application sandboxing via `nix-bwrapper`, and a containerized service ecosystem. -> **Note:** Parts of this configuration were created with the assistance of AI tools. +## 🛡️ Core Pillars -## Quick Start +- **Security Hardening**: Aggressive kernel parameters, DNS-over-TLS, AppArmor, and an `nftables` firewall with Cloudflare-specific rules. +- **Application Sandboxing**: Granular isolation for browsers, games, and proprietary apps using `bubblewrap` via a custom `nix-bwrapper` framework. +- **Modular Architecture**: A clean `myModules` namespace that decouples configuration logic from host-specific implementation. +- **Modern Desktop**: Support for **Niri** (scrollable tiling) and **Cosmic** (Epoch), with **Noctalia** shell integration. + +--- + +## 🚀 Quick Start ```bash -# Apply configuration -doas nixos-rebuild switch --flake ~/nixos#nixos +# Apply system configuration +doas nixos-rebuild switch --flake .#nixos -# Update flake inputs +# Update all flake inputs nix flake update -# Test configuration without applying -doas nixos-rebuild dry-run --flake ~/nixos#nixos -``` - -## Using These Modules - -Others can import individual modules from this flake: - -```nix -{ - inputs.ashie-nixos.url = "github:ashisgreat22/nixos"; - - outputs = { nixpkgs, ashie-nixos, ... }: { - nixosConfigurations.myhost = nixpkgs.lib.nixosSystem { - modules = [ - ashie-nixos.nixosModules.security - ashie-nixos.nixosModules.kernelHardening - { - myModules.security.enable = true; - myModules.kernelHardening.enable = true; - } - ]; - }; - }; -} -``` - -### Available Modules - -| Module | Description | -| --------------------------------------- | ------------------------------ | -| `nixosModules.security` | doas, audit logging, AppArmor | -| `nixosModules.kernelHardening` | Boot params, sysctl, ZRAM | -| `nixosModules.dnsOverTls` | DNSSEC + DNS-over-TLS | -| `nixosModules.cloudflareFirewall` | nftables Cloudflare-only rules | -| `nixosModules.caddyCloudflare` | Caddy with DNS-01 ACME | -| `nixosModules.podman` | Podman container runtime | -| `nixosModules.browserVpn` | VPN-isolated browsers | -| `homeManagerModules.hyprlandCatppuccin` | Themed Hyprland config | -| `homeManagerModules.gluetunUser` | Rootless VPN container | -| `homeManagerModules.qbittorrentVpn` | qBittorrent through VPN | - -## Structure - -``` -~/nixos/ -├── configuration.nix # Main config (enables modules via myModules.*) -├── flake.nix # Flake inputs, outputs, and module exports -├── hardware-configuration.nix -├── home.nix # Home Manager entry point -├── modules/ # Reusable NixOS modules -│ ├── default.nix # Imports all system modules -│ ├── system/ # System-level modules -│ │ ├── security.nix # doas, audit, AppArmor -│ │ ├── kernel-hardening.nix # Boot params, sysctl, ZRAM -│ │ ├── dns-over-tls.nix # DNSSEC + DoT -│ │ ├── cloudflare-firewall.nix # nftables rules -│ │ ├── caddy-cloudflare.nix # Caddy + DNS-01 -│ │ ├── podman.nix # Container runtime -│ │ └── browser-vpn.nix # VPN-isolated browsers -│ └── home/ # Home Manager modules -│ ├── hyprland-catppuccin.nix -│ ├── gluetun-user.nix -│ ├── qbittorrent-vpn.nix -│ └── browser-container-update.nix -├── system/ # Host-specific system config -│ ├── boot.nix # Bootloader -│ ├── hardware.nix # GPU, USBGuard, fonts -│ ├── networking.nix # Hostname, ddclient -│ ├── packages.nix # System packages -│ ├── services.nix # Steam, Caddy vhosts -│ └── secrets.nix # SOPS secrets -├── home/ # Host-specific Home Manager config -│ ├── fastfetch.nix, kitty.nix, steam.nix, vscode.nix -├── containers/ # Container Dockerfiles -│ ├── firefox-wayland/ # Isolated Firefox -│ ├── thorium-wayland/ # Isolated Thorium -│ └── tor-browser-wayland/ -├── unified_router/ # API routing service -├── codex2api/ # Codex API proxy -├── antigravity-src/ # Antigravity2API source -└── secrets/ # SOPS-encrypted secrets -``` - -## Integrated Services - -### API Ecosystem - -A microservices architecture for managing LLM interactions: - -- **Unified Router** (`unified_router/`) -- **Codex2API** (`codex2api/`) -- **Antigravity2API** (`antigravity-src/`) -- **Data Generator** (`scripts/data_generator/`): Tool for generating synthetic training data. - -### Web Services (via Caddy) - -| Service | URL | Port | -| --------------- | --------------------- | ----------- | -| Open WebUI | `chat.ashisgreat.xyz` | 3000 → 8080 | -| Unified Router | `api.ashisgreat.xyz` | 6767 | -| Antigravity2API | (Internal) | 8045 | - -### Containers - -```bash -# View running containers -podman ps - -# View container logs -podman logs open-webui -podman logs antigravity2api -``` - -## Isolated Browsers (VPN) - -Browsers running in containers routed through WireGuard VPN. - -### Firefox - -```bash -# Launch isolated Firefox -firefox-vpn-podman - -# Or use commands directly -firefox-vpn-podman run # Start Firefox -firefox-vpn-podman stop # Stop containers -firefox-vpn-podman status # Check status -firefox-vpn-podman build # Rebuild container image -``` - -### Tor Browser - -```bash -# Launch isolated Tor Browser -tor-browser-vpn-podman - -# Or use commands directly -tor-browser-vpn-podman run # Start Tor Browser -tor-browser-vpn-podman stop # Stop containers -tor-browser-vpn-podman status # Check status -tor-browser-vpn-podman build # Rebuild container image -``` - -> **Note:** Traffic flows through both the VPN and Tor network for double isolation. - -### Thorium Browser - -```bash -# Launch isolated Thorium Browser -thorium-vpn-podman - -# Or use commands directly -thorium-vpn-podman run # Start Thorium -thorium-vpn-podman stop # Stop containers -thorium-vpn-podman status # Check status -thorium-vpn-podman build # Rebuild container image -``` - -### Auto-Updates - -Browser containers are automatically rebuilt weekly via systemd timer. - -```bash -# Check timer status -systemctl --user status browser-containers-update.timer - -# Manually trigger update -systemctl --user start browser-containers-update - -# View update logs -journalctl --user -u browser-containers-update -n 50 -``` - -## qBittorrent (VPN) - -User service running through gluetun VPN container. - -```bash -# Start/stop -systemctl --user start qbittorrent -systemctl --user stop qbittorrent - -# View status -systemctl --user status gluetun -systemctl --user status qbittorrent - -# Access WebUI (through VPN container) -# http://127.0.0.1:8080 -``` - -## Secrets Management (SOPS) - -Secrets are encrypted with AGE and decrypted at activation time. - -```bash -# Edit secrets -sops secrets/secrets.yaml - -# Add new secret to secrets.nix, then re-encrypt -sops updatekeys secrets/secrets.yaml -``` - -## Security Features & Hardening - -### Kernel Hardening - -**Boot Parameters** (runtime protection): - -- `slab_nomerge` - Prevents slab cache merging -- `init_on_alloc/free=1` - Zeros memory (use-after-free mitigation) -- `page_alloc.shuffle=1` - Randomizes page allocator -- `randomize_kstack_offset=on` - Randomizes kernel stack -- `vsyscall=none` - Disables legacy vsyscall -- `oops=panic` - Panics on kernel oops - -**Sysctl Settings**: - -- `kptr_restrict=2` - Hide kernel pointers -- `dmesg_restrict=1` - Restrict kernel logs -- `ptrace_scope=1` - Restrict debugging -- `unprivileged_bpf_disabled=1` - Disable BPF for users - -```bash -# Verify boot params after reboot +# Check active security parameters cat /proc/cmdline +sudo nft list ruleset ``` -### Network Security +--- -- **DNS-over-TLS (DoT)**: Enabled via `systemd-resolved`. Encrypts all DNS queries to Quad9 and Cloudflare. -- **Firewall**: `nftables` with Cloudflare-only access on ports 80/443. Direct connections are blocked. -- **Caddy**: Uses DNS-01 ACME challenge (via Cloudflare API) for SSL certs. Configured with security headers (HSTS, CSP, etc.). +## 🏗️ Repository Structure -### Audit Logging +```text +/home/ashie/nixos/ +├── flake.nix # Entry point & input management +├── hosts/nixos/ # Host-specific configurations +│ ├── configuration.nix # System entry point +│ ├── default.nix # Enabled system modules (myModules.*) +│ ├── home-modules.nix # Enabled HM modules (myModules.*) +│ └── home.nix # Home Manager entry point +├── modules/ # Reusable logic +│ ├── nixos/ # System modules (Hardening, Podman, etc.) +│ └── home-manager/ # User modules (DEs, Tools, Services) +├── containers/ # Dockerfiles for isolated environments +└── secrets/ # SOPS-encrypted secrets (AGE) +``` + +--- + +## 📦 Modular System (`myModules`) + +This flake uses a unified module system. You can toggle features in `hosts/nixos/default.nix` (system) and `hosts/nixos/home-modules.nix` (user). + +### Key System Modules +| Module | Description | Status | +| :--- | :--- | :--- | +| `security` | AppArmor, doas, and system audit | Enabled | +| `kernelHardening` | Sysctl & boot-time mitigations | Enabled | +| `dnsOverTls` | Encrypted DNS via systemd-resolved | Enabled | +| `cloudflareFirewall` | nftables rules restricted to CF IPs | Enabled | +| `podman` | OCI container runtime | Enabled | +| `ollamaRocm` | Local LLM acceleration for AMD GPUs | Enabled | + +### Key User Modules +| Module | Description | Status | +| :--- | :--- | :--- | +| `niri` | Scrollable tiling window manager | **Active** | +| `cosmic` | System76's modern desktop environment | Available | +| `noctalia` | Custom shell and UI components | Enabled | +| `protonCachyos` | Auto-updating gaming runtime | Enabled | + +--- + +## 🔒 Application Sandboxing + +Applications are wrapped in `bubblewrap` namespaces using the `mkSandboxedApp` utility (see `modules/nixos/sandbox-utils.nix`). This ensures: +- **No Home Access**: Apps only see specific, required directories. +- **D-Bus Isolation**: Access to the system/session bus is filtered via `xdg-dbus-proxy`. +- **Resource Limiting**: Isolated `/proc`, `/dev`, and `/sys` nodes. + +### Sandboxed Applications +- **Browsers**: Firefox, Brave, Tor Browser, Thorium. +- **Gaming**: Steam, Prism Launcher, Lutris. +- **Social**: Vesktop (Discord), Spotify, Tutanota. + +--- + +## 🛠️ Integrated Services + +- **SearXNG**: Privacy-focused search engine at `search.ashisgreat.xyz`. +- **Antigravity2API**: High-performance LLM API proxy. +- **Ollama**: Local AI inference backend with ROCm support. +- **Redlib**: Privacy-friendly Reddit front-end. +- **OpenClaw**: Modern engine for Captain Claw. + +--- + +## 🔐 Secrets Management + +Secrets are managed via **SOPS** and encrypted with **AGE**. +- **Edit secrets**: `sops secrets/secrets.yaml` +- **Key location**: `~/.config/sops/age/keys.txt` + +--- + +## 🧹 Maintenance ```bash -# View audit logs -sudo ausearch -ts today # Today's events -sudo ausearch -k sudoers # Sudoers changes -sudo aureport --summary # Summary report -``` - -### Automatic Updates - -- Runs daily at 4 AM -- Downloads updates but doesn't auto-reboot -- Apply manually: `sudo nixos-rebuild switch --flake ~/nixos#nixos` - -### Known Security Considerations - -- **Secrets**: `cloudflare.key` is currently a raw file, not managed by SOPS. -- **Containers**: Custom service containers may run as root internally. - -## Useful Commands - -```bash -# System -sudo nixos-rebuild switch --flake ~/nixos#nixos # Apply config -sudo nixos-rebuild boot --flake ~/nixos#nixos # Apply on next boot -nix flake update # Update all inputs -nix-collect-garbage -d # Clean old generations - -# Containers -podman system prune -a # Clean unused images -podman volume ls # List volumes - -# Firewall -sudo nft list ruleset # View nftables -sudo nft list set inet cloudflare cloudflare_ipv4 # View Cloudflare IPs - -# Logs -journalctl -u caddy -f # Caddy logs -journalctl --user -u gluetun -f # VPN logs -``` - -## Troubleshooting - -### Container network issues - -```bash -# Recreate podman network -podman network rm antigravity-net -sudo systemctl restart podman-network-antigravity-net -``` - -### Firefox VPN not starting - -```bash -# Check gluetun status first -systemctl --user status gluetun -journalctl --user -u gluetun -n 50 - -# Rebuild image if needed -firefox-vpn-podman build -``` - -### Secrets not decrypting - -```bash -# Check SOPS key -ls -la ~/.config/sops/age/keys.txt -sops -d secrets/secrets.yaml # Test decryption +# Clean old system generations +nix-collect-garbage -d + +# Optimize the nix store +nix store optimise + +# View container status +podman ps -a ``` diff --git a/flake.lock b/flake.lock index 3bb8804..a3d9dbc 100644 --- a/flake.lock +++ b/flake.lock @@ -25,11 +25,11 @@ "cachyos-kernel": { "flake": false, "locked": { - "lastModified": 1771517207, - "narHash": "sha256-+zDtnmXNyMd3hMepErdPDZzqYS0PiZA0Anbbx9Pvs4g=", + "lastModified": 1772644657, + "narHash": "sha256-7zQSBFv9gFeYhe65NchqLLste7mJ396jA1OnNcf+OQQ=", "owner": "CachyOS", "repo": "linux-cachyos", - "rev": "39737576a25091a3c4ca00729b769a1f92ec98d5", + "rev": "ff5ccc4fa26d5272d929fb9c1838593a6347ca10", "type": "github" }, "original": { @@ -41,11 +41,11 @@ "cachyos-kernel-patches": { "flake": false, "locked": { - "lastModified": 1771516433, - "narHash": "sha256-SuockPZgd2bfjWGmdT8AUBTnBZWvxdA+b8Ss98lNC6c=", + "lastModified": 1772731186, + "narHash": "sha256-y70pS9Cma7+WCsni3VTacHh9g/udulmBS6zrYE2Fz64=", "owner": "CachyOS", "repo": "kernel-patches", - "rev": "505aef2086e584ba683a5ac1cb8ed8252fea2cfd", + "rev": "eac8168ee15f742547d4d20ba5c7fea283f23019", "type": "github" }, "original": { @@ -59,11 +59,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1771508520, - "narHash": "sha256-srt94sUlkaGEJHQg7k6gVrBF1QZcHUY/VBESjCgZmKI=", + "lastModified": 1772757591, + "narHash": "sha256-+DyPJcUBXOVu1YiM0mYkEtqYIB9RR0k22NoCaJJ7K2g=", "owner": "catppuccin", "repo": "nix", - "rev": "ec35c21e843e4748e60822cd5543983eb61dc87a", + "rev": "4910a6461a3c4d7ffa56feb4aa4945f3e953f8ec", "type": "github" }, "original": { @@ -75,11 +75,11 @@ "catppuccin-userstyles": { "flake": false, "locked": { - "lastModified": 1771459037, - "narHash": "sha256-QjS/R1ADaWMuRTOR+W8Ppx/HgGlUlXWjbt3iAkd5vSs=", + "lastModified": 1772749300, + "narHash": "sha256-bfFjDcJuUDAjG1+n2a/K6vQlR8LppYGxjT4WmtqAwYw=", "owner": "catppuccin", "repo": "userstyles", - "rev": "9a0dd8c2d0dd87f2962be310ad882762e4ec7074", + "rev": "e61a1c025b75e89f8e7673c024ccd79f80d3d6f0", "type": "github" }, "original": { @@ -112,40 +112,13 @@ "type": "github" } }, - "cppnix": { - "inputs": { - "flake-compat": "flake-compat_4", - "flake-parts": "flake-parts_4", - "git-hooks-nix": "git-hooks-nix", - "nixpkgs": [ - "nixbsd", - "nixpkgs" - ], - "nixpkgs-23-11": "nixpkgs-23-11", - "nixpkgs-regression": "nixpkgs-regression" - }, - "locked": { - "lastModified": 1767672747, - "narHash": "sha256-MqjbAkIYgJge5QSjx2b7hivVHkAVWuquN90HV789E1M=", - "owner": "rhelmot", - "repo": "nix", - "rev": "595d3e984f91237a0ecef84567b461c26a9bf8a9", - "type": "github" - }, - "original": { - "owner": "rhelmot", - "ref": "freebsd", - "repo": "nix", - "type": "github" - } - }, "crane": { "locked": { - "lastModified": 1771121070, - "narHash": "sha256-aIlv7FRXF9q70DNJPI237dEDAznSKaXmL5lfK/Id/bI=", + "lastModified": 1771796463, + "narHash": "sha256-9bCDuUzpwJXcHMQYMS1yNuzYMmKO/CCwCexpjWOl62I=", "owner": "ipetkov", "repo": "crane", - "rev": "a2812c19f1ed2e5ed5ce2ef7109798b575c180e1", + "rev": "3d3de3313e263e04894f284ac18177bd26169bad", "type": "github" }, "original": { @@ -161,11 +134,11 @@ ] }, "locked": { - "lastModified": 1771494902, - "narHash": "sha256-G2yfLhPTuW4nSQCWdXzqknm9uop7OR+zQuoGll5rxLA=", + "lastModified": 1772683386, + "narHash": "sha256-uiYArwJv6pBDuWgmbAJx2+TYFrufn2MdLio5nn0sdRQ=", "owner": "rycee", "repo": "nur-expressions", - "rev": "07b71eb895d1f977c763899b985ee4980412dc57", + "rev": "a992662e5a78c82423a1a58d28ad00d49548ff80", "type": "gitlab" }, "original": { @@ -222,36 +195,6 @@ "type": "github" } }, - "flake-compat_4": { - "flake": false, - "locked": { - "lastModified": 1733328505, - "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-compat_5": { - "locked": { - "lastModified": 1733328505, - "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", - "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", - "revCount": 69, - "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.1.0/01948eb7-9cba-704f-bbf3-3fa956735b52/source.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz" - } - }, "flake-parts": { "inputs": { "nixpkgs-lib": [ @@ -296,11 +239,11 @@ "nixpkgs-lib": "nixpkgs-lib_2" }, "locked": { - "lastModified": 1769996383, - "narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=", + "lastModified": 1772408722, + "narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "57928607ea566b5db3ad13af0e57e921e6b12381", + "rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3", "type": "github" }, "original": { @@ -310,28 +253,6 @@ } }, "flake-parts_4": { - "inputs": { - "nixpkgs-lib": [ - "nixbsd", - "cppnix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1733312601, - "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "flake-parts_5": { "inputs": { "nixpkgs-lib": [ "nixvim", @@ -352,7 +273,7 @@ "type": "github" } }, - "flake-parts_6": { + "flake-parts_5": { "inputs": { "nixpkgs-lib": [ "steam-config-nix", @@ -391,38 +312,21 @@ "type": "github" } }, - "git-hooks-nix": { + "flake-utils_2": { "inputs": { - "flake-compat": [ - "nixbsd", - "cppnix" - ], - "gitignore": [ - "nixbsd", - "cppnix" - ], - "nixpkgs": [ - "nixbsd", - "cppnix", - "nixpkgs" - ], - "nixpkgs-stable": [ - "nixbsd", - "cppnix", - "nixpkgs" - ] + "systems": "systems_2" }, "locked": { - "lastModified": 1734279981, - "narHash": "sha256-NdaCraHPp8iYMWzdXAt5Nv6sA3MUzlCiGiR586TCwo0=", - "owner": "cachix", - "repo": "git-hooks.nix", - "rev": "aa9f40c906904ebd83da78e7f328cd8aeaeae785", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { - "owner": "cachix", - "repo": "git-hooks.nix", + "owner": "numtide", + "repo": "flake-utils", "type": "github" } }, @@ -477,11 +381,11 @@ ] }, "locked": { - "lastModified": 1771531206, - "narHash": "sha256-1R3Wx6KUkMb4x4E5UOhW9p6rqiexzSGGWxZqSHqW5n0=", + "lastModified": 1772633327, + "narHash": "sha256-jl+DJB2DUx7EbWLRng+6HNWW/1/VQOnf0NsQB4PlA7I=", "owner": "nix-community", "repo": "home-manager", - "rev": "91be7cce763fa4022c7cf025a71b0c366d1b6e77", + "rev": "5a75730e6f21ee624cbf86f4915c6e7489c74acc", "type": "github" }, "original": { @@ -511,6 +415,27 @@ "type": "github" } }, + "home-manager_3": { + "inputs": { + "nixpkgs": [ + "nix-openclaw", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1767909183, + "narHash": "sha256-u/bcU0xePi5bgNoRsiqSIwaGBwDilKKFTz3g0hqOBAo=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "cd6e96d56ed4b2a779ac73a1227e0bb1519b3509", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, "impermanence": { "inputs": { "home-manager": "home-manager_2", @@ -570,11 +495,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1771492583, - "narHash": "sha256-nQzvnU4BGu8dA6BsPPCqmVcab/3ebVmHtX3ZWbW3Hxc=", + "lastModified": 1772216104, + "narHash": "sha256-1TnGN26vnCEQk5m4AavJZxGZTb/6aZyphemRPRwFUfs=", "owner": "nix-community", "repo": "lanzaboote", - "rev": "5e9380994665ef66c87ab8e22c913ff837174ce4", + "rev": "dbe5112de965bbbbff9f0729a9789c20a65ab047", "type": "github" }, "original": { @@ -586,11 +511,11 @@ "libnbtplusplus": { "flake": false, "locked": { - "lastModified": 1744811532, - "narHash": "sha256-qhmjaRkt+O7A+gu6HjUkl7QzOEb4r8y8vWZMG2R/C6o=", + "lastModified": 1772016279, + "narHash": "sha256-7itkptyjoRcXfGLwg1/jxajetZ3a4mDc66+w4X6yW8s=", "owner": "PrismLauncher", "repo": "libnbtplusplus", - "rev": "531449ba1c930c98e0bcf5d332b237a8566f9d78", + "rev": "687e43031df0dc641984b4256bcca50d5b3f7de3", "type": "github" }, "original": { @@ -607,11 +532,11 @@ ] }, "locked": { - "lastModified": 1769804089, - "narHash": "sha256-Wkot1j0cTx64xxjmLXzPubTckaZBSUJFhESEdOzPYas=", + "lastModified": 1772763717, + "narHash": "sha256-GaAyeUcsib5mw7YZvba/J0xixrW//m/Ehf//6kSw/UU=", "owner": "utensils", "repo": "mcp-nixos", - "rev": "37a691ea4ea9c8bdcccfe174c6127847b8213fd3", + "rev": "99ab8204aec4497942107c7c1efb76d1b1ed445b", "type": "github" }, "original": { @@ -620,27 +545,6 @@ "type": "github" } }, - "mini-tmpfiles": { - "inputs": { - "nixpkgs": [ - "nixbsd", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1742754557, - "narHash": "sha256-nGxgiNhA94eSl8jcQwCboJ5Ed132z8yrFdOoT+rf8bE=", - "owner": "nixos-bsd", - "repo": "mini-tmpfiles", - "rev": "534ee577692c7092fdcd035f89bc29b663c6f9ca", - "type": "github" - }, - "original": { - "owner": "nixos-bsd", - "repo": "mini-tmpfiles", - "type": "github" - } - }, "mkdocs-catppuccin": { "flake": false, "locked": { @@ -665,11 +569,11 @@ "rust-overlay": "rust-overlay_2" }, "locked": { - "lastModified": 1771305475, - "narHash": "sha256-lqweVTwHhYc+9T33cysp38gVwxaibGJHriOPZXWyhCY=", + "lastModified": 1772207631, + "narHash": "sha256-Jkkg+KqshFO3CbTszVVpkKN2AOObYz+wMsM3ONo1z5g=", "owner": "YaLTeR", "repo": "niri", - "rev": "a2a52911757cb3b497db9407592f9b4c439571ea", + "rev": "e708f546153f74acf33eb183b3b2992587a701e5", "type": "github" }, "original": { @@ -687,11 +591,11 @@ "treefmt-nix": "treefmt-nix" }, "locked": { - "lastModified": 1770308099, - "narHash": "sha256-VxuIePns4c+qpsHpLXW0CwovpKUx1xnvVIUuJwPO6fQ=", + "lastModified": 1772136788, + "narHash": "sha256-5M9aiuBAm1nQd/8UAGrgnr2untzliTiWQIo1sHrGEMY=", "owner": "Naxdy", "repo": "nix-bwrapper", - "rev": "1248b52f2bd4fe5690c1a36836a1798be21d953b", + "rev": "49749a10842ebcc7ff0d2daea660d3b29ca5abb5", "type": "github" }, "original": { @@ -709,11 +613,11 @@ "nixpkgs": "nixpkgs_4" }, "locked": { - "lastModified": 1771525883, - "narHash": "sha256-XqDuaRbxLGno5HcWRE5lQrgMBeXXs6ncGq+R6eCvsq8=", + "lastModified": 1772737222, + "narHash": "sha256-VQ0i0rB4wI9EEoMybDNFzgC/hzcwxEMPmxrq/Ce0JkI=", "owner": "xddxdd", "repo": "nix-cachyos-kernel", - "rev": "15fb6039dd248d478a8f3f7f6c067b206da2bf54", + "rev": "1c0e0cd60713026a6517f31890278d9d5e51de9b", "type": "github" }, "original": { @@ -737,26 +641,44 @@ "type": "github" } }, - "nixbsd": { + "nix-openclaw": { "inputs": { - "cppnix": "cppnix", - "flake-compat": "flake-compat_5", - "mini-tmpfiles": "mini-tmpfiles", + "flake-utils": "flake-utils_2", + "home-manager": "home-manager_3", + "nix-steipete-tools": "nix-steipete-tools", "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1768417153, - "narHash": "sha256-2Vu3Yocs45HGVEYokRvN3DdBtaft37H4Z6rw4rAQ1gk=", - "owner": "nixos-bsd", - "repo": "nixbsd", - "rev": "e393e147e3c30f6424c2a32c5362241c004b5156", + "lastModified": 1772765409, + "narHash": "sha256-hN6Q3uoYKAW5A1B1Tllvk+FUyhNPl8aj1M9WVu7JOpg=", + "owner": "openclaw", + "repo": "nix-openclaw", + "rev": "58c4cae97ce8dde2e314b80017635ee557654df5", "type": "github" }, "original": { - "owner": "nixos-bsd", - "repo": "nixbsd", + "owner": "openclaw", + "repo": "nix-openclaw", + "type": "github" + } + }, + "nix-steipete-tools": { + "inputs": { + "nixpkgs": "nixpkgs_5" + }, + "locked": { + "lastModified": 1772109967, + "narHash": "sha256-0oWZtmVJcI7Mc6nAXf7XM4FHLJv+H1X/8Gh31uJCyJ0=", + "owner": "openclaw", + "repo": "nix-steipete-tools", + "rev": "2b97c49e03657af1574aee5a34f57b38fba90035", + "type": "github" + }, + "original": { + "owner": "openclaw", + "repo": "nix-steipete-tools", "type": "github" } }, @@ -769,11 +691,11 @@ "treefmt-nix": "treefmt-nix_2" }, "locked": { - "lastModified": 1771514873, - "narHash": "sha256-sEAorIUS2IA1VG4mUVYWi+6LEnYmmn1f+3h6sNOqhso=", + "lastModified": 1772488460, + "narHash": "sha256-TZuI5NyeWK0DAJdBfK92X3XbasqkIoGPId9B/Q7euQA=", "owner": "kiriwalawren", "repo": "nixflix", - "rev": "078f61c04340c0365fd5a6772e08cd432d1123a3", + "rev": "1745d0c78c0463e9108db04659848a2df5b44d12", "type": "github" }, "original": { @@ -784,11 +706,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1770197578, - "narHash": "sha256-AYqlWrX09+HvGs8zM6ebZ1pwUqjkfpnv8mewYwAo+iM=", + "lastModified": 1772624091, + "narHash": "sha256-QKyJ0QGWBn6r0invrMAK8dmJoBYWoOWy7lN+UHzW1jc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "00c21e4c93d963c50d4c0c89bfa84ed6e0694df2", + "rev": "80bdc1e5ce51f56b19791b52b2901187931f5353", "type": "github" }, "original": { @@ -798,22 +720,6 @@ "type": "github" } }, - "nixpkgs-23-11": { - "locked": { - "lastModified": 1717159533, - "narHash": "sha256-oamiKNfr2MS6yH64rUn99mIZjc45nGJlj9eGth/3Xuw=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446", - "type": "github" - }, - "original": { - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446", - "type": "github" - } - }, "nixpkgs-lib": { "locked": { "lastModified": 1765674936, @@ -831,11 +737,11 @@ }, "nixpkgs-lib_2": { "locked": { - "lastModified": 1769909678, - "narHash": "sha256-cBEymOf4/o3FD5AZnzC3J9hLbiZ+QDT/KDuyHXVJOpM=", + "lastModified": 1772328832, + "narHash": "sha256-e+/T/pmEkLP6BHhYjx6GmwP5ivonQQn0bJdH9YrRB+Q=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "72716169fe93074c333e8d0173151350670b824c", + "rev": "c185c7a5e5dd8f9add5b2f8ebeff00888b070742", "type": "github" }, "original": { @@ -844,22 +750,6 @@ "type": "github" } }, - "nixpkgs-regression": { - "locked": { - "lastModified": 1643052045, - "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", - "type": "github" - }, - "original": { - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", - "type": "github" - } - }, "nixpkgs_2": { "locked": { "lastModified": 1767892417, @@ -894,11 +784,11 @@ }, "nixpkgs_4": { "locked": { - "lastModified": 1771482645, - "narHash": "sha256-MpAKyXfJRDTgRU33Hja+G+3h9ywLAJJNRq4Pjbb4dQs=", + "lastModified": 1772691005, + "narHash": "sha256-TCamkDXY0G84Se5Kio6BbqtWfWfPXg9on9ZsX19tnNo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "724cf38d99ba81fbb4a347081db93e2e3a9bc2ae", + "rev": "be4f549ba12cd3e2b66d24fa7e39cd871111bdb3", "type": "github" }, "original": { @@ -910,11 +800,27 @@ }, "nixpkgs_5": { "locked": { - "lastModified": 1771369470, - "narHash": "sha256-0NBlEBKkN3lufyvFegY4TYv5mCNHbi5OmBDrzihbBMQ=", + "lastModified": 1767364772, + "narHash": "sha256-fFUnEYMla8b7UKjijLnMe+oVFOz6HjijGGNS1l7dYaQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "0182a361324364ae3f436a63005877674cf45efb", + "rev": "16c7794d0a28b5a37904d55bcca36003b9109aaa", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_6": { + "locked": { + "lastModified": 1772624091, + "narHash": "sha256-QKyJ0QGWBn6r0invrMAK8dmJoBYWoOWy7lN+UHzW1jc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "80bdc1e5ce51f56b19791b52b2901187931f5353", "type": "github" }, "original": { @@ -925,18 +831,18 @@ }, "nixvim": { "inputs": { - "flake-parts": "flake-parts_5", + "flake-parts": "flake-parts_4", "nixpkgs": [ "nixpkgs" ], - "systems": "systems_2" + "systems": "systems_3" }, "locked": { - "lastModified": 1771135771, - "narHash": "sha256-wyvBIhDuyCRyjB3yPg77qoyxrlgQtBR1rVW3c9knV3E=", + "lastModified": 1772402258, + "narHash": "sha256-3DmCFOdmbkFML1/G9gj8Wb+rCCZFPOQtNoMCpqOF8SA=", "owner": "nix-community", "repo": "nixvim", - "rev": "ed0424f0b08d303a7348f52f7850ad1b2704f9ba", + "rev": "21ae25e13b01d3b4cdc750b5f9e7bad68b150c10", "type": "github" }, "original": { @@ -949,14 +855,15 @@ "inputs": { "nixpkgs": [ "nixpkgs" - ] + ], + "noctalia-qs": "noctalia-qs" }, "locked": { - "lastModified": 1771554771, - "narHash": "sha256-atFYM8h8fgnXW/i/zM3yZnhsbVxlsIQ6eq/FcC6uZ6k=", + "lastModified": 1772755279, + "narHash": "sha256-vDeyLhCqy2weSYD/5LtX4kXXc/pBwd1rUqcPkzCGTKs=", "owner": "noctalia-dev", "repo": "noctalia-shell", - "rev": "8eef8ef71d64a7ad0144eb79221cdfcc568848cf", + "rev": "52a7165b46117ac7dcf41be1f9df6f1e1a538b13", "type": "github" }, "original": { @@ -965,6 +872,27 @@ "type": "github" } }, + "noctalia-qs": { + "inputs": { + "nixpkgs": [ + "noctalia", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1772673824, + "narHash": "sha256-TLHXPoELZA6VeuzC1Zpx+MnSsYzrJs+DSieMgfjAOJc=", + "owner": "noctalia-dev", + "repo": "noctalia-qs", + "rev": "f8531192cd09b9ea2e78d18e9cfc9d3dba498690", + "type": "github" + }, + "original": { + "owner": "noctalia-dev", + "repo": "noctalia-qs", + "type": "github" + } + }, "nuschtosSearch": { "inputs": { "flake-utils": "flake-utils", @@ -1041,11 +969,11 @@ ] }, "locked": { - "lastModified": 1770726378, - "narHash": "sha256-kck+vIbGOaM/dHea7aTBxdFYpeUl/jHOy5W3eyRvVx8=", + "lastModified": 1771858127, + "narHash": "sha256-Gtre9YoYl3n25tJH2AoSdjuwcqij5CPxL3U3xysYD08=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "5eaaedde414f6eb1aea8b8525c466dc37bba95ae", + "rev": "49bbbfc218bf3856dfa631cead3b052d78248b83", "type": "github" }, "original": { @@ -1062,11 +990,11 @@ ] }, "locked": { - "lastModified": 1771507368, - "narHash": "sha256-Q7cDybjd7GjYsN9SHd/fBSNdgieM5bX23gErJ9QE5xc=", + "lastModified": 1772470402, + "narHash": "sha256-bC61/pe4YDXtOQh66wf2QObCA10efl7mSRwrN6MjRYQ=", "owner": "PrismLauncher", "repo": "PrismLauncher", - "rev": "eac55d849c7ab44a3310a9c5c822a850331c3160", + "rev": "b114d043f638e30d421b8a299fdfed4b3230ba3d", "type": "github" }, "original": { @@ -1090,9 +1018,9 @@ "nix-bwrapper": "nix-bwrapper", "nix-cachyos-kernel": "nix-cachyos-kernel", "nix-flatpak": "nix-flatpak", - "nixbsd": "nixbsd", + "nix-openclaw": "nix-openclaw", "nixflix": "nixflix", - "nixpkgs": "nixpkgs_5", + "nixpkgs": "nixpkgs_6", "nixvim": "nixvim", "noctalia": "noctalia", "opencode-flake": "opencode-flake", @@ -1109,11 +1037,11 @@ ] }, "locked": { - "lastModified": 1771125043, - "narHash": "sha256-ldf/s49n6rOAxl7pYLJGGS1N/assoHkCOWdEdLyNZkc=", + "lastModified": 1771988922, + "narHash": "sha256-Fc6FHXtfEkLtuVJzd0B6tFYMhmcPLuxr90rWfb/2jtQ=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "4912f951a26dc8142b176be2c2ad834319dc06e8", + "rev": "f4443dc3f0b6c5e6b77d923156943ce816d1fcb9", "type": "github" }, "original": { @@ -1150,11 +1078,11 @@ ] }, "locked": { - "lastModified": 1771524872, - "narHash": "sha256-eksVUcUsfS9mQx4D9DrYu88u9w70bAf+n6KmTDuIGEE=", + "lastModified": 1772495394, + "narHash": "sha256-hmIvE/slLKEFKNEJz27IZ8BKlAaZDcjIHmkZ7GCEjfw=", "owner": "Mic92", "repo": "sops-nix", - "rev": "e85540ffe97322dc1fea14dd11cdc2f59d540ac7", + "rev": "1d9b98a29a45abe9c4d3174bd36de9f28755e3ff", "type": "github" }, "original": { @@ -1165,18 +1093,18 @@ }, "steam-config-nix": { "inputs": { - "flake-parts": "flake-parts_6", + "flake-parts": "flake-parts_5", "nixpkgs": [ "nixpkgs" ], - "systems": "systems_3" + "systems": "systems_4" }, "locked": { - "lastModified": 1770296756, - "narHash": "sha256-3jBIUXJu+Pc2MPu1KaHqhTS1z6KospVbDlBPvggATqs=", + "lastModified": 1771641886, + "narHash": "sha256-+mchQJE30NiI66DUMwXW+dBrKJeF240n4v45TcXmkIc=", "owner": "different-name", "repo": "steam-config-nix", - "rev": "e409b3bac9412513ff95fe293eb6ad42f985c60b", + "rev": "5b24ff543683b62663adbc3b54942929ca0d4d91", "type": "github" }, "original": { @@ -1230,6 +1158,21 @@ "type": "github" } }, + "systems_4": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, "treefmt-nix": { "inputs": { "nixpkgs": "nixpkgs_3" diff --git a/flake.nix b/flake.nix index 73a8806..9e4c3aa 100644 --- a/flake.nix +++ b/flake.nix @@ -1,11 +1,6 @@ { description = "Modular NixOS Configuration with Hyprland"; - nixConfig = { - extra-substituters = [ "https://attic.mildlyfunctional.gay/nixbsd" ]; - extra-trusted-public-keys = [ "nixbsd:gwcQlsUONBLrrGCOdEboIAeFq9eLaDqfhfXmHZs1mgc=" ]; - }; - inputs = { nixpkgs.url = "nixpkgs/nixos-unstable"; @@ -103,10 +98,11 @@ inputs.nixpkgs.follows = "nixpkgs"; }; - nixbsd = { - url = "github:nixos-bsd/nixbsd"; + nix-openclaw = { + url = "github:openclaw/nix-openclaw"; inputs.nixpkgs.follows = "nixpkgs"; }; + }; outputs = @@ -121,7 +117,7 @@ nixflix, arkenfox, firefox-addons, - nixbsd, + ... }@inputs: { @@ -147,24 +143,6 @@ default = import ./modules/home-manager; }; - nixosConfigurations.nixbsd = nixbsd.lib.nixbsdSystem { - specialArgs = { inherit inputs; }; - modules = [ - ./hosts/nixbsd/configuration.nix - ]; - }; - - nixosConfigurations.nixbsd-vm = nixbsd.lib.nixbsdSystem { - specialArgs = { inherit inputs; }; - modules = [ - ./hosts/nixbsd/configuration.nix - ({ config, ... }: { - # Enable VM variant - # This is already in configuration.nix but we can make it explicit here if we want. - }) - ]; - }; - nixosConfigurations.nixos = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; specialArgs = { inherit inputs; }; @@ -188,9 +166,9 @@ useUserPackages = true; backupFileExtension = "backup"; users.ashie = import ./hosts/nixos/home.nix; + }; } - ./modules/nixos/impermanence.nix ]; }; @@ -216,9 +194,9 @@ useUserPackages = true; backupFileExtension = "backup"; users.ashie = import ./hosts/nixos/home.nix; + }; } - ./modules/nixos/impermanence.nix ]; }; }; diff --git a/flaresolverr_test.json b/flaresolverr_test.json deleted file mode 100644 index aac331b..0000000 --- a/flaresolverr_test.json +++ /dev/null @@ -1 +0,0 @@ -{"status": "ok", "message": "Challenge solved!", "solution": {"url": "https://1337x.to/", "status": 200, "cookies": [{"domain": ".1337x.to", "expiry": 1800392887, "httpOnly": true, "name": "cf_clearance", "path": "/", "sameSite": "None", "secure": true, "value": "Hppe3HHGqJQSvfeLipKrf9nn.IZO1L2b73ABvWOlr04-1768856887-1.2.1.1-g4KpGkeQcFK8YxEHGOnDwNGDswkXCjmqjnNWsfVN0bKKR1Ck_0CMie8_SB9aK.VhT9qQ9keJmCNKXVv0QDWmqqzPH.2.3IA6lStvUhYpQ3sccVKvFNfvdpsywn8RZYIpX5fuZQxxl80NhPXHNHQSQn11r3AdYRg_EIwtfiD8m1GXrBmVK726kmeTnHNiTfBkvDMaRwsV8DKIVYl4g2kzvLyrSUzOGQq59h1I3ApK6qTSOKtBEti_5oOJOBWvkhvD"}], "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36", "headers": {}, "response": "\n\n\nTorrent Search Engine | 1337x.to\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
\n \n \n
\n \n
\n \n
\n
\n
\n \n \n
\n
\n \n
\n
\n \n \n
\n
\n \n \n
\n
\n
\n
\"logo\"
\n\n
\n
\n \n \n
\n
    \n \n
    \n
    \n
    \n \n \n \n\n\n \n\n \n\n\n"}, "startTimestamp": 1768856883767, "endTimestamp": 1768856894907, "version": "3.4.6"} \ No newline at end of file diff --git a/hosts/nixbsd/configuration.nix b/hosts/nixbsd/configuration.nix deleted file mode 100644 index 91253bc..0000000 --- a/hosts/nixbsd/configuration.nix +++ /dev/null @@ -1,47 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: -{ - nixpkgs.hostPlatform = "x86_64-freebsd"; - - # NixBSD doesn't have systemd, it uses FreeBSD's init or similar. - # The NixBSD modules handle the specific FreeBSD configuration. - - networking.hostName = "nixbsd"; - - # Default user configuration - users.users.ashie = { - isNormalUser = true; - description = "Ashie"; - extraGroups = [ "wheel" ]; - initialPassword = "nixbsd"; - }; - - # SSH service for access - services.sshd.enable = true; - - # FreeBSD loader configuration - boot.loader.stand-freebsd.enable = true; - - # File system layout - fileSystems."/" = { - device = "/dev/gpt/nixos"; - fsType = "ufs"; - }; - - fileSystems."/boot" = { - device = "/dev/msdosfs/ESP"; - fsType = "msdosfs"; - }; - - # VM variant for running in QEMU - virtualisation.vmVariant = { - virtualisation = { - memorySize = 2048; # 2GB RAM - cores = 2; - }; - }; -} diff --git a/hosts/nixos/configuration.nix b/hosts/nixos/configuration.nix index 6382229..1407edf 100644 --- a/hosts/nixos/configuration.nix +++ b/hosts/nixos/configuration.nix @@ -33,18 +33,29 @@ # Enable performance optimizations myModules.performance.enable = true; - + services.resolved.dnssec = "false"; # Enable modularized components myModules.desktop.cosmic.enable = true; myModules.media.enable = true; myModules.gaming.gamemode.enable = true; myModules.redlib.enable = true; + services.openclaw-service.enable = true; # Enable sandboxed applications + myModules.wireproxy = { + enable = true; + endpointIP = "94.228.209.212"; + }; myModules.steamSandboxed.enable = true; myModules.lutrisSandboxed.enable = true; - myModules.firefoxSandboxed.enable = true; - myModules.braveSandboxed.enable = true; + myModules.firefoxSandboxed = { + enable = true; + useProxy = true; + }; + myModules.braveSandboxed = { + enable = true; + useProxy = true; + }; myModules.azaharSandboxed.enable = true; myModules.faugusSandboxed.enable = true; myModules.citronSandboxed.enable = true; diff --git a/hosts/nixos/default.nix b/hosts/nixos/default.nix index 51dedd4..5b26cc7 100644 --- a/hosts/nixos/default.nix +++ b/hosts/nixos/default.nix @@ -40,7 +40,7 @@ dnsOverTls = { enable = true; - dnssec = true; + dnssec = false; }; cloudflareFirewall = { @@ -51,6 +51,7 @@ # Ports that are public 443 80 + 1080 ]; restrictedPorts = [ ]; # Ports that are Cloudflare only }; @@ -69,7 +70,7 @@ }; ollamaRocm = { - enable = false; # Disabled temporarily to unblock install (namespace issues) + enable = true; }; openWebUI = { diff --git a/hosts/nixos/home.nix b/hosts/nixos/home.nix index ccbc532..7210ab2 100644 --- a/hosts/nixos/home.nix +++ b/hosts/nixos/home.nix @@ -9,7 +9,7 @@ ../../modules/home-manager/gluetun-user.nix ../../modules/home-manager/cosmic.nix inputs.sops-nix.homeManagerModules.sops - inputs.steam-config-nix.homeModules.default + # inputs.steam-config-nix.homeModules.default inputs.catppuccin.homeManagerModules.catppuccin inputs.nixvim.homeManagerModules.nixvim # inputs.unified-router-mcp.homeManagerModules.default @@ -41,6 +41,9 @@ sops.age.keyFile = "/home/ashie/.config/sops/age/keys.txt"; sops.secrets.master_api_key = { }; + sops.secrets.discord_bot_token = { }; + sops.secrets.searxng_brave_api_key = { }; + sops.secrets.github_token = { }; # Unified Router MCP Servers # services.unified-router-mcp = { @@ -60,6 +63,7 @@ username = "ashie"; password = "AshieAntigravity2024!"; apiKey = "sk-antigravity-local-key"; + glmApiKeyPath = "/run/secrets/glm_api_key"; }; }; @@ -111,6 +115,17 @@ updateInterval = 24 * 60 * 60 * 1000; # every day definedAliases = [ "@ag" ]; }; + "Brave Search" = { + urls = [{ + template = "https://search.brave.com/search"; + params = [ + { name = "q"; value = "{searchTerms}"; } + ]; + }]; + iconUpdateURL = "https://search.brave.com/favicon.ico"; + updateInterval = 24 * 60 * 60 * 1000; # every day + definedAliases = [ "@b" ]; + }; }; }; arkenfox = { diff --git a/hosts/nixos/home/steam.nix b/hosts/nixos/home/steam.nix index 7a11e6d..c915eb0 100644 --- a/hosts/nixos/home/steam.nix +++ b/hosts/nixos/home/steam.nix @@ -1,22 +1 @@ -{ - config, - pkgs, - inputs, - ... -}: -{ - programs.steam.config = { - enable = true; - closeSteam = true; # Closes Steam on rebuild, to prevent data loss - defaultCompatTool = "proton-cachyos-latest"; - - apps = { - overwatch2 = { - id = 2357570; - compatTool = "proton-cachyos-latest"; - launchOptions = "gamemoderun mangohud PROTON_USE_NTSYNC=1 ENABLE_LAYER_MESA_ANTI_LAG=1 PROTON_LOCAL_SHADER_CACHE=1 %command%"; - }; - }; - }; - -} +{ ... }: { } diff --git a/hosts/nixos/home/vscode.nix b/hosts/nixos/home/vscode.nix index e18a0e7..54c1930 100644 --- a/hosts/nixos/home/vscode.nix +++ b/hosts/nixos/home/vscode.nix @@ -51,6 +51,10 @@ let # Run antigravity binary when the FHS env is invoked runScript = pkgs.writeShellScript "antigravity-wrapper" '' unset LD_PRELOAD + + # Use a wrapper for bash that ignores user configuration + export SHELL=${pkgs.writeShellScript "bash-sandboxed" ''exec ${pkgs.bash}/bin/bash --noprofile --norc "$@"''} + exec ${pkgs.antigravity}/bin/antigravity "$@" ''; @@ -59,6 +63,11 @@ let export LD_LIBRARY_PATH=/usr/lib:/usr/lib64:$LD_LIBRARY_PATH ''; + extraBwrapArgs = [ + "--tmpfs" + "/home/ashie/.config/fish" + ]; + extraBindMounts = [ "/etc/subuid" "/etc/subgid" @@ -93,16 +102,18 @@ let # Helper to adapt VS Code extensions for Antigravity # Home Manager expects extensions to be in share/antigravity/extensions based on the package name, # but standard extensions are in share/vscode/extensions. - adaptToAntigravity = ext: pkgs.symlinkJoin { - name = "${ext.name}-antigravity"; - paths = [ ext ]; - # Ensure passthru attributes are preserved (though symlinkJoin usually handles this, specific ones might help) - inherit (ext) meta; - postBuild = '' - mkdir -p $out/share/antigravity - ln -sf ${ext}/share/vscode/extensions $out/share/antigravity/extensions - ''; - }; + adaptToAntigravity = + ext: + pkgs.symlinkJoin { + name = "${ext.name}-antigravity"; + paths = [ ext ]; + # Ensure passthru attributes are preserved (though symlinkJoin usually handles this, specific ones might help) + inherit (ext) meta; + postBuild = '' + mkdir -p $out/share/antigravity + ln -sf ${ext}/share/vscode/extensions $out/share/antigravity/extensions + ''; + }; in { home.packages = [ @@ -132,49 +143,52 @@ in enableExtensionUpdateCheck = false; # Extensions from nixpkgs - extensions = map adaptToAntigravity (with pkgs.vscode-extensions; [ - # Theme & Icons - catppuccin.catppuccin-vsc - catppuccin.catppuccin-vsc-icons + extensions = map adaptToAntigravity ( + with pkgs.vscode-extensions; + [ + # Theme & Icons + catppuccin.catppuccin-vsc + catppuccin.catppuccin-vsc-icons - # Git - eamodio.gitlens + # Git + eamodio.gitlens - # C/C++ - llvm-vs-code-extensions.vscode-clangd + # C/C++ + llvm-vs-code-extensions.vscode-clangd - # Nix - jnoortheen.nix-ide + # Nix + jnoortheen.nix-ide - # Python - ms-python.python - ms-python.debugpy + # Python + ms-python.python + ms-python.debugpy - # Go - golang.go + # Go + golang.go - # Java (RedHat + vscjava) - redhat.java - vscjava.vscode-java-debug - vscjava.vscode-java-dependency - vscjava.vscode-java-pack - vscjava.vscode-java-test - vscjava.vscode-gradle - vscjava.vscode-maven + # Java (RedHat + vscjava) + redhat.java + vscjava.vscode-java-debug + vscjava.vscode-java-dependency + vscjava.vscode-java-pack + vscjava.vscode-java-test + vscjava.vscode-gradle + vscjava.vscode-maven - # PHP - bmewburn.vscode-intelephense-client - xdebug.php-debug + # PHP + bmewburn.vscode-intelephense-client + xdebug.php-debug - # Ruby - shopify.ruby-lsp + # Ruby + shopify.ruby-lsp - # Docker & Containers - ms-azuretools.vscode-docker + # Docker & Containers + ms-azuretools.vscode-docker - # Formatters - esbenp.prettier-vscode - ]); + # Formatters + esbenp.prettier-vscode + ] + ); # User settings (settings.json equivalent) userSettings = { diff --git a/hosts/nixos/kafka.nix b/hosts/nixos/kafka.nix new file mode 100644 index 0000000..ec5c1fb --- /dev/null +++ b/hosts/nixos/kafka.nix @@ -0,0 +1,96 @@ +{ + config, + pkgs, + inputs, + ... +}: +{ + imports = [ + inputs.sops-nix.homeManagerModules.sops + inputs.nix-openclaw.homeManagerModules.openclaw + ]; + + home.username = "kafka"; + home.homeDirectory = "/home/kafka"; + home.stateVersion = "25.05"; + + sops.defaultSopsFile = ../../secrets/secrets.yaml; + sops.defaultSopsFormat = "yaml"; + sops.age.keyFile = "/home/kafka/.config/sops/age/keys.txt"; + + sops.secrets.openai_api_key = { }; + sops.secrets.github_token = { }; + + programs.openclaw = { + enable = true; + stateDir = "/home/kafka/openclaw"; + workspaceDir = "/home/kafka/openclaw/workspace"; + config = { + gateway = { + port = 18789; + bind = "loopback"; + trustedProxies = [ "::1" "127.0.0.1" "10.88.0.0/16" "10.89.0.0/16" ]; + auth = { + mode = "none"; + }; + controlUi = { + dangerouslyAllowHostHeaderOriginFallback = true; + allowedOrigins = [ "*" ]; + }; + }; + channels = { + discord = { + enabled = true; + token = "/run/secrets/openclaw-discord-token"; + allowFrom = [ "1178286690750693419" "*" ]; + groupPolicy = "open"; + dmPolicy = "open"; + }; + }; + agents = { + defaults = { + workspace = "/home/kafka/openclaw/workspace"; + model = { + primary = "zai/glm-4.7"; + }; + }; + }; + commands = { + native = true; + nativeSkills = "auto"; + restart = true; + ownerDisplay = "raw"; + }; + tools = { + exec = { + security = "full"; + ask = "off"; + }; + }; + models = { + mode = "merge"; + providers.zai = { + baseUrl = "https://api.z.ai/api/coding/paas/v4"; + apiKey = "e77f2c392cb942eca9d0407eebc75549.XG7ikxT2kBEQUPYx"; + models = [ + { + id = "glm-4.7"; + name = "GLM 4.7"; + reasoning = true; + contextWindow = 128000; + maxTokens = 128000; + } + { + id = "glm-5"; + name = "GLM 5"; + reasoning = true; + contextWindow = 128000; + maxTokens = 128000; + } + ]; + }; + }; + skills.entries.mcporter.enabled = true; + }; + }; +} diff --git a/hosts/nixos/system/authelia.nix b/hosts/nixos/system/authelia.nix index 742019a..b36cd70 100644 --- a/hosts/nixos/system/authelia.nix +++ b/hosts/nixos/system/authelia.nix @@ -48,6 +48,12 @@ domain = "auth.ashisgreat.xyz"; policy = "bypass"; } + # Bypass for local network (service-to-service communication) + { + domain = "*.ashisgreat.xyz"; + networks = [ "10.89.0.0/24" ]; + policy = "bypass"; + } # Bypass for Jellyfin (handles its own auth) { domain = "jellyfin.ashisgreat.xyz"; @@ -61,6 +67,7 @@ "prowlarr.ashisgreat.xyz" "torrent.ashisgreat.xyz" "jellyseer.ashisgreat.xyz" + "openclaw.ashisgreat.xyz" ]; policy = "two_factor"; } diff --git a/hosts/nixos/system/game-drive.nix b/hosts/nixos/system/game-drive.nix index 0124645..ec8e04d 100644 --- a/hosts/nixos/system/game-drive.nix +++ b/hosts/nixos/system/game-drive.nix @@ -21,7 +21,7 @@ device = "/games"; fsType = "none"; options = [ - "bind" + "rbind" "x-systemd.after=games.mount" ]; }; diff --git a/hosts/nixos/system/kernel.nix b/hosts/nixos/system/kernel.nix index 2bc69bd..fcba9fd 100644 --- a/hosts/nixos/system/kernel.nix +++ b/hosts/nixos/system/kernel.nix @@ -10,9 +10,9 @@ # inputs.nix-cachyos-kernel.overlays.default # ]; # Use CachyOS Kernel - boot.kernelPackages = - pkgs.linuxPackagesFor - inputs.nix-cachyos-kernel.packages.${pkgs.system}.linux-cachyos-bore-lto; + # boot.kernelPackages = + # pkgs.linuxPackagesFor + # inputs.nix-cachyos-kernel.packages.${pkgs.system}.linux-cachyos-bore-lto; # ============================================================================= # DEFAULT BOOT: Linux Desktop Mode (GPU for Host) @@ -44,5 +44,6 @@ "net.ipv4.tcp_congestion_control" = "bbr"; "net.core.default_qdisc" = "fq"; "vm.max_map_count" = 1048576; + "user.max_user_namespaces" = 10000; }; } diff --git a/hosts/nixos/system/networking.nix b/hosts/nixos/system/networking.nix index 2f40cc5..7535a54 100644 --- a/hosts/nixos/system/networking.nix +++ b/hosts/nixos/system/networking.nix @@ -96,11 +96,22 @@ "jellyseer.ashisgreat.xyz" "jellyseerr.ashisgreat.xyz" "search.ashisgreat.xyz" + "openclaw.ashisgreat.xyz" ]; interval = "10min"; usev6 = "disabled"; usev4 = "cmdv4"; - extraConfig = "cmdv4='${pkgs.curl}/bin/curl -s https://api.ipify.org'"; + extraConfig = '' + cmdv4='${pkgs.curl}/bin/curl -s https://api.ipify.org' + + # Update IPv4 and IPv6 for root domain + usev6=cmdv6 + cmdv6='${pkgs.curl}/bin/curl -s https://api64.ipify.org' + ashisgreat.xyz + + # Revert to IPv4 only for subdomains appended below + usev6=disabled + ''; }; # Make ddclient use a static user for UID-based routing @@ -125,6 +136,7 @@ # Ensures the host can reach these domains even if VPN routing prevents public IP loopback networking.hosts = { "127.0.0.1" = [ + "ashisgreat.xyz" "api.ashisgreat.xyz" "search.ashisgreat.xyz" "chat.ashisgreat.xyz" @@ -138,6 +150,7 @@ "jellyfin.ashisgreat.xyz" "jellyseer.ashisgreat.xyz" "jellyseerr.ashisgreat.xyz" + "openclaw.ashisgreat.xyz" ]; }; } diff --git a/hosts/nixos/system/nix-settings.nix b/hosts/nixos/system/nix-settings.nix index 24744a2..8be297d 100644 --- a/hosts/nixos/system/nix-settings.nix +++ b/hosts/nixos/system/nix-settings.nix @@ -6,6 +6,7 @@ }: { nixpkgs.overlays = [ + inputs.nix-openclaw.overlays.default (final: prev: { antigravity = prev.antigravity.overrideAttrs (oldAttrs: rec { version = "1.18.3"; @@ -46,7 +47,7 @@ "nix-command" "flakes" ]; - nix.settings.allowed-users = [ "ashie" ]; + nix.settings.allowed-users = [ "ashie" "kafka" ]; nix.settings.sandbox = true; # Automatic Garbage Collection diff --git a/hosts/nixos/system/packages.nix b/hosts/nixos/system/packages.nix index b1a9b05..fb7392f 100644 --- a/hosts/nixos/system/packages.nix +++ b/hosts/nixos/system/packages.nix @@ -98,7 +98,7 @@ stress-ng kdePackages.kleopatra kdePackages.ark - + qdirstat dysk zstd podman diff --git a/hosts/nixos/system/secrets.nix b/hosts/nixos/system/secrets.nix index 1b4b835..1069b60 100644 --- a/hosts/nixos/system/secrets.nix +++ b/hosts/nixos/system/secrets.nix @@ -75,7 +75,8 @@ # Unified API Key sops.secrets.master_api_key = { - owner = "ashie"; + group = "media"; + mode = "0440"; }; sops.templates."api_key.env" = { @@ -92,6 +93,26 @@ owner = "ashie"; }; + sops.secrets.github_token = { + group = "media"; + mode = "0440"; + }; + + sops.secrets.searxng_brave_api_key = { + group = "media"; + mode = "0440"; + }; + + sops.secrets.discord_bot_token = { + group = "media"; + mode = "0440"; + }; + + sops.secrets.glm_api_key = { + group = "media"; + mode = "0440"; + }; + sops.secrets.hashed_password = { neededForUsers = true; }; diff --git a/hosts/nixos/system/services.nix b/hosts/nixos/system/services.nix index 4656aec..1422686 100644 --- a/hosts/nixos/system/services.nix +++ b/hosts/nixos/system/services.nix @@ -14,6 +14,11 @@ let proxy_pass_request_body off; proxy_set_header Content-Length ""; proxy_set_header X-Original-URI $request_uri; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $http_host; + proxy_set_header X-Original-Method $request_method; ''; }; @@ -23,8 +28,12 @@ let auth_request_set $target_url $scheme://$http_host$request_uri; auth_request_set $user $upstream_http_remote_user; auth_request_set $groups $upstream_http_remote_groups; + auth_request_set $name $upstream_http_remote_name; + auth_request_set $email $upstream_http_remote_email; proxy_set_header Remote-User $user; proxy_set_header Remote-Groups $groups; + proxy_set_header Remote-Name $name; + proxy_set_header Remote-Email $email; error_page 401 =302 https://auth.ashisgreat.xyz/?rd=$target_url; ''; in @@ -101,6 +110,22 @@ in myModules.nginx.enable = true; services.nginx.virtualHosts = { + "ashisgreat.xyz" = { + useACMEHost = "ashisgreat.xyz"; + forceSSL = true; + root = pkgs.stdenv.mkDerivation { + name = "ashisgreat-website"; + src = ./website; + installPhase = '' + mkdir -p $out + cp -r * $out/ + ''; + }; + locations."/" = { + tryFiles = "$uri $uri/ =404"; + }; + }; + "_" = { default = true; useACMEHost = "ashisgreat.xyz"; @@ -295,6 +320,28 @@ in ''; }; }; + + "openclaw.ashisgreat.xyz" = { + useACMEHost = "ashisgreat.xyz"; + forceSSL = true; + extraConfig = autheliaProtect; + locations."/authelia" = autheliaLocation; + locations."/" = { + proxyPass = "http://127.0.0.1:18789"; + proxyWebsockets = true; + extraConfig = '' + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + proxy_buffering off; + ''; + }; + }; }; # Hardening for Chrony diff --git a/hosts/nixos/system/users.nix b/hosts/nixos/system/users.nix index 24aa903..788fe5e 100644 --- a/hosts/nixos/system/users.nix +++ b/hosts/nixos/system/users.nix @@ -51,6 +51,9 @@ ]; }; + + users.groups.media = { }; + # Disable root password login users.users.root = { hashedPassword = "!"; diff --git a/hosts/nixos/system/website/index.html b/hosts/nixos/system/website/index.html new file mode 100644 index 0000000..e1aaafe --- /dev/null +++ b/hosts/nixos/system/website/index.html @@ -0,0 +1,191 @@ + + + + + + Ash (Phil) B. | Python Developer & Data Engineer + + + +
    +
    + Ash B. +
    +

    Ash (Phil) B. | Python Developer & Data Engineer

    +

    I build custom Python scripts that replace hours of manual data entry.

    +
    +
    + +
    +
    +
    +

    Web Scraping & Data Extraction

    +
    +
    +

    Workflow & Excel Automation

    +
    +
    +

    Discord / Telegram Chatbots

    +
    +
    +
    + +
    + Hire me on Fiverr + View my GitHub + Contact via Email +
    +
    + + diff --git a/hosts/nixos/system/website/profile.png b/hosts/nixos/system/website/profile.png new file mode 100644 index 0000000..9ef8561 Binary files /dev/null and b/hosts/nixos/system/website/profile.png differ diff --git a/modules/home-manager/antigravity2api.nix b/modules/home-manager/antigravity2api.nix index 6dcae5f..4b802b4 100644 --- a/modules/home-manager/antigravity2api.nix +++ b/modules/home-manager/antigravity2api.nix @@ -29,6 +29,11 @@ in default = ""; description = "API Key for client access"; }; + glmApiKeyPath = lib.mkOption { + type = lib.types.str; + default = ""; + description = "Path to Z.AI (GLM) API Key file"; + }; }; }; @@ -46,33 +51,38 @@ in export PATH="${pkgs.coreutils}/bin:${pkgs.iputils}/bin:$PATH" mkdir -p "${workDir}" - # Ensure network exists (Not needed for host network) - # ${pkgs.podman}/bin/podman network create antigravity-net --ignore >/dev/null 2>&1 || true + # Ensure network exists + ${pkgs.podman}/bin/podman network create antigravity-net --ignore >/dev/null 2>&1 || true - # Wait for connectivity to 8.8.8.8 (Google DNS) used by the container - echo "Waiting for internet connectivity..." - until ping -c1 -W1 8.8.8.8 >/dev/null 2>&1; do - sleep 2 - done - echo "Connectivity check passed." + # Wait for connectivity + until ping -c1 -W1 8.8.8.8 >/dev/null 2>&1; do sleep 2; done + # Generate .env file using a temporary script to capture secret correctly + GLM_VAL=$(cat /run/secrets/glm_api_key) + cat > "${workDir}/.env" <> /tmp/noctalia.log 2>&1" binds { + Print { spawn "sh" "-c" "grim - | wl-copy"; } + Mod+Shift+S { spawn "sh" "-c" "grim - | satty --filename - --fullscreen --initial-tool crop --output-filename ~/Pictures/satty-$(date '+%Y%m%d-%H%M%S').png --early-exit"; } Mod+Return { spawn "${cfg.terminal}"; } Mod+D { spawn "sh" "-c" "${cfg.launcher}"; } Mod+Q { close-window; } @@ -260,9 +236,6 @@ in Mod+Equal { set-column-width "+10%"; } Mod+Shift+E { spawn "bemoji" "-t"; } - - Print { spawn "freeze-shot"; } - // Browsers Mod+W { spawn "firefox"; } Mod+Alt+W { spawn "tor-browser-vpn-podman"; } diff --git a/modules/nixos/bla.sh b/modules/nixos/bla.sh new file mode 100644 index 0000000..1270050 --- /dev/null +++ b/modules/nixos/bla.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +git clone https://github.com/liuw1535/antigravity2api-nodejs && cd antigravity2api-nodejs && cp .env.example .env && chmod +x start.sh + diff --git a/modules/nixos/brave-sandboxed.nix b/modules/nixos/brave-sandboxed.nix index ac1bab3..e435729 100644 --- a/modules/nixos/brave-sandboxed.nix +++ b/modules/nixos/brave-sandboxed.nix @@ -38,9 +38,27 @@ in extraBindMounts = lib.mkOption { type = lib.types.listOf lib.types.str; - default = []; + default = [ ]; description = "Extra paths to bind mount (read-write) into the sandbox"; }; + + useProxy = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether to use the wireproxy SOCKS5 proxy"; + }; + + proxyAddress = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1"; + description = "The address of the SOCKS5 proxy"; + }; + + proxyPort = lib.mkOption { + type = lib.types.int; + default = 1080; + description = "The port of the SOCKS5 proxy"; + }; }; config = lib.mkIf cfg.enable { @@ -48,14 +66,39 @@ in (final: prev: { brave-sandboxed = bwrapperPkgs.mkBwrapper { app = { - package = pkgs.symlinkJoin { - name = "brave-single-desktop"; - paths = [ prev.brave ]; - inherit (prev.brave) pname version meta; - postBuild = '' - rm $out/share/applications/com.brave.Browser.desktop - ''; - }; + package = + let + braveExe = lib.getExe prev.brave; + binName = builtins.baseNameOf braveExe; + in + pkgs.symlinkJoin { + name = "brave-wrapped"; + inherit (prev.brave) pname version meta; + paths = [ prev.brave ]; + nativeBuildInputs = [ pkgs.makeWrapper ]; + postBuild = '' + ${lib.optionalString cfg.useProxy '' + rm -f $out/bin/${binName} + makeWrapper ${braveExe} $out/bin/${binName} \ + --add-flags "--proxy-server=socks5://127.0.0.1:${toString cfg.proxyPort}" \ + --run ' + ( + SOCKET="/run/user/${toString config.users.users.${config.myModules.system.mainUser}.uid}/brave-proxy.sock" + for i in $(seq 1 50); do + if [ -S "$SOCKET" ]; then + ${pkgs.socat}/bin/socat TCP-LISTEN:${toString cfg.proxyPort},fork UNIX-CLIENT:"$SOCKET" + exit 0 + fi + sleep 0.1 + done + echo "Error: Brave proxy socket not found at $SOCKET" >&2 + exit 1 + ) & + ' + ''} + rm -f $out/share/applications/com.brave.Browser.desktop + ''; + }; # id = "brave-browser"; # Omit app.id to avoid potential bind errors (like Firefox) env = { # Propagate XDG_DATA_DIRS so GTK can find themes in user profile/system @@ -75,35 +118,31 @@ in unshareUser = true; unshareUts = false; unshareCgroup = false; - unsharePid = false; - unshareNet = false; - unshareIpc = false; + unsharePid = true; + unshareNet = cfg.useProxy; + unshareIpc = true; }; - fhsenv.bwrap.baseArgs = lib.mkForce [ - "--new-session" - "--proc /proc" - "--dev /dev" - "--dev-bind /dev/dri /dev/dri" - "--tmpfs /home" - "--tmpfs /mnt" - "--tmpfs /run" - "--ro-bind-try /run/current-system /run/current-system" - "--ro-bind-try /run/booted-system /run/booted-system" - "--ro-bind-try /run/opengl-driver /run/opengl-driver" - "--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32" - # Brave flags - "--setenv NIXOS_OZONE_WL \"1\"" - "--setenv NOTIFY_IGNORE_PORTAL 1" - # Bind policies for Theme - "--dir /etc/brave/policies/managed" - "--ro-bind ${bravePolicies} /etc/brave/policies/managed/policies.json" - # Fallback paths for Chromium/Chrome base - "--dir /etc/chromium/policies/managed" - "--ro-bind ${bravePolicies} /etc/chromium/policies/managed/policies.json" - "--dir /etc/opt/chrome/policies/managed" - "--ro-bind ${bravePolicies} /etc/opt/chrome/policies/managed/policies.json" - ]; + fhsenv.bwrap.baseArgs = lib.mkForce ( + sandboxUtils.mkCommonBindArgs { inherit config lib; } + ++ sandboxUtils.mkGamingBindArgs { } + ++ [ + "--tmpfs /mnt" + "--ro-bind-try /run/booted-system /run/booted-system" + "--setenv NIXOS_OZONE_WL \"1\"" + "--setenv NOTIFY_IGNORE_PORTAL 1" + # Bind policies for Theme + "--dir /etc/brave/policies/managed" + "--ro-bind ${bravePolicies} /etc/brave/policies/managed/policies.json" + # Fallback paths for Chromium/Chrome base + "--dir /etc/chromium/policies/managed" + "--ro-bind ${bravePolicies} /etc/chromium/policies/managed/policies.json" + "--dir /etc/opt/chrome/policies/managed" + "--ro-bind ${bravePolicies} /etc/opt/chrome/policies/managed/policies.json" + # Expose GPU device nodes + "--dev-bind /dev/dri /dev/dri" + ] + ); # Filesystem: Limited to Brave directories and Downloads mounts = { @@ -152,31 +191,35 @@ in ]; }; - fhsenv.bwrap.additionalArgs = [ - ''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.brave/bus" "$XDG_RUNTIME_DIR/bus"'' - ''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.brave/bus_system" /run/dbus/system_bus_socket'' - "--dir /run/systemd/resolve" - "--ro-bind-try /run/systemd/resolve /run/systemd/resolve" - "--bind-try /run/user/${toString config.users.users.${config.myModules.system.mainUser}.uid}/dconf /run/user/${toString config.users.users.${config.myModules.system.mainUser}.uid}/dconf" - ]; + fhsenv.bwrap.additionalArgs = + sandboxUtils.mkGuiBindArgs { } + ++ [ + ''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.brave/bus" "$XDG_RUNTIME_DIR/bus"'' + ''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.brave/bus_system" /run/dbus/system_bus_socket'' + "--bind-try /run/user/${ + toString config.users.users.${config.myModules.system.mainUser}.uid + }/dconf /run/user/${toString config.users.users.${config.myModules.system.mainUser}.uid}/dconf" + ] + ++ lib.optionals cfg.useProxy [ + "--bind-try /run/user/${ + toString config.users.users.${config.myModules.system.mainUser}.uid + }/brave-proxy.sock /run/user/${ + toString config.users.users.${config.myModules.system.mainUser}.uid + }/brave-proxy.sock" + ]; }; }) ]; - environment.systemPackages = [ - (pkgs.writeShellScriptBin "brave" '' - exec ${config.myModules.system.repoPath}/scripts/launch-vpn-app.sh ${pkgs.brave-sandboxed}/bin/brave "$@" - '') - (pkgs.makeDesktopItem { - name = "brave-vpn"; - desktopName = "Brave Web Browser"; - exec = "brave %U"; - icon = "brave-browser"; - categories = [ - "Network" - "WebBrowser" - ]; - }) - ]; + environment.systemPackages = [ pkgs.brave-sandboxed ]; + + systemd.user.services.brave-proxy-bridge = lib.mkIf cfg.useProxy { + description = "Bridge SOCKS5 proxy to UNIX socket for Brave Sandbox"; + wantedBy = [ "default.target" ]; + serviceConfig = { + ExecStart = "${pkgs.socat}/bin/socat UNIX-LISTEN:%t/brave-proxy.sock,fork TCP:${cfg.proxyAddress}:${toString cfg.proxyPort}"; + Restart = "always"; + }; + }; }; } \ No newline at end of file diff --git a/modules/nixos/cloudflare-firewall.nix b/modules/nixos/cloudflare-firewall.nix index 749624c..3e6760d 100644 --- a/modules/nixos/cloudflare-firewall.nix +++ b/modules/nixos/cloudflare-firewall.nix @@ -107,6 +107,9 @@ in # Allow established and related connections ct state established,related accept + + # Allow UDP for VPN handshakes (common ports) + udp dport { 51820, 1637, 1320 } accept # Allow ICMP (Ping) ip protocol icmp accept @@ -161,6 +164,10 @@ in # Allow established/related forwarding ct state established,related accept } + + chain output { + type filter hook output priority 0; policy accept; + } ''; }; diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix index bf17a67..1b2bd58 100644 --- a/modules/nixos/default.nix +++ b/modules/nixos/default.nix @@ -19,6 +19,7 @@ ./nginx.nix ./podman.nix ./browser-vpn.nix + ./wireproxy.nix ./ollama-rocm.nix ./open-webui.nix ./lutris-sandboxed.nix @@ -40,6 +41,8 @@ ./cosmic.nix ./steam-gamemode.nix ./redlib.nix + ./impermanence.nix ./auto-update.nix + ./openclaw.nix ]; } \ No newline at end of file diff --git a/modules/nixos/dns-over-tls.nix b/modules/nixos/dns-over-tls.nix index 8505ee6..6596254 100644 --- a/modules/nixos/dns-over-tls.nix +++ b/modules/nixos/dns-over-tls.nix @@ -5,8 +5,8 @@ # myModules.dnsOverTls = { # enable = true; # dnssec = true; # default: true -# primaryDns = [ "9.9.9.9" "1.1.1.1" ]; # default: Quad9 + Cloudflare -# fallbackDns = [ "1.1.1.1" "1.0.0.1" ]; # default: Cloudflare +# primaryDns = [ "9.9.9.9" "149.112.112.112" ]; # default: Quad9 +# fallbackDns = [ "9.9.9.9" "149.112.112.112" ]; # default: Quad9 # }; { @@ -34,19 +34,17 @@ in default = [ "9.9.9.9" "149.112.112.112" - "1.1.1.1" - "1.0.0.1" ]; - description = "Primary DNS servers (Quad9 + Cloudflare by default)"; + description = "Primary DNS servers (Quad9 by default)"; }; fallbackDns = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ - "1.1.1.1" - "1.0.0.1" + "9.9.9.9" + "149.112.112.112" ]; - description = "Fallback DNS servers"; + description = "Fallback DNS servers (Quad9 by default)"; }; }; diff --git a/modules/nixos/firefox-sandboxed.nix b/modules/nixos/firefox-sandboxed.nix index d8292c7..e10a501 100644 --- a/modules/nixos/firefox-sandboxed.nix +++ b/modules/nixos/firefox-sandboxed.nix @@ -16,7 +16,26 @@ let policies = { Preferences = { "xpinstall.signatures.required" = false; - }; + "network.manage-offline-status" = false; + "network.captive-portal-service.enabled" = false; + "widget.use-xdg-desktop-portal.file-picker" = 1; + } + // ( + if cfg.useProxy then + { + # Always 127.0.0.1: the internal socat listener binds locally + # inside the sandbox regardless of where cfg.proxyAddress lives + # on the host. Pointing Firefox at cfg.proxyAddress would fail + # when it isn't 127.0.0.1 because that address doesn't exist + # inside the isolated network namespace. + "network.proxy.socks" = "127.0.0.1"; + "network.proxy.socks_port" = cfg.proxyPort; + "network.proxy.type" = 1; + "network.proxy.socks_remote_dns" = true; + } + else + { } + ); }; } ); @@ -30,6 +49,24 @@ in default = [ ]; description = "Extra paths to bind mount (read-write) into the sandbox"; }; + + useProxy = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether to use the wireproxy SOCKS5 proxy"; + }; + + proxyAddress = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1"; + description = "The address of the SOCKS5 proxy"; + }; + + proxyPort = lib.mkOption { + type = lib.types.int; + default = 1080; + description = "The port of the SOCKS5 proxy"; + }; }; config = lib.mkIf cfg.enable { @@ -37,7 +74,28 @@ in (final: prev: { firefox-sandboxed = bwrapperPkgs.mkBwrapper { app = { - package = prev.firefox-esr; + package = + if cfg.useProxy then + pkgs.symlinkJoin { + name = "firefox-esr-proxy-wrapped"; + inherit (prev.firefox-esr) pname version meta; + paths = [ prev.firefox-esr ]; + nativeBuildInputs = [ pkgs.makeWrapper ]; + postBuild = + let + firefoxExe = lib.getExe prev.firefox-esr; + binName = builtins.baseNameOf firefoxExe; + in + '' + rm -f $out/bin/${binName} + makeWrapper ${firefoxExe} $out/bin/${binName} \ + --run '${pkgs.socat}/bin/socat TCP-LISTEN:${toString cfg.proxyPort},fork UNIX-CLIENT:/run/user/${ + toString config.users.users.${config.myModules.system.mainUser}.uid + }/firefox-proxy.sock &' + ''; + } + else + prev.firefox-esr; # Omit app.id to avoid document portal bind that fails on FUSE env = { MOZ_ENABLE_WAYLAND = "1"; @@ -57,9 +115,9 @@ in unshareUser = true; unshareUts = false; unshareCgroup = false; - unsharePid = false; - unshareNet = false; - unshareIpc = false; + unsharePid = true; + unshareNet = cfg.useProxy; + unshareIpc = true; }; fhsenv.bwrap.baseArgs = lib.mkForce ( @@ -74,6 +132,10 @@ in "--dir /etc/firefox" "--dir /etc/firefox/policies" "--ro-bind ${firefoxPolicies} /etc/firefox/policies/policies.json" + # Expose GPU device nodes so Firefox can use hardware acceleration + # (VA-API / VDPAU / WebGL). Without this it falls back to software + # rendering on pure-Wayland sessions. + "--dev-bind /dev/dri /dev/dri" ] ); @@ -117,17 +179,35 @@ in ]; }; - fhsenv.bwrap.additionalArgs = sandboxUtils.mkGuiBindArgs { } ++ [ - ''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.firefox/bus" "$XDG_RUNTIME_DIR/bus"'' - ''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.firefox/bus_system" /run/dbus/system_bus_socket'' - "--bind-try /run/user/${ - toString config.users.users.${config.myModules.system.mainUser}.uid - }/dconf /run/user/${toString config.users.users.${config.myModules.system.mainUser}.uid}/dconf" - ]; + fhsenv.bwrap.additionalArgs = + sandboxUtils.mkGuiBindArgs { } + ++ [ + ''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.firefox/bus" "$XDG_RUNTIME_DIR/bus"'' + ''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.firefox/bus_system" /run/dbus/system_bus_socket'' + "--bind-try /run/user/${ + toString config.users.users.${config.myModules.system.mainUser}.uid + }/dconf /run/user/${toString config.users.users.${config.myModules.system.mainUser}.uid}/dconf" + ] + ++ lib.optionals cfg.useProxy [ + "--bind-try /run/user/${ + toString config.users.users.${config.myModules.system.mainUser}.uid + }/firefox-proxy.sock /run/user/${ + toString config.users.users.${config.myModules.system.mainUser}.uid + }/firefox-proxy.sock" + ]; }; }) ]; environment.systemPackages = [ pkgs.firefox-sandboxed ]; + + systemd.user.services.firefox-proxy-bridge = lib.mkIf cfg.useProxy { + description = "Bridge SOCKS5 proxy to UNIX socket for Firefox Sandbox"; + wantedBy = [ "default.target" ]; + serviceConfig = { + ExecStart = "${pkgs.socat}/bin/socat UNIX-LISTEN:%t/firefox-proxy.sock,fork TCP:${cfg.proxyAddress}:${toString cfg.proxyPort}"; + Restart = "always"; + }; + }; }; } diff --git a/modules/nixos/impermanence.nix b/modules/nixos/impermanence.nix index 7cebe49..331216e 100644 --- a/modules/nixos/impermanence.nix +++ b/modules/nixos/impermanence.nix @@ -44,6 +44,7 @@ "/var/lib/nixarr" "/var/lib/nixflix" "/var/lib/authelia-main" + "/var/lib/openclaw" ]; files = [ @@ -101,6 +102,7 @@ ".local/share/icons" # Application icons ".local/bin" # User scripts ".local/share/qBittorrent" + ".local/share/openclaw" ".local/share/jellyfin-desktop" ".cache/jellyfin-desktop" ".local/share/zoxide" @@ -110,5 +112,7 @@ files = [ ]; }; + + }; } diff --git a/modules/nixos/lutris-sandboxed.nix b/modules/nixos/lutris-sandboxed.nix index 734d032..10f7f6f 100644 --- a/modules/nixos/lutris-sandboxed.nix +++ b/modules/nixos/lutris-sandboxed.nix @@ -29,6 +29,8 @@ in (final: prev: { lutris-sandboxed = bwrapperPkgs.mkBwrapper { app = { + id = "net.lutris.Lutris"; + renameDesktopFile = false; package = prev.lutris.override { extraPkgs = pkgs: [ pkgs.curl @@ -38,6 +40,9 @@ in pkgs.zstd pkgs.xz pkgs.p7zip + pkgs.libadwaita + pkgs.zenity + pkgs.gamescope pkgs.which pkgs.file pkgs.zenity @@ -53,7 +58,6 @@ in ]; }; isFhsenv = true; - id = "net.lutris.Lutris"; env = { WEBKIT_DISABLE_DMABUF_RENDERER = 1; APPIMAGE_EXTRACT_AND_RUN = 1; diff --git a/modules/nixos/media.nix b/modules/nixos/media.nix index ab0db95..d2dc9a8 100644 --- a/modules/nixos/media.nix +++ b/modules/nixos/media.nix @@ -18,6 +18,17 @@ let PGID = pgid; TZ = "Europe/Berlin"; }; + + # Host aliases so containers can communicate using public domain names locally (routes traffic to Nginx) + localAddHosts = [ + "--add-host=sonarr.ashisgreat.xyz:10.89.0.1" + "--add-host=radarr.ashisgreat.xyz:10.89.0.1" + "--add-host=prowlarr.ashisgreat.xyz:10.89.0.1" + "--add-host=torrent.ashisgreat.xyz:10.89.0.1" + "--add-host=jellyfin.ashisgreat.xyz:10.89.0.1" + "--add-host=jellyseer.ashisgreat.xyz:10.89.0.1" + "--add-host=auth.ashisgreat.xyz:10.89.0.1" + ]; in { options.myModules.media = { @@ -34,8 +45,8 @@ in # --- VPN Gateway --- vpn = { - image = "docker.io/qmcgaw/gluetun"; - labels = { "io.containers.autoupdate" = "registry"; }; + image = "docker.io/qmcgaw/gluetun:v3.41.1"; # Pinned: v3.42+ breaks on kernels without nfnetlink_conntrack (conntrack flush via netlink fails) + # No auto-update label — pinned to specific version intentionally # The VPN manages the ports for the attached containers ports = [ "127.0.0.1:8080:8080" # qBittorrent WebUI (Localhost only) @@ -58,16 +69,16 @@ in "--network=media" # It joins the bridge so others can talk to it "--ip=10.89.0.5" # Static IP for VPN/Flaresolverr "--network-alias=flaresolverr" # Allow other containers to reach Flaresolverr via VPN - "--add-host=sonarr:10.89.0.50" # Allow Prowlarr to reach Sonarr - "--add-host=radarr:10.89.0.51" # Allow Prowlarr to reach Radarr - "--add-host=prowlarr:127.0.0.1" # Prowlarr matches VPN IP for self-reference if needed - ]; + ] + ++ localAddHosts; }; # --- Torrent Client (Routed via VPN) --- torrent = { image = "lscr.io/linuxserver/qbittorrent:latest"; - labels = { "io.containers.autoupdate" = "registry"; }; + labels = { + "io.containers.autoupdate" = "registry"; + }; # VITAL: Reuse the VPN container's network stack extraOptions = [ "--network=container:vpn" ]; dependsOn = [ "vpn" ]; @@ -83,7 +94,9 @@ in # --- The Arr Stack --- prowlarr = { image = "lscr.io/linuxserver/prowlarr:latest"; - labels = { "io.containers.autoupdate" = "registry"; }; + labels = { + "io.containers.autoupdate" = "registry"; + }; extraOptions = [ "--network=container:vpn" ]; @@ -94,14 +107,15 @@ in sonarr = { image = "lscr.io/linuxserver/sonarr:latest"; - labels = { "io.containers.autoupdate" = "registry"; }; + labels = { + "io.containers.autoupdate" = "registry"; + }; extraOptions = [ "--network=media" "--ip=10.89.0.50" "--dns=8.8.8.8" - "--add-host=qbittorrent:10.89.0.5" - "--add-host=prowlarr:10.89.0.5" - ]; + ] + ++ localAddHosts; ports = [ "127.0.0.1:8989:8989" ]; environment = commonEnv; volumes = [ @@ -112,14 +126,15 @@ in radarr = { image = "lscr.io/linuxserver/radarr:latest"; - labels = { "io.containers.autoupdate" = "registry"; }; + labels = { + "io.containers.autoupdate" = "registry"; + }; extraOptions = [ "--network=media" "--ip=10.89.0.51" "--dns=8.8.8.8" - "--add-host=qbittorrent:10.89.0.5" - "--add-host=prowlarr:10.89.0.5" - ]; + ] + ++ localAddHosts; ports = [ "127.0.0.1:7878:7878" ]; environment = commonEnv; volumes = [ @@ -131,13 +146,16 @@ in # --- Media Server --- jellyfin = { image = "lscr.io/linuxserver/jellyfin:latest"; - labels = { "io.containers.autoupdate" = "registry"; }; + labels = { + "io.containers.autoupdate" = "registry"; + }; extraOptions = [ "--network=media" "--device=/dev/dri:/dev/dri" "--dns=8.8.8.8" "--ip=10.89.0.4" - ]; + ] + ++ localAddHosts; ports = [ "127.0.0.1:8096:8096" ]; environment = commonEnv; volumes = [ @@ -148,16 +166,16 @@ in jellyseerr = { image = "ghcr.io/seerr-team/seerr:latest"; # Migrated from jellyseerr (stale) to seerr (v3+) - labels = { "io.containers.autoupdate" = "registry"; }; + labels = { + "io.containers.autoupdate" = "registry"; + }; extraOptions = [ "--init" # Required for Seerr "--network=media" "--dns=8.8.8.8" "--ip=10.89.0.3" - "--add-host=sonarr:10.89.0.50" - "--add-host=radarr:10.89.0.51" - "--add-host=jellyfin:10.89.0.4" - ]; + ] + ++ localAddHosts; ports = [ "127.0.0.1:5055:5055" ]; environment = commonEnv; volumes = [ "/var/lib/nixarr/jellyseerr:/app/config" ]; @@ -165,7 +183,9 @@ in flaresolverr = { image = "ghcr.io/flaresolverr/flaresolverr:latest"; - labels = { "io.containers.autoupdate" = "registry"; }; + labels = { + "io.containers.autoupdate" = "registry"; + }; extraOptions = [ "--network=container:vpn" ]; dependsOn = [ "vpn" ]; environment = { diff --git a/modules/nixos/nginx.nix b/modules/nixos/nginx.nix index cfb9fac..0363369 100644 --- a/modules/nixos/nginx.nix +++ b/modules/nixos/nginx.nix @@ -45,8 +45,20 @@ in # Use the wildcard cert by default for these domains commonHttpConfig = '' + # WebSocket Upgrade Map + map $http_upgrade $connection_upgrade { + default upgrade; + "" close; + } + # HSTS 1 year add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; + + # Real IP configuration + set_real_ip_from 127.0.0.1; + set_real_ip_from 10.89.0.0/24; + real_ip_header X-Forwarded-For; + real_ip_recursive on; ''; }; }; diff --git a/modules/nixos/ollama-rocm.nix b/modules/nixos/ollama-rocm.nix index c6e5226..3415b65 100644 --- a/modules/nixos/ollama-rocm.nix +++ b/modules/nixos/ollama-rocm.nix @@ -164,7 +164,7 @@ in lib.optionalString (cfg.numThreads != null) "-e OLLAMA_NUM_THREADS=${toString cfg.numThreads}" } \ -v ${cfg.dataDir}:/root/.ollama:U \ - -p 127.0.0.1:${toString cfg.port}:11434 \ + -p ${toString cfg.port}:11434 \ ${cfg.image} ''; ExecStop = "${pkgs.podman}/bin/podman stop ollama"; diff --git a/modules/nixos/openclaw.nix b/modules/nixos/openclaw.nix new file mode 100644 index 0000000..f281700 --- /dev/null +++ b/modules/nixos/openclaw.nix @@ -0,0 +1,199 @@ +{ config, lib, pkgs, inputs, ... }: + +with lib; + +let + cfg = config.services.openclaw-service; + openclawPkg = inputs.nix-openclaw.packages.${pkgs.system}.default; +in +{ + options.services.openclaw-service = { + enable = mkEnableOption "OpenClaw AI Agent Service"; + + user = mkOption { + type = types.str; + default = "kafka"; + description = "User to run OpenClaw as"; + }; + + group = mkOption { + type = types.str; + default = "kafka"; + description = "Group to run OpenClaw as"; + }; + + port = mkOption { + type = types.int; + default = 18789; + description = "Port to listen on"; + }; + + dataDir = mkOption { + type = types.str; + default = "/var/lib/openclaw"; + description = "Directory for OpenClaw data and workspace"; + }; + }; + + config = mkIf cfg.enable { + users.users.${cfg.user} = { + isNormalUser = true; + linger = true; + uid = 1001; + group = cfg.group; + description = "OpenClaw Service User"; + home = cfg.dataDir; + createHome = true; + }; + + users.groups.${cfg.group} = { + gid = 1001; + }; + + sops.secrets."openclaw/discord_token" = { + owner = cfg.user; + group = cfg.group; + key = "discord_bot_token"; + }; + + sops.secrets."openclaw/glm_api_key" = { + owner = cfg.user; + group = cfg.group; + key = "glm_api_key"; + }; + + sops.secrets."openclaw/brave_api_key" = { + owner = cfg.user; + group = cfg.group; + key = "searxng_brave_api_key"; + }; + + # Ensure secrets exist in sops config, if not user needs to add them. + # We assume secrets.yaml has these keys or user will map them. + # The user had /run/secrets/openclaw-discord-token before. + + systemd.services.openclaw = { + description = "OpenClaw AI Agent"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + StateDirectory = "openclaw"; # Creates /var/lib/openclaw + WorkingDirectory = cfg.dataDir; + Restart = "always"; + RestartSec = "10s"; + + # Environment variables or config file generation + # OpenClaw seems to take config via a file or env vars. + # Based on previous flake, it used a config file. + # We can generate the config file in the ExecStartPre or rely on env vars if supported. + # The previous flake copied a config file. + + # Let's verify how openclaw takes config. + # It used OPENCLAW_CONFIG_DIR, OPENCLAW_DATA_DIR, OPENCLAW_WORKSPACE_DIR env vars. + }; + + environment = { + OPENCLAW_CONFIG_PATH = "${cfg.dataDir}/config/openclaw.json"; + OPENCLAW_HOME = "${cfg.dataDir}"; + OPENCLAW_DATA_DIR = "${cfg.dataDir}/data"; + OPENCLAW_WORKSPACE_DIR = "${cfg.dataDir}/workspace"; + OPENCLAW_GATEWAY_TOKEN = "openclaw-local-token"; + XDG_RUNTIME_DIR = "/run/user/1001"; + DBUS_SESSION_BUS_ADDRESS = "unix:path=/run/user/1001/bus"; + # We need to ensure these directories exist. + }; + + preStart = '' + mkdir -p ${cfg.dataDir}/config + mkdir -p ${cfg.dataDir}/data + mkdir -p ${cfg.dataDir}/workspace + + # Generate config.json + cat > ${cfg.dataDir}/config/openclaw.json <