# 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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.azcomputerguru.gururmm-agent</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/bin/gururmm-agent</string>
        <string>run</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <dict>
        <key>SuccessfulExit</key>
        <false/>
    </dict>
    <key>StandardOutPath</key>
    <string>/usr/local/var/log/gururmm-agent.log</string>
    <key>StandardErrorPath</key>
    <string>/usr/local/var/log/gururmm-agent.log</string>
</dict>
</plist>
```

### Site Configuration Plist (Generated by Install Script)
**Location:** `/usr/local/etc/gururmm/site.plist`
**Permissions:** 600 (root:wheel)

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>site_id</key>
    <string>SITE-CODE-HERE</string>
</dict>
</plist>
```

After enrollment, `agent_key` field is added by agent:
```xml
    <key>agent_key</key>
    <string>agent-key-from-enrollment</string>
```

## 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.
