diff --git a/README.md b/README.md index c508295..dacc440 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ Curated 1-click self-hosted application catalog with hardware-accelerated Immich ### 2. User & Administrator Guides - [Installation & Live ISO Guide](docs/guides/installation.md) +- [Testing NaxOS on macOS (QEMU & Local)](docs/guides/testing-on-mac.md) - [Migration Guide: Transitioning from `nixos-lukas` to NaxOS](docs/guides/migration-from-nixos-lukas.md) ### 3. Developer Manual diff --git a/docs/guides/testing-on-mac.md b/docs/guides/testing-on-mac.md new file mode 100644 index 0000000..c7fe62e --- /dev/null +++ b/docs/guides/testing-on-mac.md @@ -0,0 +1,284 @@ +# 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. diff --git a/docs/images/app-store.png b/docs/images/app-store.png index edbf637..353f166 100644 Binary files a/docs/images/app-store.png and b/docs/images/app-store.png differ diff --git a/docs/images/dashboard-overview.png b/docs/images/dashboard-overview.png index e86d6ea..e1c4519 100644 Binary files a/docs/images/dashboard-overview.png and b/docs/images/dashboard-overview.png differ diff --git a/docs/images/migration-wizard.png b/docs/images/migration-wizard.png index f092372..4313474 100644 Binary files a/docs/images/migration-wizard.png and b/docs/images/migration-wizard.png differ diff --git a/docs/images/perses-analytics.png b/docs/images/perses-analytics.png index 26b3e6f..35b5028 100644 Binary files a/docs/images/perses-analytics.png and b/docs/images/perses-analytics.png differ diff --git a/docs/images/storage-management.png b/docs/images/storage-management.png index e8c39cc..3aecf1b 100644 Binary files a/docs/images/storage-management.png and b/docs/images/storage-management.png differ diff --git a/docs/images/system-logs.png b/docs/images/system-logs.png index 3d7da1e..dfdd9b1 100644 Binary files a/docs/images/system-logs.png and b/docs/images/system-logs.png differ diff --git a/docs/images/system-telemetry.png b/docs/images/system-telemetry.png index 26b3e6f..35b5028 100644 Binary files a/docs/images/system-telemetry.png and b/docs/images/system-telemetry.png differ