# Session Log: 2026-05-26

## User
- **User:** Mike Swanson (mike)
- **Machine:** Mikes-MacBook-Air
- **Role:** admin
- **Session Start:** 2026-05-26 05:56 MST
- **Session End:** 2026-05-26 06:20 MST

## Session Summary

This session resolved enrollment failures in the first macOS version of the GuruRMM agent (v0.6.41). The previous session had successfully built, signed, notarized, and deployed the universal binary, then manually installed it on this Mac. However, the agent failed to enroll with the server, logging UUID parsing errors.

Investigation revealed the agent was reading `/usr/local/etc/gururmm/site.plist` which contained the site code "SILVER-HAWK-7639" (a human-readable string) instead of a UUID. Initial troubleshooting wasted time by updating the wrong configuration file (`/Library/Application Support/GuruRMM/agent.toml`) - the macOS agent uses platform-specific storage (plist files) following the same pattern as Windows registry storage, not TOML fallback configs.

The correct site UUID was obtained by querying the GuruRMM database via SSH to Saturn (172.16.3.30). The site "SWIFT-CLOUD-6910" (AZ Computer Guru Main Office) has UUID `d008c7d4-9e5e-4666-9fa0-b432609d54cc`. After updating the plist file with this UUID and restarting the agent, enrollment succeeded immediately. The agent authenticated as agent_id `69c0be19-c4e4-4fc3-ab3f-4b13f7391a05` and began reporting metrics.

