# Testing NaxOS on macOS This guide provides step-by-step instructions for testing and running the complete **NaxOS** appliance on macOS. It covers full live virtual machine testing with QEMU, rapid local frontend/API development, and headless automated verification. --- ## Testing Methods Overview | Method | Target Use Case | Requirements | | :--- | :--- | :--- | | **Method 1: Full Live QEMU VM** | Complete appliance test: Linux 6.18 kernel, real OpenZFS pool `tank`, live `naxos-api` daemon, and `naxos-ui` web interface. | `qemu-system-x86_64` (Homebrew or Nix) | | **Method 2: Local UI / API Dev** | Rapid UI development, styling iteration, and component testing on macOS without a VM. | Node.js 20+, npm | | **Method 3: Headless Verification** | Automated end-to-end browser capture and visual validation. | Python 3 + Playwright or Chromium | --- ## Method 1: Full Appliance VM Testing via QEMU This method boots the actual NaxOS NixOS 26.05 operating system with Linux kernel 6.18, creates a virtual target disk, provisions a real OpenZFS pool (`tank`), and runs the full API daemon and Web UI with port forwarding to macOS. ### 1. Prerequisites Install QEMU on macOS using Homebrew (or via Nix): ```bash # Using Homebrew brew install qemu # Or if you use Nix on macOS: nix-shell -p qemu ``` Verify that `qemu-system-x86_64` is available: ```bash qemu-system-x86_64 --version ``` > [!NOTE] > **Apple Silicon (M1/M2/M3/M4) Note**: When running `qemu-system-x86_64` on Apple Silicon Macs, QEMU uses TCG (Tiny Code Generator) software emulation. Because it is emulating x86_64 on ARM64, boot times will take approximately 15–30 seconds. --- ### 2. Download the NaxOS Installation Media Download the official bootable ISO from the Gitea package registry: ```bash curl -fSL -o /tmp/naxos-installer.iso \ "https://git.lholz.de/api/packages/naxos/generic/naxos-installer-iso/1.0.0/naxos-installer-x86_64-linux.iso" ``` --- ### 3. Prepare Boot Artifacts & Virtual Drive Extract the direct-boot kernel and initrd from the ISO (or boot directly from the ISO cdrom): ```bash # Create a 20 GB QCOW2 virtual drive for OpenZFS testing qemu-img create -f qcow2 /tmp/naxos-target-disk.qcow2 20G # Mount or extract the ISO to retrieve the kernel and initrd mkdir -p /tmp/naxos-iso-mnt /tmp/naxos-boot hdiutil attach -nobrowse -readonly -mountpoint /tmp/naxos-iso-mnt /tmp/naxos-installer.iso cp -R /tmp/naxos-iso-mnt/boot /tmp/naxos-boot/ hdiutil detach /tmp/naxos-iso-mnt ``` Find the extracted kernel and initrd paths: ```bash KERNEL=$(find /tmp/naxos-boot -name bzImage | head -n 1) INITRD=$(find /tmp/naxos-boot -name initrd | head -n 1) echo "Kernel: $KERNEL" echo "Initrd: $INITRD" ``` --- ### 4. Launch the NaxOS Virtual Appliance Start QEMU with: - **RAM**: 2048 MB (2 GB) - **CPU**: 2 cores (`-smp 2`) - **Storage**: VirtIO disk attached to `/tmp/naxos-target-disk.qcow2` (appears as `/dev/vda` in the VM) - **Networking**: User-mode networking with port forwarding: - Host port `18080` -> VM port `80` (NaxOS Web UI) - Host port `18443` -> VM port `8443` (NaxOS HTTPS / API) - **Serial Console**: Stdio redirected to your terminal for real-time kernel and shell access. ```bash qemu-system-x86_64 \ -m 2048 \ -smp 2 \ -kernel "$KERNEL" \ -initrd "$INITRD" \ -append "init=/nix/store/*-nixos-system-nixos-*/init console=ttyS0,115200n8 loglevel=6" \ -drive file=/tmp/naxos-target-disk.qcow2,if=virtio,format=qcow2 \ -net nic,model=virtio \ -net user,hostfwd=tcp::18443-:8443,hostfwd=tcp::18080-:80 \ -nographic ``` Once booted, press **Enter** to get the root prompt: ```text <<< Welcome to NixOS 26.05! >>> [root@nixos:~]# ``` --- ### 5. Initialize the OpenZFS Storage Pool in the VM Inside the VM terminal, create the real OpenZFS pool `tank` on the virtual drive `/dev/vda`: ```bash # Create OpenZFS pool zpool create tank /dev/vda -f # Create standard appliance datasets zfs create tank/media zfs create tank/backup zfs create tank/container # Verify pool health and status zpool status zpool list zfs list ``` You should see: ```text NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT tank 19.5G 832K 19.5G - - 0% 0% 1.00x ONLINE - ``` --- ### 6. Verify Real System Metrics & Declarative Rebuild The NaxOS Management Daemon (`naxos-api`) queries real kernel interfaces rather than mocked data: - **ZFS ARC Cache**: `/proc/spl/kstat/zfs/arcstats` - **Uptime**: `/proc/uptime` - **Memory**: `os.totalmem()` and `os.freemem()` - **Disk I/O**: `/proc/diskstats` - **Journal Logs**: Real `journalctl -n 100 -o json --no-pager` Test the API daemon endpoints from inside the VM or from macOS: ```bash # Check system metrics from your Mac: curl http://localhost:18443/api/v1/system/metrics # Check real ZFS pools: curl http://localhost:18443/api/v1/storage/pools # Check active system services: curl http://localhost:18443/api/v1/system/services ``` #### Testing "Apply Changes" (Live SSE Stream) Trigger a declarative system rebuild from macOS: ```bash curl -N -X POST http://localhost:18443/api/v1/system/rebuild ``` You will receive real Server-Sent Events (SSE) streaming live steps: 1. `validating` - Synthesizing declarative NixOS configuration. 2. `building` - Executing `/run/current-system/sw/bin/nixos-rebuild dry-activate`. 3. `reloading` - Applying dataset quotas and reloading Samba shares. 4. `completed` - Generation activated successfully. --- ### 7. Accessing the Web Dashboard Open your web browser on macOS and navigate to: ```text http://localhost:18080 ``` You will see: 1. **Glassmorphism Appliance Dashboard**: Live storage capacity, real ARC hit ratio (100%), ARC cache size, CPU and memory gauges. 2. **Storage Management**: Visual hierarchy of pool `tank`, datasets (`media`, `backup`, `container`), and snapshots. 3. **App Store**: Curated 1-click catalog (Immich, Jellyfin, Nextcloud, Vaultwarden). 4. **Apply Changes Modal**: Interactive rebuild viewer streaming real systemd activation logs. --- ### 8. Clean VM Shutdown To cleanly unmount OpenZFS datasets, export the storage pool, and power down the virtual machine, execute inside the VM console: ```bash poweroff ``` Or press `Ctrl+A` then `X` to exit QEMU immediately. --- ## Method 2: Local UI & API Rapid Development To develop or preview the Web UI directly on macOS without launching QEMU: ### 1. Build and Run the Web UI ```bash cd naxos-ui # Install dependencies npm install # Start Vite development server npm run dev ``` Open `http://localhost:5173` in your browser. ### 2. Previewing the Production SPA Build ```bash cd naxos-ui npm run build npm run preview -- --port 3000 ``` Open `http://localhost:3000` to inspect the production asset bundle. ### 3. Running the Management Daemon Locally (Mock Fallback) If running `naxos-api` on macOS outside of Linux, it automatically falls back gracefully when Linux-specific paths (`/proc/spl/kstat/zfs/arcstats`, `zpool`) are unavailable: ```bash cd naxos-api npm install npm run build npm start ``` The API daemon listens on `http://localhost:8088`. --- ## Method 3: Headless Automated Browser Verification To run headless end-to-end tests or re-generate screenshots automatically: ```bash # Using Python Playwright python3 -m pip install playwright python3 -m playwright install chromium # Run headless screenshot verification python3 - << 'EOF' from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page(viewport={"width": 1440, "height": 900}) page.goto("http://localhost:18080") page.wait_for_selector("text=System Healthy") page.screenshot(path="test-overview.png") print("Screenshot captured successfully!") browser.close() EOF ``` --- ## Troubleshooting & FAQ ### Port `18080` or `18443` is already in use Check which process is listening on the port: ```bash lsof -i :18080 lsof -i :18443 ``` Kill the stale process or change the `-net user,hostfwd=tcp::18080-:80` parameter in the QEMU launch command to an alternate port (e.g. `hostfwd=tcp::28080-:80`). ### Strict MIME Type Errors (`Failed to load module script`) Browsers enforce strict MIME checking for ES modules (`Content-Type: text/javascript; charset=utf-8`). If serving files via Python or custom static servers, ensure MIME types are explicitly configured: ```python import mimetypes mimetypes.add_type('application/javascript', '.js') mimetypes.add_type('text/css', '.css') ``` The standard `naxos-api` and production `nginx` containers already include appropriate MIME headers out of the box.