# GuruRMM - macOS Agent Phase 1 Implementation ## User - **User:** Mike Swanson (mike) - **Machine:** Mikes-MacBook-Air - **Role:** admin - **Date:** 2026-05-12 - **Session Duration:** ~2 hours (06:20 UTC - 13:35 UTC) ## Session Summary Successfully implemented macOS agent support for GuruRMM, completing Phase 1 of the macOS deployment plan. The agent builds, installs, and enrolls correctly, but encounters code signing restrictions on Apple Silicon Macs. All infrastructure and code changes are committed and deployed. ### What Was Accomplished 1. **Agent Platform Storage** - Implemented plist-based configuration storage for macOS - Created `agent/src/macos_storage.rs` with read/write functions for site.plist - Updated `agent/src/registry.rs` to route macOS → plist storage via conditional compilation - Added plist crate dependency to `agent/Cargo.toml` for macOS targets - Integrated macos_storage module into `agent/src/main.rs` with platform-specific logging 2. **Server Install Endpoints** - Added macOS installer script generation - Created `install_script_macos()` in `server/src/api/install.rs` - generates bash script with LaunchDaemon - Created `download_macos()` endpoint - serves site-configured macOS binaries with site-code trailer - Updated `build_site_binary()` helper to handle "macos" and "macos-x86_64" platforms - Registered routes in `server/src/main.rs`: `/install/:site_code/macos` and `/download/macos` 3. **Binary Builds** - Built both architectures on this Mac - Apple Silicon (aarch64-apple-darwin): 3.3MB release binary - Intel (x86_64-apple-darwin): 3.9MB release binary - Both uploaded to Jupiter (.30) at `/var/www/gururmm/downloads/` 4. **Server Deployment** - Manually rebuilt and restarted server with new endpoints - Resolved sqlx offline cache issues by building with DATABASE_URL - Server now responds to `/install/:site_code/macos` with functional bash installer - Download endpoint serves binaries with site-code trailers appended 5. **Infrastructure Verification** - Confirmed Cloudflare WAF rule already exists (created 2026-05-11 by Howard) - Rule skips bot detection on `/install/*` paths - curl installs work correctly - Tested full install flow on this Mac - script executes successfully - LaunchDaemon plist created correctly, files deployed properly ### Key Decisions Made **Architecture Decision: plist Storage (not Keychain)** - **Rationale:** Simplicity, no external dependencies, matches Windows registry pattern - Site ID stored at `/usr/local/etc/gururmm/site.plist` (write-once artifact) - Agent key written after enrollment, same as Windows/Linux flow - Permissions: 600 on config file, 755 on binary, 644 on LaunchDaemon plist **Build Location: Native Mac Builds (not cross-compile)** - Built directly on this MacBook Air (Mikes-MacBook-Air.local) - Avoids cross-compilation complexity for initial Phase 1 - Future: Add to build-agents.sh via SSH to this Mac or cross-compile setup **Service Management: LaunchDaemon (not systemd)** - Uses macOS native service management - Plist at `/Library/LaunchDaemons/com.azcomputerguru.gururmm-agent.plist` - `RunAtLoad: true` and `KeepAlive` with `SuccessfulExit: false` for auto-restart - Logs to `/usr/local/var/log/gururmm-agent.log` **Installation: Shell Script One-Liner (not .pkg)** - Follows same pattern as Linux installer - Single curl pipe to bash: `curl -fsSL https://rmm.azcomputerguru.com/install/SITE-CODE/macos | sudo bash` - No separate .pkg installer needed for Phase 1 - Future Phase 3: Consider .pkg with signed/notarized binary ### Problems Encountered and Solutions **Problem 1: Server Build Failed - Missing sqlx Cache** - **Issue:** `cargo check` failed with "SQLX_OFFLINE=true but no cached data" - **Root Cause:** No `.sqlx/` directory, and DATABASE_URL not set in environment - **Solution:** Built with `DATABASE_URL="postgresql://gururmm:43617ebf7eb242e814ca9988cc4df5ad@localhost/gururmm"` on Jupiter - **Result:** Server compiled successfully in 3m 16s **Problem 2: macOS Binaries Deleted by Build Pipeline** - **Issue:** Uploaded binaries removed after automated build completed - **Root Cause:** Build pipeline cleanup script removes old files, didn't recognize macOS binaries - **Solution:** Re-uploaded binaries after build completed - **Future Fix:** Update `build-agents.sh` to preserve macOS binaries or build them as part of pipeline **Problem 3: Code Signing Blocker on Apple Silicon** - **Issue:** Agent installs successfully but crashes immediately with SIGKILL (-9) - **Root Cause:** Adhoc-signed (unsigned) binaries cannot execute on Apple Silicon Macs - **Diagnosis:** `codesign -dv` shows "Signature=adhoc, TeamIdentifier=not set" - **Attempted Workarounds:** - `xattr -c` to remove all extended attributes - didn't help - `spctl --add` to allow binary - command no longer supported on modern macOS - **Current Status:** Installation works on Intel Macs or with Gatekeeper disabled - **Next Steps:** Requires Apple Developer Program enrollment ($99/year) + code signing certificate **Problem 4: Build Pipeline Still Running During Testing** - **Issue:** Pluto (Windows) build was still in progress, blocking server restart via webhook - **Root Cause:** Windows builds take 10-15 minutes with full legacy/x86/debug variants - **Solution:** Manually rebuilt server with `cargo build --release` and restarted via systemctl - **Result:** Server available for testing within 3 minutes instead of waiting for full pipeline ## Code Changes ### Files Created 1. **agent/src/macos_storage.rs** (108 lines) - `read_site_id()` - reads site_id from plist - `read_agent_key()` - reads agent_key if enrolled - `write_agent_key()` - writes agent_key after enrollment - Uses `plist` crate for XML property list parsing 2. **docs/macos-agent-implementation-plan.md** (760 lines) - Comprehensive 3-phase implementation plan - Phase 1: Minimal viable (4-6h) - unsigned shell installer - Phase 2: Dashboard + docs (2h) - Phase 3: Code signing + notarization (6-8h) ### Files Modified 1. **agent/Cargo.toml** - Added `plist = "1.7"` dependency for macOS targets only - Added to `[target.'cfg(target_os = "macos")'.dependencies]` section 2. **agent/src/registry.rs** - Added macOS conditional compilation blocks - Routes `read_site_id()`, `read_agent_key()`, `write_agent_key()` to `macos_storage` module - Linux/other Unix platforms remain no-op (TOML fallback) 3. **agent/src/main.rs** - Added `#[cfg(target_os = "macos")] mod macos_storage;` module declaration - Updated `resolve_windows_config()` to work on both Windows AND macOS - Changed conditional compilation from `#[cfg(windows)]` to `#[cfg(any(windows, target_os = "macos"))]` - Added platform-specific info log for plist configuration 4. **server/src/api/install.rs** (183 lines added) - `install_script_macos()` function (lines 758-878) - generates bash installer script - `download_macos()` function (lines 418-440) - serves macOS binary with site-code trailer - Updated `build_site_binary()` - added "macos" and "macos-x86_64" platform cases 5. **server/src/main.rs** - Registered `/install/:site_code/macos` route → `install_script_macos` - Registered `/install/:site_code/download/macos` route → `download_macos` - Added background reaper task (from upstream merge - unrelated to macOS work) 6. **projects/msp-tools/guru-rmm/PROJECT_STATE.md** - Released session lock (removed IN_PROGRESS entry) - Added macOS agent to component state table - Updated server state to "BUILDING" during rebuild - Added three entries to Recent Changes log ## Commands Run ### Build Commands ```bash # Build Apple Silicon binary (native on this Mac) cd /Users/azcomputerguru/ClaudeTools/projects/msp-tools/guru-rmm/agent cargo build --release # Result: target/release/gururmm-agent (3.3MB, arm64) # Build Intel binary (cross-compile) cargo build --release --target x86_64-apple-darwin # Result: target/x86_64-apple-darwin/release/gururmm-agent (3.9MB, x86_64) # Server build on Jupiter (manual) ssh guru@172.16.3.30 "cd /home/guru/gururmm/server && sudo -i bash -c 'source /home/guru/.cargo/env && cd /home/guru/gururmm/server && DATABASE_URL=\"postgresql://gururmm:43617ebf7eb242e814ca9988cc4df5ad@localhost/gururmm\" cargo build --release'" # Duration: 3m 16s ``` ### Deployment Commands ```bash # Upload macOS binaries to Jupiter scp agent/target/release/gururmm-agent guru@172.16.3.30:/tmp/macos-aarch64 scp agent/target/x86_64-apple-darwin/release/gururmm-agent guru@172.16.3.30:/tmp/macos-x86_64 # Move to downloads directory ssh guru@172.16.3.30 "sudo mv /tmp/macos-aarch64 /var/www/gururmm/downloads/gururmm-agent-macos-aarch64-latest && sudo mv /tmp/macos-x86_64 /var/www/gururmm/downloads/gururmm-agent-macos-x86_64-latest && sudo chmod 755 /var/www/gururmm/downloads/gururmm-agent-macos-*-latest" # Deploy and restart server ssh guru@172.16.3.30 "sudo systemctl stop gururmm-server && sudo cp /home/guru/gururmm/server/target/release/gururmm-server /opt/gururmm/gururmm-server && sudo systemctl start gururmm-server" ``` ### Test Commands ```bash # Test install script endpoint curl -s "https://rmm.azcomputerguru.com/install/SILVER-HAWK-7639/macos" | head -60 # Test full installation (with code signing issue) curl -fsSL "https://rmm.azcomputerguru.com/install/SILVER-HAWK-7639/macos" | sudo bash # Check LaunchDaemon status launchctl list | grep gururmm # Result: Service crashes with -9 (SIGKILL - unsigned binary blocked) # Verify binary signature codesign -dv /usr/local/bin/gururmm-agent # Output: Signature=adhoc, TeamIdentifier=not set ``` ### Git Commands ```bash # Commit agent changes git add agent/src/macos_storage.rs agent/Cargo.toml agent/src/registry.rs agent/src/main.rs git commit -m "feat(agent): Add macOS support with plist storage" # Commit server endpoints git add server/src/api/install.rs server/src/main.rs git commit -m "feat(server): Add macOS installer endpoints" # Update PROJECT_STATE git add PROJECT_STATE.md git commit -m "docs: Update PROJECT_STATE for macOS agent Phase 1 completion" # Push all changes git push # Result: 3 commits pushed to main branch ``` ## Infrastructure & Servers ### GuruRMM Production Server (Jupiter) - **Hostname:** jupiter / 172.16.3.30 - **Services:** - gururmm-server (Rust/Axum) @ localhost:3001 - PostgreSQL @ localhost:5432/gururmm - nginx reverse proxy @ port 80/443 - **Downloads Directory:** `/var/www/gururmm/downloads/` - **Server Binary:** `/opt/gururmm/gururmm-server` - **Build Script:** `/home/guru/gururmm/scripts/build-agents.sh` - **Build Log:** `/var/log/gururmm-build.log` ### macOS Agent Installation Paths - **Binary:** `/usr/local/bin/gururmm-agent` - **Config:** `/usr/local/etc/gururmm/site.plist` - **LaunchDaemon:** `/Library/LaunchDaemons/com.azcomputerguru.gururmm-agent.plist` - **Logs:** `/usr/local/var/log/gururmm-agent.log` ### Cloudflare (rmm.azcomputerguru.com) - **Zone ID:** 1beb9917c22b54be32e5215df2c227ce - **WAF Rule:** "Skip bot check for RMM install endpoint" (ec57116fa2f34b5a991fe533129840cb) - Expression: `(http.host eq "rmm.azcomputerguru.com" and starts_with(http.request.uri.path, "/install/"))` - Action: Skip bot fight mode (allows curl installs) - Created: 2026-05-11 by Howard Enos - Status: Active and working correctly ### Test Sites in Database Available site codes for testing (from sites table on Jupiter): - `SILVER-HAWK-7639` - Main Office (site_id: 851376d1-33be-46ee-9e48-be44767e4a0a) - `LOWER-GROVE-5965` - Mara Home (site_id: 901f0f81-0ea7-412f-ae3c-67c1c78869a3) - `LOWER-OCEAN-7336` - Country Club (site_id: 7b32983d-982a-4a5c-af07-45a23453f589) - `INNER-BRIDGE-8354` - IMCMain (site_id: 2c5b65ad-2d5e-47b3-b12b-632e35e08ff6) - `SOUTH-PHOENIX-4306` - StambackSeptic (site_id: 0f3abe88-834f-4943-b28f-e97c236a0fea) ## Credentials & Database Access ### GuruRMM Database (Jupiter) - **Host:** 172.16.3.30 - **Port:** 5432 - **Database:** gururmm - **Username:** gururmm - **Password:** 43617ebf7eb242e814ca9988cc4df5ad - **Connection String:** `postgresql://gururmm:43617ebf7eb242e814ca9988cc4df5ad@localhost/gururmm` - **Note:** Password stored in CONTEXT.md and used for server builds ### Cloudflare API Access - **Full Account Token:** cfat_vQIRUHq6JwQ68F7aanbbwk14WnKInl0V0DjxpBg9d197012a - **Zone ID (azcomputerguru.com):** 1beb9917c22b54be32e5215df2c227ce - **Account ID:** 44594c346617d918bd3302a00b07e122 - **Account Name:** Mike@azcomputerguru.com Account - **Vault Location:** `/Users/azcomputerguru/vault/services/cloudflare.sops.yaml` ### SSH Access - **Jupiter (guru@172.16.3.30):** SSH key authentication, passwordless sudo - **Pluto (Administrator@172.16.3.36):** Used by build pipeline for Windows builds ## Configuration Changes ### LaunchDaemon Plist (Generated by Install Script) **Location:** `/Library/LaunchDaemons/com.azcomputerguru.gururmm-agent.plist` ```xml Label com.azcomputerguru.gururmm-agent ProgramArguments /usr/local/bin/gururmm-agent run RunAtLoad KeepAlive SuccessfulExit StandardOutPath /usr/local/var/log/gururmm-agent.log StandardErrorPath /usr/local/var/log/gururmm-agent.log ``` ### Site Configuration Plist (Generated by Install Script) **Location:** `/usr/local/etc/gururmm/site.plist` **Permissions:** 600 (root:wheel) ```xml site_id SITE-CODE-HERE ``` After enrollment, `agent_key` field is added by agent: ```xml agent_key agent-key-from-enrollment ``` ## Known Issues & Blockers ### BLOCKER: Code Signing Required for Apple Silicon Macs **Issue:** Unsigned binaries cannot execute on Apple Silicon (ARM64) Macs running modern macOS. **Symptoms:** - Installation completes successfully (binary copied, plist created, LaunchDaemon loaded) - Agent crashes immediately with SIGKILL (-9) when launched - `launchctl list` shows status: `-9` (killed by system) - No log output generated (killed before execution) **Technical Details:** - Binary signature: `adhoc` (unsigned) - TeamIdentifier: `not set` - macOS Gatekeeper blocks adhoc-signed executables on ARM64 architecture - `xattr -c` to remove extended attributes does not resolve issue - `spctl --add` is no longer supported on modern macOS **Workarounds (Testing Only):** 1. **Disable Gatekeeper system-wide** (requires reboot): ```bash sudo spctl --master-disable # Reboot Mac # Run install sudo spctl --master-enable ``` 2. **Intel Macs:** May work with unsigned binaries (needs verification) 3. **Development Signing:** Sign with ad-hoc certificate (still blocked on some systems) **Permanent Solution Required:** 1. **Enroll in Apple Developer Program** ($99/year) 2. **Obtain Developer ID Application certificate** from Apple 3. **Sign binaries** with valid certificate: ```bash codesign --sign "Developer ID Application: Arizona Computer Guru LLC" --timestamp --options runtime gururmm-agent ``` 4. **Notarize with Apple** (submit to Apple for malware scan): ```bash xcrun notarytool submit gururmm-agent.zip --keychain-profile "notarytool-profile" --wait xcrun stapler staple gururmm-agent ``` **Impact:** - Cannot deploy to Sylvia's Mac mini (WEST-MEADOW-9025) until code signing resolved - All Apple Silicon Macs blocked from running agent - Intel Macs may work (needs testing with unsigned binary) **Status:** Pending user decision on Apple Developer Program enrollment ### Issue 2: Build Pipeline Cleanup Removes macOS Binaries **Problem:** Automated build pipeline cleans up old files, removing manually-uploaded macOS binaries. **Temporary Workaround:** Re-upload binaries after pipeline completes. **Permanent Fix Required:** Update `build-agents.sh` to either: - Preserve macOS binaries during cleanup (add to exclusion list) - Build macOS binaries as part of pipeline (SSH to this Mac or cross-compile) ## Pending/Incomplete Tasks ### Immediate Next Steps 1. **Apple Developer Program Enrollment** - Decision: Enroll or wait? - Cost: $99/year - Timeline: ~24 hours for approval - Required for: Code signing certificate 2. **Code Signing Setup** (if enrolled) - Install Xcode Command Line Tools (if not present) - Download Developer ID Application certificate - Configure keychain access for codesign - Sign both arm64 and x86_64 binaries - Test signed binary on this Mac 3. **Notarization** (after signing) - Create app bundle or zip for submission - Submit to Apple notarization service - Wait for scan results (~5-15 minutes) - Staple notarization ticket to binary - Verify notarization: `spctl -a -vvv -t install gururmm-agent` 4. **Build Pipeline Integration** - Add macOS build steps to `build-agents.sh` - Option A: SSH to this Mac for native builds - Option B: Cross-compile from Jupiter (if feasible) - Include code signing in automated builds - Create `-latest` symlinks for macOS binaries - Add SHA256 checksums 5. **Dashboard Updates** (Phase 2) - Add macOS install command to SiteDetail page - Show macOS agent in platform selector - Display macOS-specific instructions - Add architecture detection (arm64 vs x86_64) ### Future Enhancements (Phase 3) - **.pkg Installer:** macOS package installer (alternative to shell script) - **Auto-Update Support:** Ensure macOS agents receive auto-updates - **Uninstaller:** Script to cleanly remove agent and LaunchDaemon - **Menu Bar Agent:** Optional GUI indicator (future feature) - **Apple Silicon Optimization:** Native performance tuning ### Testing Pending Once code signing resolved: - Deploy to Sylvia's Mac mini (WEST-MEADOW-9025) - original blocker from Howard's message - Verify enrollment flow on macOS - Test agent reconnection after server restart - Verify auto-update mechanism works on macOS - Load testing with multiple macOS agents - Intel Mac testing (if available) ## Reference Information ### Install Command (For Users) ```bash curl -fsSL https://rmm.azcomputerguru.com/install/SITE-CODE/macos | sudo bash ``` ### Uninstall Commands (Manual) ```bash # Stop and unload service sudo launchctl unload /Library/LaunchDaemons/com.azcomputerguru.gururmm-agent.plist # Remove files sudo rm /usr/local/bin/gururmm-agent sudo rm /Library/LaunchDaemons/com.azcomputerguru.gururmm-agent.plist sudo rm -rf /usr/local/etc/gururmm/ sudo rm -rf /usr/local/var/log/gururmm-agent.log ``` ### Service Management ```bash # Check service status sudo launchctl list | grep gururmm # View logs sudo tail -f /usr/local/var/log/gururmm-agent.log # Restart service sudo launchctl unload /Library/LaunchDaemons/com.azcomputerguru.gururmm-agent.plist sudo launchctl load /Library/LaunchDaemons/com.azcomputerguru.gururmm-agent.plist # Verify plist syntax plutil -lint /Library/LaunchDaemons/com.azcomputerguru.gururmm-agent.plist # Check config file sudo plutil -p /usr/local/etc/gururmm/site.plist ``` ### API Endpoints (New) - `GET /install/:site_code/macos` - Returns bash installer script - `GET /install/:site_code/download/macos` - Returns arm64 binary with site-code trailer - `GET /install/:site_code/download/macos-x86_64` - (Not yet implemented) Intel binary ### File Paths (All platforms) - **Windows:** Registry at `HKLM\SOFTWARE\GuruRMM\` (SiteId, AgentKey) - **macOS:** Plist at `/usr/local/etc/gururmm/site.plist` - **Linux:** TOML fallback (future implementation) ### Related Documentation - Implementation Plan: `projects/msp-tools/guru-rmm/docs/macos-agent-implementation-plan.md` - Project Context: `projects/msp-tools/guru-rmm/CONTEXT.md` - Project State: `projects/msp-tools/guru-rmm/PROJECT_STATE.md` - Roadmap: `projects/msp-tools/guru-rmm/ROADMAP.md` ## Commits Made 1. **d7816d3** - feat(agent): Add macOS support with plist storage - agent/src/macos_storage.rs (new) - agent/Cargo.toml (modified) - agent/src/registry.rs (modified) - agent/src/main.rs (modified) 2. **f83806e** - feat(server): Add macOS installer endpoints - server/src/api/install.rs (+183 lines) - server/src/main.rs (+2 lines) 3. **8ee25f3** - docs: Update PROJECT_STATE for macOS agent Phase 1 completion - PROJECT_STATE.md (released lock, added macOS component, logged changes) All commits pushed to `main` branch on Gitea (git.azcomputerguru.com/azcomputerguru/gururmm). ## Success Metrics [OK] Agent compiles for both architectures (arm64 + x86_64) [OK] Server endpoints return valid install script [OK] Install script downloads and configures agent correctly [OK] LaunchDaemon plist created with correct format [OK] Site configuration plist created with site_id [OK] Cloudflare WAF rule allows curl installs [BLOCKED] Agent runs successfully (requires code signing) [PENDING] Agent enrolls with server (blocked by execution) [PENDING] Dashboard shows macOS install option (Phase 2) ## Lessons Learned 1. **Code Signing is Critical:** Cannot defer Apple Developer enrollment for production macOS deployments on Apple Silicon. Should have been identified in planning phase. 2. **Build Pipeline Coordination:** Manual server rebuilds are faster for testing server-only changes. Full pipeline takes 15+ minutes for Windows builds. 3. **Platform Storage Abstraction:** The registry.rs pattern worked perfectly - adding macOS support required minimal changes to main.rs because of good abstraction. 4. **Installation Testing:** Shell script installers are easy to test locally - immediate feedback loop compared to .pkg installers. 5. **Cloudflare WAF:** Existing rule from Howard's earlier work meant no additional configuration needed - good team coordination. ## Notes for Future Sessions - Howard Enos left message (2026-05-07) about Sylvia blocked on Mac mini - this work unblocks her once code signing resolved - Build pipeline still building Windows variants while we were testing - consider separating server/agent builds - This Mac (Mikes-MacBook-Air) can be used for future macOS builds via SSH - Intel Mac testing still needed - unsigned binary may work on x86_64 - Consider documenting Apple Developer setup process for future reference --- **Session Status:** Phase 1 implementation complete. Deployment blocked on Apple Developer Program enrollment and code signing certificate.