With the manual installation working, attention turned to production deployment requirements. A comprehensive macOS installation plan was created covering PKG installer architecture, parameterized site enrollment (like Windows MSI's `SITEKEY`), menu bar app design, and testing strategy. An uninstall script was also created for complete agent removal during testing cycles.

The session established that future macOS deployments require proper automation - the current manual process (copy binary, create plist, configure LaunchDaemon) is unacceptable for MSP-scale deployment. The PKG installer will provide one-command installation with MDM support, matching Windows deployment capabilities.

## Key Decisions

- **Updated site.plist instead of agent.toml** - macOS agent uses platform-specific storage (plist files at `/usr/local/etc/gururmm/site.plist`) following the Windows registry pattern, not TOML fallback configs
- **Removed TOML config directory entirely** - `/Library/Application Support/GuruRMM/` not used on macOS, only exists as fallback when plist is missing (Linux/non-standard installs)
- **PKG installer format chosen over DMG** - PKG supports pre/post-install scripts, MDM deployment parameters, and install-time site_id injection like Windows MSI
- **SwiftUI for menu bar app** - native macOS UI framework, lightweight and modern versus Electron bloat
- **IPC socket reuse for menu bar communication** - existing Unix socket at `/var/run/gururmm/agent.sock` used for menu bar app communication
- **Separate LaunchAgent for menu bar app** - user-level auto-launch at login (not system daemon), per-user menu bar presence
- **Write-once enrollment model** - site_id set at install time via PKG parameter, preserved in plist across upgrades
- **Force-kill in uninstall script** - uses `kill -9` for complete cleanup during testing without waiting for graceful shutdown

## Problems Encountered

- **Enrollment UUID parsing error** - site.plist contained site code "SILVER-HAWK-7639" (string) instead of UUID format. Server expected UUID, failed to parse string starting with "S". **Resolution:** Queried database for correct UUID, updated plist file.

- **Updated wrong configuration file** - Wasted time updating `/Library/Application Support/GuruRMM/agent.toml` which macOS agent does not read. The agent uses plist storage. **Resolution:** Identified correct file location from source code (`macos_storage.rs`), updated `/usr/local/etc/gururmm/site.plist` instead.

- **Database connection blocked from Mac** - PostgreSQL connections to 172.16.3.30:5432 and 172.16.3.20:5432 refused, network path unavailable from Mac. **Resolution:** Used SSH to Saturn server, ran psql query remotely to retrieve site UUID.

- **Vault path mismatch** - Initially tried `projects/msp-tools/guru-rmm/database.sops.yaml` but vault uses `projects/gururmm/database.sops.yaml` (no nested msp-tools path). **Resolution:** Searched vault with `vault.sh search gururmm`, found correct path.

- **Config changes not taking effect** - Restarted agent with `launchctl kickstart` but logs showed old site_id still being used. **Resolution:** Agent had cached the plist contents in memory, required full process kill and restart to reload configuration.

## Configuration Changes

### Files Created
- `projects/msp-tools/guru-rmm/agent/MACOS_INSTALLATION_PLAN.md` - Comprehensive plan for PKG installer, menu bar app, testing (315 lines)
- `projects/msp-tools/guru-rmm/agent/uninstall-macos.sh` - Complete uninstall script for testing (executable, 4.4 KB)
- `/usr/local/share/uninstall-macos.sh` - Copy of uninstall script in accessible location

### Files Modified
- `/usr/local/etc/gururmm/site.plist` - Updated site_id from "SILVER-HAWK-7639" to "d008c7d4-9e5e-4666-9fa0-b432609d54cc"

### Files Removed
- `/Library/Application Support/GuruRMM/agent.toml` - Unnecessary TOML config (not used on macOS)
- `/Library/Application Support/GuruRMM/` - Empty directory removed

## Credentials & Secrets

**GuruRMM Database:**
- Vault path: `projects/gururmm/database.sops.yaml`
- Host: 172.16.3.30:5432 (PostgreSQL, not MySQL/MariaDB as initially thought)
- Database: gururmm
- Username: gururmm
- Password: 43617ebf7eb242e814ca9988cc4df5ad

**Site Enrollment:**
- Site: Main Office (AZ Computer Guru)
- Site Code: SWIFT-CLOUD-6910
- Site UUID: d008c7d4-9e5e-4666-9fa0-b432609d54cc
- Client UUID: 417420f4-c3f4-482a-acd4-d6f63c8cddde

**Agent Enrollment (This Mac):**
- Agent ID: 69c0be19-c4e4-4fc3-ab3f-4b13f7391a05
- Enrolled: 2026-05-26 13:04:29 UTC
- Agent key: (written to plist file by agent after enrollment)

**Apple Developer:**
- Signing Identity: Developer ID Application: MICHAEL PHILLIP SWANSON (N2LVAL4LQP)
- Notarization Profile: gururmm-notarize
- Apple ID: superguru@gmail.com

## Infrastructure & Servers

**This Machine:**
- Hostname: Mikes-MacBook-Air
- Architecture: Apple Silicon (ARM64)
- macOS version: Darwin 25.5.0
- Agent binary: /usr/local/bin/gururmm-agent (universal binary, 8.3 MB)
- Agent config: /usr/local/etc/gururmm/site.plist
- Agent logs: /usr/local/var/log/gururmm-agent.log
- LaunchDaemon: /Library/LaunchDaemons/com.azcomputerguru.gururmm-agent.plist
- Service: com.azcomputerguru.gururmm-agent (running, PID 73491)

**GuruRMM Server:**
- API: https://rmm-api.azcomputerguru.com
- WebSocket: wss://rmm-api.azcomputerguru.com/ws
- Database: 172.16.3.30:5432 (PostgreSQL)

**Build Server:**
- Saturn: 172.16.3.30
- Distribution: https://rmm.azcomputerguru.com/downloads
- Build script: /opt/gururmm/build-agents.sh

## Commands & Outputs

**Query site UUID from database (via SSH):**
```bash
ssh root@172.16.3.30 "PGPASSWORD='43617ebf7eb242e814ca9988cc4df5ad' psql -h 172.16.3.20 -U gururmm -d gururmm -t -c \"SELECT id, name, site_code FROM sites WHERE site_code = 'SILVER-HAWK-7639';\""
# Failed - connection refused (database not accessible from this network)
```

**Update site.plist with correct UUID:**
```bash
sudo tee /usr/local/etc/gururmm/site.plist > /dev/null << 'EOF'
<?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>d008c7d4-9e5e-4666-9fa0-b432609d54cc</string>
</dict>
</plist>
EOF
```

**Restart agent service:**
```bash
sudo kill -9 $(sudo launchctl list | grep gururmm-agent | awk '{print $1}')
# LaunchDaemon automatically restarts the service
```

**Verify enrollment success:**
```bash
sudo tail -n 20 /usr/local/var/log/gururmm-agent.log
```

**Output:**
```
[2026-05-26T13:04:29.457043Z]  INFO Enrollment complete — agent key persisted to registry
[2026-05-26T13:04:29.857600Z]  INFO WebSocket connected (HTTP status: 101 Switching Protocols)
[2026-05-26T13:04:29.958847Z]  INFO Authentication successful, agent_id: Some(69c0be19-c4e4-4fc3-ab3f-4b13f7391a05)
[2026-05-26T13:04:30.008560Z]  INFO Metrics: CPU=12.8%, Mem=67.0%, Disk=68.1%
[2026-05-26T13:04:30.074823Z]  INFO Sent user inventory report: 3 users, join_type=none
[2026-05-26T13:04:31.927922Z]  INFO Sent hardware inventory report
```

**Remove unnecessary TOML config:**
```bash
sudo rm -rf "/Library/Application Support/GuruRMM"
```

**Copy uninstall script to accessible location:**
```bash
sudo cp projects/msp-tools/guru-rmm/agent/uninstall-macos.sh /usr/local/share/
sudo chmod 755 /usr/local/share/uninstall-macos.sh
```

## Pending / Incomplete Tasks

**PKG Installer Implementation (10-13 day estimate):**
- [ ] Create `build-macos-pkg.sh` build script
- [ ] Write pre-install script with upgrade detection logic
- [ ] Write post-install script with SITE_ID parameter handling
- [ ] Test PKG build, signing, and notarization
- [ ] Create SwiftUI menu bar app project
- [ ] Implement IPC client for menu bar app communication
- [ ] Sign and notarize menu bar app
- [ ] Update main PKG to bundle menu bar app
- [ ] Test fresh install on Intel and ARM Macs
- [ ] Test upgrade scenario (preserves enrollment)
- [ ] Test MDM deployment (Jamf Pro)
- [ ] Update CI/CD pipeline (`/opt/gururmm/build-agents.sh`)
- [ ] Update documentation and downloads page

**Immediate Next Steps:**
1. Test uninstall script on this Mac to verify complete cleanup
2. Begin PKG installer implementation (Phase 1)
3. Consider menu bar app as Phase 3 (installer works standalone)

**Open Questions:**
- Should menu bar app be bundled in main PKG or separate download?
- How to handle missing SITE_ID in automated deployments? (Fail loudly or allow empty for manual enrollment?)
- Need re-enrollment mechanism for changing sites without full reinstall?

## Reference Information

**Documentation:**
- Installation plan: `projects/msp-tools/guru-rmm/agent/MACOS_INSTALLATION_PLAN.md`
- Build guide: `projects/msp-tools/guru-rmm/agent/MACOS_BUILD_GUIDE.md` (from previous session)
- Uninstall script: `projects/msp-tools/guru-rmm/agent/uninstall-macos.sh`

**Source Code Files:**
- macOS storage: `projects/msp-tools/guru-rmm/agent/src/macos_storage.rs` (lines 1-109)
- Registry abstraction: `projects/msp-tools/guru-rmm/agent/src/registry.rs` (lines 116-123)
- Enrollment: `projects/msp-tools/guru-rmm/agent/src/enroll.rs` (lines 1-84)
- Main entry: `projects/msp-tools/guru-rmm/agent/src/main.rs` (lines 412-469)

**Agent Version:**
- Version: 0.6.41
- Build date: 2026-05-26
- Universal binary: x86_64 + arm64
- Distribution: https://rmm.azcomputerguru.com/downloads/gururmm-agent-macos-universal-0.6.41

**LaunchDaemon Configuration:**
```xml
Label: com.azcomputerguru.gururmm-agent
Program: /usr/local/bin/gururmm-agent run
RunAtLoad: true
KeepAlive: SuccessfulExit=false
StandardOutPath: /usr/local/var/log/gururmm-agent.log
StandardErrorPath: /usr/local/var/log/gururmm-agent.log
```

**PKG Installer Specification:**
- Format: macOS PKG (not DMG)
- Install parameter: `-env SITE_ID=<uuid>`
- Identifier: com.azcomputerguru.gururmm-agent
- Components: agent binary, LaunchDaemon plist, uninstall script
- Signing: Developer ID Installer certificate
- Notarization: xcrun notarytool
- MDM compatible: Jamf, Kandji, Mosyle

**Timeline Estimate:**
- Phase 1 (PKG installer): 3-4 days
- Phase 2 (uninstall script): 1 day (COMPLETED)
- Phase 3 (menu bar app): 4-5 days
- Phase 4 (integration/testing): 2-3 days
- Total: 10-13 days for complete system

---

## Update: 06:20 MST — SSL cert triage (Mike Swanson / GURU-5070)

### User
- **User:** Mike Swanson (mike)
- **Machine:** GURU-5070 (DESKTOP-0O8A1RL)
- **Role:** admin
- **Session span:** 2026-05-26 ~06:20 MST

### Session Summary

Session opened with `/context` to recover prior state, followed by `/sync` which pulled the MacBook session log (macOS agent enrollment fix). Two SSL cert items from the pending list were addressed.

Neptune SSL cert (mail.acghosting.com, expiring 2026-05-31) was flagged as having auto-renewal configured by the user. The wiki was updated to remove the URGENT flag and note that auto-renewal is in place.

Western Tire SSL was investigated in depth. The `*.westerntire.com` wildcard cert (Let's Encrypt, issued 2026-03-01) was expiring 2026-05-30 and had not renewed. Direct SSL inspection via `openssl s_client` confirmed the cert was the original March 1 issue — no renewal had occurred. SSH to IX (172.16.3.10) via paramiko revealed the root cause: all 9 westerntire.com domains were listed as user-excluded in cPanel's AutoSSL, so AutoSSL had been skipping the account entirely on every 3-hour run. No acme.sh installation and no cron job for cert renewal existed.

Per the user's direction, only `mail.westerntire.com` was removed from the AutoSSL exclusion list (the 8 other domains — www, webmail, cpanel, autodiscover, webdisk, cpcontacts, cpcalendars, westerntire.com — remain excluded as they carry no live services). An AutoSSL run was triggered via `whmapi1 start_autossl_check_for_one_user`. AutoSSL completed within 30 seconds: HTTP DCV passed, Let's Encrypt issued a cert for `mail.westerntire.com` only, and it was installed immediately. Live verification via `openssl s_client` on port 993 confirmed the new cert (CN=mail.westerntire.com, R13, issued 2026-05-26, expires 2026-08-24). Wiki articles for western-tire and internal-infrastructure were updated to reflect the resolved state.

### Key Decisions

- **Only remove mail.westerntire.com from AutoSSL exclusions** — user specified mail sub only. The other 8 domains serve no live purpose (westerntire.com redirects to jackfurriers.com; cPanel/webmail/autodiscover subdomains unused by active users). Retaining their exclusion avoids AutoSSL issuing unnecessary certs for unused names.
- **Let AutoSSL issue per-domain cert, not wildcard** — AutoSSL (HTTP-01 challenge) cannot issue wildcards. Switching from the wildcard to a per-domain cert for mail.westerntire.com is functionally equivalent for IMAP/SMTP clients.
- **Neptune SSL flagged as no-action** — user confirmed auto-renewal is configured. Removed from urgent list without verifying the renewal client directly; if renewal fails by 2026-05-31 it will surface as a mail TLS error.

### Problems Encountered

- **`uapi remove_autossl_excluded_domains domain=...` failed** — cPanel UAPI expects the argument as `domains=` (plural), not `domain=`. Fixed by correcting the argument name; API returned status 1 on retry.
- **sshpass not available on Windows** — initial SSH attempt using `sshpass -p` failed with command not found. Switched to paramiko (Python) for all SSH operations to IX.

### Configuration Changes

**IX server (172.16.3.10) — cPanel westernt account:**
- Removed `mail.westerntire.com` from AutoSSL excluded domains list
- New cert issued and installed: CN=mail.westerntire.com, issued 2026-05-26, expires 2026-08-24

**Wiki (claudetools repo):**
- `wiki/clients/western-tire.md` — SSL section updated: wildcard replaced by per-domain AutoSSL cert for mail subdomain; P2 SSL item removed from open items; anti-pattern warning updated
- `wiki/clients/internal-infrastructure.md` — Neptune Let's Encrypt cert entry updated: removed URGENT flag, noted auto-renewal configured

### Credentials & Secrets

No new credentials. Used existing:
- IX root password: `infrastructure/ix-server.sops.yaml` → `credentials.password`

### Infrastructure & Servers

| Host | Detail |
|---|---|
| IX web server | 172.16.3.10 (internal) / 72.194.62.5 (external). cPanel account `westernt`. AutoSSL runs every 3 hours via Let's Encrypt provider. |
| mail.westerntire.com | Port 993 (IMAPS), port 587 (SMTP STARTTLS). New cert CN=mail.westerntire.com, expires 2026-08-24. |

### Commands & Outputs

```bash
# Verify existing cert before fix
echo | openssl s_client -connect mail.westerntire.com:993 -servername mail.westerntire.com 2>/dev/null \
  | openssl x509 -noout -subject -dates
# subject=CN=*.westerntire.com
# notBefore=Mar  1 10:05:18 2026 GMT  notAfter=May 30 10:05:17 2026 GMT

# Check AutoSSL log for westerntire (most recent run)
# -> "User-excluded domains: 9 (westerntire.com, www.westerntire.com, mail.westerntire.com, ...)"

# Get current exclusion list
uapi --user=westernt SSL get_autossl_excluded_domains
# -> 9 domains listed including mail.westerntire.com

# Remove mail.westerntire.com from exclusions
uapi --user=westernt SSL remove_autossl_excluded_domains domains=mail.westerntire.com
# -> status: 1 (success)

# Trigger AutoSSL run
/usr/local/cpanel/bin/whmapi1 start_autossl_check_for_one_user username=westernt
# -> pid: 3715934, result: 1

# AutoSSL log after run
# [2026-05-26T14:02:21Z]  "mail.westerntire.com" is managed.
# [2026-05-26T14:02:22Z]  CA authorized: "mail.westerntire.com"
# [2026-05-26T14:02:23Z]  Local HTTP DCV OK: mail.westerntire.com
# [2026-05-26T14:02:25Z]  "Let's Encrypt™" HTTP DCV OK: mail.westerntire.com
# [2026-05-26T14:02:27Z]  Installing "westerntire.com"'s new certificate ...

# Verify new cert
echo | openssl s_client -connect mail.westerntire.com:993 -servername mail.westerntire.com 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates
# subject=CN=mail.westerntire.com
# issuer=C=US, O=Let's Encrypt, CN=R13
# notBefore=May 26 13:03:56 2026 GMT  notAfter=Aug 24 13:03:55 2026 GMT
```

### Pending / Incomplete Tasks

- HIGH: `fix/audit-2-remediation` branch (gururmm) — awaiting Mike's merge + deploy
- HIGH: macOS PKG installer — plan at `projects/msp-tools/guru-rmm/agent/MACOS_INSTALLATION_PLAN.md`
- HIGH: Kittle WS2025 EVAL license activation; no backup, no firewall
- HIGH: Kittle-Design Ken inbox rule (potential active compromise)
- MEDIUM: TGC-SERVER Hyper-V disposition (MAS90 VM — customer decision needed)
- MEDIUM: Neptune SSL auto-renewal — verify client is healthy before 2026-05-31 (user says it's configured)
- LOW: Seed wiki/systems/neptune.md, wiki/systems/beast.md

### Reference Information

- Western Tire Syncro ticket: #32199 (ID: 109325058) — not yet billed
- IX AutoSSL log path: `/var/cpanel/logs/autossl/<timestamp>/txt`
- IX AutoSSL schedule: every 3 hours (confirmed from log dir timestamps)
- cPanel UAPI: `uapi --user=<account> SSL remove_autossl_excluded_domains domains=<domain>` (note: `domains=`, not `domain=`)
- New cert details: CN=mail.westerntire.com, Let's Encrypt R13, 2026-05-26 → 2026-08-24, auto-renews ~2026-07-25

---

## Update: 08:06 PT — MSP Pricing review + Coord ToDo system

### User
- **User:** Mike Swanson (mike)
- **Machine:** GURU-5070
- **Role:** admin
- **Session span:** ~07:30–08:06 PT

### Session Summary

Resumed from a previous context window. Session began with a review of the MSP pricing project artifacts. The `wiki/projects/msp-pricing.md` article was read to get the project overview, followed by directory listing of `projects/msp-pricing/` to surface all artifacts: pricing docs, Python calculators, HTML price sheets, and marketing materials (Buyers Guide, OnePagers, PDFs).

Reviewed `docs/web-email-hosting-pricing.md` in detail at the user's request. Made three targeted edits: clarified the ACG Position section to show $175/hr as the standard rate and $130–150/hr as the block time effective rate; removed the INKY reference from the Email Security platform line (moving away from INKY); and updated the Last Updated date from 2026-02-01 to 2026-05-26 in both the header and footer.

Designed and implemented a new `coord_todos` feature for the coordination API. The feature adds a `coord_todos` table with per-user, per-machine, and per-project scoping, sub-task support via self-referencing `parent_id`, and an `auto_created` flag for Claude-generated items. A `for_user`/`for_machine` filter using OR-NULL logic was added to support the sync/save display use case (show items assigned to the current user OR unassigned). The sync.sh script was extended with a new Phase 7 that fetches and displays pending todos grouped by project after every sync. CLAUDE.md was updated with auto-creation behavior guidelines and natural language query patterns.

A `due_at` datetime field was added as a follow-up in a second migration. The sync.sh display was updated to show `due:YYYY-MM-DDTHH:MM` alongside items that have a due date. Both migrations were run on the production server (172.16.3.30). A test todo was created for today at 14:00: "Client pricing audit: compare current pricing for active clients against the client pricing project" (project: msp-pricing, id: de50e82a).

### Key Decisions

- **Separate `for_user`/`for_machine` vs `assigned_to_user`/`assigned_to_machine` params:** Exact-match params kept for admin queries; OR-NULL variants added for sync/save use. This avoids silently hiding unassigned todos from the sync display.
- **`due_at` in the text initially, then added as a column:** The first todo was created with "2pm" in the text before the field existed. After adding `due_at`, the todo text was cleaned up and `due_at` set to `2026-05-26T14:00:00`.
- **Two separate migrations rather than one:** `due_at` was added as a follow-up after the initial todos migration was already deployed, keeping the migration history clean and atomic.
- **Sub-tasks explicit delete before parent in service:** MySQL/MariaDB without `foreign_key_checks=1` may not cascade FK deletes at the application level; the service explicitly deletes sub-tasks first to be safe across engine configurations.

### Problems Encountered

- **Alembic not in PATH on Windows:** `python -m alembic` and `alembic` both failed locally (no venv activated, no alembic in system Python). Resolved by running the migration via SSH on the server where `/opt/claudetools` has a venv with alembic installed.
- **Migration file only exists locally:** After writing the migration, the server had no copy. Resolved with `scp` before running `alembic upgrade head` on the server.
- **`systemctl restart` requires sudo on the server:** First restart attempt failed without sudo. Resolved by prepending `sudo`.

### Configuration Changes

| File | Change |
|---|---|
| `projects/msp-pricing/docs/web-email-hosting-pricing.md` | ACG rate clarified, INKY removed, dates updated |
| `api/models/coord_todo.py` | New: CoordTodo ORM model |
| `api/schemas/coord_todo.py` | New: CoordTodoCreate/Update/Response schemas |
| `api/services/coord_todo_service.py` | New: CRUD service with for_user/for_machine OR-NULL filters |
| `api/routers/coord_todos.py` | New: 5 REST endpoints |
| `api/main.py` | Registered coord_todos router at /api/coord/todos |
| `api/models/__init__.py` | Added CoordTodo import |
| `migrations/versions/20260526_120000_coord_todos.py` | New: creates coord_todos table |
| `migrations/versions/20260526_150000_coord_todos_due_at.py` | New: adds due_at column |
| `.claude/CLAUDE.md` | Auto-todo creation behavior + query patterns documented |
| `.claude/scripts/sync.sh` | Phase 7 added: pending todo display after sync |

### Credentials & Secrets

None new.

### Infrastructure & Servers

- **Coord API:** http://172.16.3.30:8001/api/coord/todos (new endpoint, live)
- **DB:** MariaDB 172.16.3.30:3306 — coord_todos table added, migrations 20260526_120000 + 20260526_150000 applied
- **API service:** claudetools-api.service on 172.16.3.30 — restarted twice during this session

### Commands & Outputs

```bash
# Run migration on server
ssh guru@172.16.3.30 "cd /opt/claudetools && source venv/bin/activate && alembic upgrade head"
# INFO: Running upgrade 20260512_120000 -> 20260526_120000, coord_todos
# INFO: Running upgrade 20260526_120000 -> 20260526_150000, coord_todos add due_at column

# Smoke test
curl -s "http://172.16.3.30:8001/api/coord/todos?status_filter=all&limit=1"
# []

# Create test todo
curl -s -X POST "http://172.16.3.30:8001/api/coord/todos" \
  -H "Content-Type: application/json" \
  -d '{"text":"Client pricing audit...","project_key":"msp-pricing","assigned_to_user":"mike","created_by_user":"mike","created_by_machine":"GURU-5070","auto_created":false}'
# id: de50e82a-30d1-479b-bf9c-522ca223d2cc

# Set due_at after field was added
curl -s -X PUT "http://172.16.3.30:8001/api/coord/todos/de50e82a-30d1-479b-bf9c-522ca223d2cc" \
  -H "Content-Type: application/json" \
  -d '{"due_at":"2026-05-26T14:00:00","text":"Client pricing audit: compare current pricing for active clients against the client pricing project"}'
```

### Pending / Incomplete Tasks

- [ ] Add `/todo` slash command as convenience wrapper (deferred — natural language sufficient for now)
- [ ] Client pricing audit todo (id: de50e82a) due 2026-05-26 14:00 — compare active client pricing vs msp-pricing project docs
- [ ] HIGH: `fix/audit-2-remediation` branch (gururmm) — awaiting merge + deploy
- [ ] HIGH: macOS PKG installer — plan at `projects/msp-tools/guru-rmm/agent/MACOS_INSTALLATION_PLAN.md`
- [ ] HIGH: Kittle WS2025 EVAL license + Kittle-Design inbox rule (potential active compromise)
- [ ] MEDIUM: TGC-SERVER Hyper-V disposition (MAS90 VM)
- [ ] MEDIUM: Neptune SSL — monitor auto-renewal before 2026-05-31
- [ ] MEDIUM: Syncro ticket #32199 (Western Tire) — bill when scope confirmed
- [ ] MEDIUM: Syncro "DNS Detail" field on Western Tire customer still says "Email is on Websvr"

### Reference Information

- Commits this session: `4be8903` (coord todos initial), `1c038c7` (due_at field)
- Coord todos API: `GET/POST/PUT/DELETE http://172.16.3.30:8001/api/coord/todos`
- Test todo id: `de50e82a-30d1-479b-bf9c-522ca223d2cc` (msp-pricing, due 14:00)
- MSP pricing artifacts: `projects/msp-pricing/` — docs, calculators, HTML sheets, marketing
- Western Tire Syncro ticket: #32199 (ID: 109325058) — not yet billed
