# Clawdie Admin Panel — System Management UI **Purpose:** bsddialog-based system administration interface for Clawdie desktop users **Philosophy:** Pure shell + bsddialog (no external GUI toolkits), inspired by PC-BSD warden's CLI approach --- ## Overview **clawdie-admin** is a lightweight terminal UI that lets users manage: - **Clawdie service** — start/stop, view status - **Jails** — list, start/stop, open console (jexec) - **ZFS snapshots** — create, list, restore (inspired by Life-Preserver) - **System health** — CPU, RAM, disk usage, pool status - **Logs** — tail service and jail logs - **Configuration** — change assistant name, timezone, LLM provider --- ## Entry Points ### 1. Desktop Launcher / Panel Entry ``` Right-click system tray └─ "Clawdie Admin" └─ Launches /usr/local/bin/clawdie-admin ``` ### 2. CLI (Terminal) ```bash $ clawdie-admin # Opens interactive menu ``` ### 3. Desktop Launcher Launch `~/.local/share/applications/clawdie-admin.desktop` from the desktop or file manager. --- ## Menu Structure ### Main Menu ``` ┌─ Clawdie Admin Panel ──────────────────────────────────────┐ │ │ │ Quick Status │ │ ├─ Clawdie service: ✓ Running (pid 42315) │ │ ├─ ZFS pool (zroot): 15.2 GB / 100 GB (15% used) │ │ ├─ Memory: 2.3 GB / 8.0 GB (29%) │ │ ├─ CPU Load: 0.24 (1 min avg) │ │ ├─ Audio: ✓ OSS (Intel HDA) │ │ └─ Network: ✓ em0 (DHCP, 192.168.1.100) │ │ │ │ [ System Health ] [ Jails ] [ Snapshots ] │ │ [ Hardware ] [ Config ] [ Quit ] │ │ │ └─────────────────────────────────────────────────────────────┘ ``` ### System Health Submenu ``` ┌─ System Health ────────────────────────────────────────┐ │ │ │ CPU Load │ │ │ 1-min: 0.24 │ │ │ 5-min: 0.18 │ │ │ 15-min: 0.12 │ │ │ │ │ Memory Usage │ │ │ Used: 2.3 GB │ │ │ Free: 5.7 GB │ │ │ Cache: 0.8 GB │ │ │ │ │ ZFS Pool: zroot │ │ │ Used: 15.2 GB │ │ │ Free: 84.8 GB │ │ │ Pool health: ✓ ONLINE │ │ │ │ │ Clawdie Service │ │ │ Status: ✓ Running │ │ │ PID: 42315 │ │ │ Memory: 145 MB │ │ │ │ │ [ Back ] │ │ │ └────────────────────────────────────────────────────────┘ ``` ### Hardware Submenu ``` ┌─ Hardware Detection ───────────────────────────────────────┐ │ │ │ Audio Card │ │ │ Device: Intel HDA (pci0:27:0:0) │ │ │ Status: ✓ Working (OSS) │ │ │ Mixer: /dev/mixer0 │ │ │ │ │ Network Interfaces │ │ │ em0: Ethernet (DHCP) — 192.168.1.100 │ │ │ re0: Ethernet (not configured) │ │ │ Wireless: None detected │ │ │ Note: WiFi firmware can be installed post-boot │ │ │ │ │ Other Devices │ │ │ USB: 3 devices detected │ │ │ Bluetooth: Not found (optional feature, v1.0+) │ │ │ │ │ [ Configure Audio ] [ Network Setup ] [ Back ] │ │ │ └────────────────────────────────────────────────────────────┘ ``` **Post-Install Suggestions:** If WiFi detected but no firmware: ``` ⚠️ WiFi Device Detected: Intel AC 8260 → Install firmware: sudo pkg install iwm-firmware-8260 → Then restart: sudo service netif restart ``` If audio not working: ``` ⚠️ No audio output detected → Check: cat /dev/sndstat → Install: pkg install snd-hda-generic (for Intel/AMD) → Install: pkg install snd-usb-audio (for USB headsets) ``` --- ### Jails Submenu ``` ┌─ Clawdie Jails ────────────────────────────────────────┐ │ │ │ (N) Name Status Process Count IP Address │ │ 1) worker Running 4 10.0.0.101 │ │ 2) db Running ✓ Healthy 10.0.0.102 │ │ 3) cms Running ✓ Ready 10.0.0.103 │ │ 4) mgmt Stopped -- 10.0.0.104 │ │ │ │ Manage jail: │ │ [ Boot All ] [ Stop All ] [ Back ] │ │ │ │ Select jail number (1-4) to manage: │ │ >_ │ │ │ └────────────────────────────────────────────────────────┘ # After selecting jail (e.g., "1" for worker): ┌─ Jail: worker ──────────────────────────────────────────┐ │ │ │ Status: ✓ Running │ │ IPv4: 10.0.0.101 │ │ PID: 42350 │ │ Memory: 234 MB │ │ │ │ [ Start ] [ Stop ] [ Restart ] │ │ [ Console (jexec) ] [ Back ] │ │ │ └─────────────────────────────────────────────────────────┘ ``` ### Snapshots Submenu ``` ┌─ ZFS Snapshots ────────────────────────────────────────┐ │ │ │ Snapshot Management │ │ [ Create Snapshot ] │ │ [ List Snapshots ] │ │ [ Restore Snapshot ] │ │ │ │ Auto-Backup Status │ │ │ Last backup: 2026-03-23 18:45 UTC │ │ │ Interval: Daily at 02:00 │ │ │ Retention: 7 days │ │ │ Status: ✓ Enabled │ │ │ │ [ Back ] │ │ │ └────────────────────────────────────────────────────────┘ # After selecting "List Snapshots": ┌─ ZFS Snapshots on zroot ──────────────────────────────┐ │ │ │ Snapshot Created Size │ │ zroot@auto-2026-03-23 2026-03-23 02:00 2.3 GB │ │ zroot@auto-2026-03-22 2026-03-22 02:00 2.1 GB │ │ zroot@manual-20260323_1842 2026-03-23 18:42 1.8 GB │ │ zroot@manual-20260323_1200 2026-03-23 12:00 1.7 GB │ │ │ │ Total snapshots: 4 │ │ Total space: 7.9 GB │ │ │ │ Select snapshot to restore: (or press Back) │ │ >_ │ │ │ └────────────────────────────────────────────────────────┘ # After selecting snapshot: ┌─ Restore zroot@manual-20260323_1842 ──────────────────┐ │ │ │ ⚠ WARNING: This will rollback ZFS to the snapshot │ │ point. All changes since creation will be lost. │ │ │ │ Created: 2026-03-23 18:42 UTC │ │ Size: 1.8 GB │ │ Systems affected: postgres, apps │ │ │ │ [ Confirm Restore ] [ Cancel ] │ │ │ └────────────────────────────────────────────────────────┘ ``` ### Logs Submenu ``` ┌─ System Logs ──────────────────────────────────────────┐ │ │ │ View logs from: │ │ [ Clawdie Service (syslog) ] │ │ [ Clawdie Service (stdout/stderr) ] │ │ [ Worker Jail Console ] │ │ [ Database Jail Console ] │ │ [ System Messages (/var/log/messages) ] │ │ │ │ [ Back ] │ │ │ └────────────────────────────────────────────────────────┘ # After selecting log: ┌─ Clawdie Service Log (last 50 lines) ──────────────────┐ │ │ │ [2026-03-23 23:15:22] Agent startup complete │ │ [2026-03-23 23:15:21] Jails ready: worker, db, cms │ │ [2026-03-23 23:15:18] PostgreSQL health check: OK │ │ [2026-03-23 23:15:15] Nginx config validated │ │ ... │ │ [2026-03-23 23:15:01] rc.d clawdie service start │ │ │ │ [ Refresh ] [ Tail (follow mode) ] [ Back ] │ │ │ └────────────────────────────────────────────────────────┘ ``` ### Config Submenu ``` ┌─ Configuration ────────────────────────────────────────┐ │ │ │ Current Settings │ │ ├─ Assistant Name: Clawdie │ │ ├─ Domain: clawdie.local │ │ ├─ Timezone: Europe/Ljubljana │ │ ├─ LLM Provider: Anthropic │ │ └─ Telegram: ✓ Configured │ │ │ │ [ Change Assistant Name ] │ │ [ Change Timezone ] │ │ [ Change LLM Provider ] │ │ [ Export .env ] │ │ [ View .env (sensitive) ] │ │ [ Back ] │ │ │ └────────────────────────────────────────────────────────┘ ``` --- ## Implementation (clawdie-admin.sh) ### File Locations ``` /usr/local/bin/clawdie-admin ← wrapper script (user-callable) /usr/local/libexec/clawdie-admin.sh ← main menu logic (sourced) /usr/local/libexec/clawdie-lib-admin.sh ← helper functions (jail, snap, log ops) ``` ### Wrapper Script ```bash #!/bin/sh # /usr/local/bin/clawdie-admin # Make sure we're running as clawdie user (or via sudo for root ops) if [ "$(id -u)" != 1001 ]; then # If run as root, switch to clawdie user if [ "$(id -u)" = 0 ]; then exec su - clawdie -c "clawdie-admin" fi echo "Error: must run as clawdie user or root" exit 1 fi # Source main menu . /usr/local/libexec/clawdie-admin.sh # Start menu clawdie_admin_main_menu ``` ### Main Menu Function ```bash #!/bin/sh # /usr/local/libexec/clawdie-admin.sh . /usr/local/libexec/clawdie-lib-admin.sh clawdie_admin_main_menu() { while true; do # Refresh status each iteration STATUS=$(clawdie_admin_quick_status) CHOICE=$(bsddialog --title "Clawdie Admin Panel" \ --menu "Quick Status:\n$STATUS" 20 70 0 \ "1" "System Health" \ "2" "Jails" \ "3" "Snapshots" \ "4" "Logs" \ "5" "Configuration" \ "0" "Quit" \ 2>&1 >/dev/tty) case $CHOICE in 1) clawdie_admin_health ;; 2) clawdie_admin_jails ;; 3) clawdie_admin_snapshots ;; 4) clawdie_admin_logs ;; 5) clawdie_admin_config ;; 0) break ;; *) ;; esac done } clawdie_admin_quick_status() { # Returns 4-line status string for main menu display # Check if clawdie service running if service clawdie status > /dev/null 2>&1; then PID=$(service clawdie status | grep -oP 'pid \K\d+' || echo "?") SERVICE_STATUS="✓ Running (pid $PID)" else SERVICE_STATUS="✗ Stopped" fi # ZFS pool usage POOL_USAGE=$(zfs list -H -o used,available zroot 2>/dev/null | awk '{ used = $1 / 1024 / 1024 / 1024 avail = $2 / 1024 / 1024 / 1024 total = used + avail pct = int(used / total * 100) printf "%.1f GB / %.1f GB (%d%%)", used, total, pct }') # Memory usage MEM=$(free -m | grep "^Mem:" | awk '{ used = $3 total = $2 pct = int(used / total * 100) printf "%.1f GB / %.1f GB (%d%%)", used/1024, total/1024, pct }') # CPU load LOAD=$(uptime | sed 's/.*load average: //' | awk '{print $1}') echo "Clawdie: $SERVICE_STATUS" echo "ZFS pool (zroot): $POOL_USAGE" echo "Memory: $MEM" echo "CPU Load: $LOAD (1 min)" } ``` ### Helper Library Functions ```bash #!/bin/sh # /usr/local/libexec/clawdie-lib-admin.sh # Jail operations clawdie_jail_list() { jls -N name hostname state ip4.addr 2>/dev/null | grep -E "^(worker|db|cms|mgmt)" || echo "No jails found" } clawdie_jail_status() { local jail=$1 jls -j "$jail" state 2>/dev/null || echo "unknown" } clawdie_jail_start() { local jail=$1 service jail onestart "$jail" 2>&1 | tail -5 } clawdie_jail_stop() { local jail=$1 service jail onestop "$jail" 2>&1 | tail -5 } clawdie_jail_console() { local jail=$1 jexec "$jail" /bin/sh } # ZFS operations clawdie_snapshot_create() { local label=$1 local snapshot="zroot@manual-$(date +%Y%m%d_%H%M)" zfs snapshot "$snapshot" 2>&1 echo "Created: $snapshot" } clawdie_snapshot_list() { zfs list -t snapshot -r zroot 2>/dev/null | grep "^zroot" || echo "No snapshots" } clawdie_snapshot_restore() { local snapshot=$1 bsddialog --title "Confirm Restore" \ --yesno "Restore $snapshot? This will rollback all changes since creation." 0 0 if [ $? -eq 0 ]; then zfs rollback "$snapshot" 2>&1 | head -20 echo "Rollback complete. System restart recommended." fi } # Log operations clawdie_log_service() { tail -50 /var/log/clawdie.log 2>/dev/null || echo "No log found" } clawdie_log_jail() { local jail=$1 jexec "$jail" tail -50 /var/log/messages 2>/dev/null || echo "No jail log" } # Config operations clawdie_config_read_env() { # Read current .env without exposing secrets grep -E "^(ASSISTANT_NAME|AGENT_DOMAIN|TZ|LLM_PROVIDER)" \ /home/clawdie/clawdie-ai/.env 2>/dev/null || echo "Config not found" } clawdie_config_change_name() { bsddialog --title "Change Assistant Name" \ --inputbox "Enter new assistant name:" 0 0 "$CURRENT_NAME" 2>&1 | \ xargs -I {} sed -i '' "s/^ASSISTANT_NAME=.*/ASSISTANT_NAME={}/" \ /home/clawdie/clawdie-ai/.env } ``` --- ## Usage Examples ### Example 1: Start Worker Jail ```bash # User: Jails → select "1" (worker) → [Start] clawdie_jail_start worker # Output: "Jail worker started" ``` ### Example 2: View Recent Logs ```bash # User: Logs → [Clawdie Service] → shows tail -50 /var/log/clawdie.log tail -50 /var/log/clawdie.log ``` ### Example 3: Create Manual Snapshot ```bash # User: Snapshots → [Create Snapshot] clawdie_snapshot_create "manual" # Output: "Created: zroot@manual-20260323_2145" ``` --- ## Desktop Integration ### Tray Applet **Binary:** `/usr/local/libexec/clawdie-tray-applet` (shell script) ```bash #!/bin/sh # Updates tray icon every 5 seconds RUNNING_ICON="/usr/local/share/icons/clawdie-running.png" STOPPED_ICON="/usr/local/share/icons/clawdie-stopped.png" while true; do if service clawdie status > /dev/null 2>&1; then echo "ICON:$RUNNING_ICON" echo "LABEL:Clawdie" else echo "ICON:$STOPPED_ICON" echo "LABEL:Clawdie (offline)" fi sleep 5 done ``` **Right-click binding:** `Exec=/usr/local/bin/clawdie-admin` --- ## Permissions & Security ### User (clawdie) Operations - Jail status queries: via `jls` (unprivileged) - Logs: read `/var/log/clawdie.log` (user-owned) - Config: read `.env` (user-owned) ### Privileged Operations (via sudo) - Jail start/stop: requires root via `sudo service jail ...` - ZFS snapshot/rollback: requires root via `sudo zfs ...` - System restart: requires root via `sudo shutdown ...` **sudo Setup (automatic in firstboot.sh):** ```bash # /usr/local/etc/sudoers.d/clawdie-admin # Clawdie admin operations %wheel ALL=(ALL) NOPASSWD: /usr/local/libexec/clawdie-lib-admin.sh %wheel ALL=(ALL) NOPASSWD: /usr/sbin/service %wheel ALL=(ALL) NOPASSWD: /usr/sbin/zfs %wheel ALL=(ALL) NOPASSWD: /usr/sbin/jail %wheel ALL=(ALL) NOPASSWD: /usr/bin/killall ``` --- ## Accessibility - **Keyboard navigation:** bsddialog supports tab/arrow keys, Enter - **High contrast:** Inherits the active desktop theme settings - **No mouse required:** All functions accessible via keyboard - **Screen reader:** Standard bsddialog output readable by screen readers --- ## Performance Considerations - **Startup:** <200 ms (pure shell, no Java/Python startup overhead) - **Refresh rate:** 5-second poll for status (minimal CPU) - **Memory:** <5 MB footprint - **Concurrency:** Lock file prevents multiple instances (`/tmp/clawdie-admin.lock`) --- ## Testing Checklist - [ ] `clawdie-admin` launches from the desktop tray or launcher - [ ] `clawdie-admin` launches from terminal - [ ] System Health displays correct CPU/memory/disk stats - [ ] Jails submenu lists all jails (worker, db, cms) - [ ] Can toggle jail start/stop with sudo confirmation - [ ] Can open jail console via jexec - [ ] Snapshots create/list/restore work with sudo - [ ] Logs display correctly (tailed, not truncated) - [ ] Config display matches current .env - [ ] No secrets logged to console - [ ] Multiple concurrent instances prevented (lock file) - [ ] Menu navigation works with keyboard only --- ## Future Enhancements 1. **Backup scheduling UI** — configure daily/weekly backups 2. **Network config** — change IP addresses, DNS 3. **Service management** — start/stop nginx, PostgreSQL within jails 4. **Metrics export** — export system stats to Grafana 5. **Terminal multiplexing** — tmux split for multiple jails open simultaneously