Files
claudetools/session-logs/2026-05-25-session.md
Mike Swanson 6945b4237e sync: auto-sync from GURU-5070 at 2026-05-25 09:21:41
Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-05-25 09:21:41
2026-05-25 09:21:44 -07:00

708 lines
47 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Session Log — 2026-05-25
## User
- **User:** Mike Swanson (mike)
- **Machine:** Mikes-MacBook-Air.local
- **Role:** admin
- **Session:** 05:00 - 05:48 MST
## Session Summary
Recovered GURU-KALI workstation from black screen caused by nvidia driver installation using GuruRMM remote command execution. The system had booted to black screen after installing nvidia driver version 595.71.05-1, but the GuruRMM agent remained online and responsive, enabling remote diagnosis and repair.
Connected to the GuruRMM API at 172.16.3.30:3001 and confirmed GURU-KALI agent (ID a73ba38e-cd02-4331-b8bf-474cd899ec22) was online despite the display failure. Sent remote shell command to enumerate installed nvidia packages, discovering 50+ packages including driver, libraries, and firmware. Initial removal attempt failed with "Read-only file system" errors across /var/lib/dpkg and /var/cache/apt, indicating the filesystem had been mounted read-only - likely a protective measure after a previous boot failure.
Remounted the root filesystem as read-write using "mount -o remount,rw /", then executed a full nvidia package removal using apt-get with DEBIAN_FRONTEND=noninteractive to avoid interactive prompts. This removed all nvidia-* and libnvidia-* packages, but firmware packages and some DKMS modules remained. Performed a second pass removing firmware-nvidia-graphics and firmware-nvidia-gsp, then created /etc/modprobe.d/blacklist-nvidia.conf to prevent the nvidia kernel modules from loading on future boots. Updated initramfs to apply the blacklist.
Rebooted the system twice - first after the initial driver removal, then again after the blacklist was applied. After the second reboot, verified that lightdm display manager started successfully (active and running state). User confirmed the display was restored and showing the login screen. The system is now using either the Intel i915 integrated graphics driver or framebuffer fallback instead of the problematic nvidia driver. Blacklist remains in place to prevent recurrence.
## Key Decisions
- **Used GuruRMM remote commands rather than physical access** — Agent was online despite black screen, enabling fully remote recovery without needing console access or recovery media
- **Remounted filesystem before package operations** — Read-only state blocked all dpkg/apt operations; remounting as read-write was mandatory before proceeding with driver removal
- **Performed multi-pass removal** — First removed main driver packages, then firmware, then created blacklist and updated initramfs as separate operations to ensure each step completed cleanly
- **Created permanent blacklist** — Added /etc/modprobe.d/blacklist-nvidia.conf rather than just removing packages, preventing automatic reloading if packages get reinstalled via dependencies
- **Rebooted twice** — First reboot applied the package removal; second reboot after blacklist creation ensured nvidia modules wouldn't load from initramfs
- **Used DEBIAN_FRONTEND=noninteractive** — Prevented apt-get from blocking on interactive prompts during unattended remote execution
## Problems Encountered
- **Filesystem mounted read-only** — Initial package removal failed with "unable to access dpkg database" and "Read-only file system" errors. Resolved by running "mount -o remount,rw /" before retrying removal operations.
- **JSON parsing control characters** — Command output containing terminal control codes caused jq parsing failures. Worked around by using grep/python for status checks or by stripping control characters.
- **Firmware packages remained after initial removal** — First apt-get pass removed driver packages but left firmware-nvidia-graphics and firmware-nvidia-gsp. Required explicit second removal targeting firmware-* packages.
- **Blacklist file initially missing** — After first reboot, /etc/modprobe.d/blacklist-nvidia.conf was not present despite creation command showing success. Recreated using heredoc syntax and verified file contents before final reboot.
- **Exit code 100 despite success** — Several apt-get operations returned exit code 100 (indicating warnings/non-critical issues) but included success markers in stdout. Used marker strings like "NVIDIA REMOVAL COMPLETE" to verify actual completion rather than relying solely on exit codes.
## Configuration Changes
**GURU-KALI (100.75.148.91 / Tailscale) — remote via GuruRMM:**
- Removed 50+ nvidia packages (nvidia-driver, nvidia-open, xserver-xorg-video-nvidia, all libnvidia-* libs)
- Removed firmware-nvidia-graphics and firmware-nvidia-gsp
- Created `/etc/modprobe.d/blacklist-nvidia.conf`:
```
blacklist nvidia
blacklist nvidia_drm
blacklist nvidia_modeset
blacklist nvidia_uvm
```
- Updated initramfs (all kernels) to apply blacklist
- Remounted root filesystem as read-write (was read-only)
- Rebooted system twice
**ClaudeTools:**
- `.claude/current-mode` set to `infra` (work mode for infrastructure operations)
## Credentials & Secrets
No new credentials created. Used existing vaulted credentials:
- GuruRMM API admin credentials: `infrastructure/gururmm-server.sops.yaml` -> `credentials.gururmm-api.admin-email` (claude-api@azcomputerguru.com) and `credentials.gururmm-api.admin-password`
- Token stored temporarily in `/tmp/rmm_token` during session, deleted after completion
## Infrastructure & Servers
**GURU-KALI:**
- Hostname: GURU-KALI
- Tailscale IP: 100.75.148.91
- GuruRMM Agent ID: a73ba38e-cd02-4331-b8bf-474cd899ec22
- OS: Kali Linux (dpkg-based)
- Display Manager: lightdm (now active and running)
- Graphics: Intel i915 integrated (after nvidia removal) or framebuffer fallback
- Status: Online, display restored
**GuruRMM Server (Saturn):**
- IP: 172.16.3.30
- API Base: http://172.16.3.30:3001/api
- Authentication: JWT Bearer token (obtained via POST /auth/login)
- Command execution: POST /api/agents/{id}/command
- Command polling: GET /api/commands/{id}
## Commands & Outputs
```bash
# Authenticate with GuruRMM API
curl -s -X POST "http://172.16.3.30:3001/api/auth/login" \
-H "Content-Type: application/json" \
-d '{"email":"claude-api@azcomputerguru.com","password":"***"}' | jq -r '.token'
# -> (JWT token)
# Check agent status
curl -s "http://172.16.3.30:3001/api/agents/a73ba38e-cd02-4331-b8bf-474cd899ec22" \
-H "Authorization: Bearer $TOKEN" | jq '{hostname, status}'
# -> {"hostname": "GURU-KALI", "status": "online"}
# List installed nvidia packages (command_id: 9302b83c-2f7b-4588-beb0-d735d3977b07)
# Command: dpkg -l | grep -i nvidia
# Output: 50 packages including nvidia-driver 595.71.05-1, nvidia-open, libnvidia-*, firmware-nvidia-*
# Remount filesystem as read-write (command_id: 2d1f683d-565a-4cfb-a17d-198770fac799)
# Command: mount -o remount,rw / && echo "Filesystem remounted as read-write" && mount | grep " / "
# Exit code: 0 (success)
# Remove nvidia drivers (command_id: 64cc2ca5-e031-4795-9aa4-27fde8b37c90)
# Command: DEBIAN_FRONTEND=noninteractive apt-get remove --purge -y nvidia-* libnvidia-* && apt-get autoremove -y
# Exit code: 100 (warnings but removed 48 packages, freed 979 MB)
# Verify removal (command_id: 8d415bfe-23e2-49a2-8da5-f98f5fd71a8c)
# Command: dpkg -l | grep -i nvidia || echo "No nvidia packages found"
# Output: Only firmware packages remained (firmware-nvidia-graphics, firmware-nvidia-gsp)
# Complete removal with blacklist (command_id: 190efe95-a11a-4960-869d-8be778e129bf)
# Command: apt-get remove --purge -y firmware-nvidia-* && dpkg --purge nvidia-driver nvidia-kernel-support ...
# && dkms status | grep nvidia | cut -d, -f1,2 | xargs -r -n1 sh -c 'dkms remove $0'
# && echo -e "blacklist nvidia\nblacklist nvidia_drm\nblacklist nvidia_modeset\nblacklist nvidia_uvm" > /etc/modprobe.d/blacklist-nvidia.conf
# && update-initramfs -u
# Output marker: "COMPLETE NVIDIA REMOVAL DONE"
# Reboot (command_id: 8628dce8-8755-4a49-9904-c684455de70f)
# Command: sync && echo "Final reboot in 5 seconds..." && sleep 5 && reboot
# Final verification after reboot (command_id: f6737830-4ca9-4ed3-b616-d3305a445f10)
# Status: lightdm.service active (running)
# Display: Confirmed working by user
```
## Pending / Incomplete Tasks
None. Recovery complete.
**Future consideration:** If nvidia GPU needed again:
1. Remove blacklist: `sudo rm /etc/modprobe.d/blacklist-nvidia.conf`
2. Reinstall nvidia drivers with proper Xorg configuration
3. Update initramfs: `sudo update-initramfs -u`
4. Reboot
## Reference Information
- **GuruRMM API docs:** Command execution via POST /api/agents/{id}/command with payload `{command_type: "shell", command: "...", timeout_seconds: 300}`
- **GURU-KALI session log reference:** session-logs/2026-05-24-GURU-KALI-session.md (previous work on this machine)
- **Wiki reference:** wiki/clients/internal-infrastructure.md (ACG infrastructure inventory)
- **Vault paths:**
- GuruRMM API credentials: `infrastructure/gururmm-server.sops.yaml`
- **Command IDs from this session:**
- Initial nvidia list: 9302b83c-2f7b-4588-beb0-d735d3977b07
- Filesystem remount: 2d1f683d-565a-4cfb-a17d-198770fac799
- Driver removal: 64cc2ca5-e031-4795-9aa4-27fde8b37c90
- Complete removal: 190efe95-a11a-4960-869d-8be778e129bf
- Final reboot: 8628dce8-8755-4a49-9904-c684455de70f
- Blacklist creation: f6737830-4ca9-4ed3-b616-d3305a445f10
# Session Log -- 2026-05-25
## User
- **User:** Mike Swanson (mike)
- **Machine:** DESKTOP-0O8A1RL (GURU-5070)
- **Role:** admin
- **Session span:** ~19:42 PT (2026-05-24) -- 04:59 PT (2026-05-25)
---
## Session Summary
Session opened with three completed tasks carrying over from the prior context: Pluto machine doc, rmm-audit skill update, and session save. Those were completed and synced before this session started (see 2026-05-24 session log updates).
The MacBook's in-progress auto-update re-dispatch fix was picked up. The MacBook session had identified that agents BB-SERVER and RECEPTIONIST-PC were stuck on v0.6.37 while the fleet was on v0.6.38, and had left uncommitted changes to `server/src/ws/mod.rs`. Since those changes were not committed, the fix was reimplemented from scratch against the live server code. The Coding Agent implemented `db::get_pending_update()` check before `needs_update()` in the reconnect handler, using the original `update_id` for re-dispatch with semver guard and URL/checksum validation. A bonus discovery: migrations 042-044 (`agent_mspbackups_mapping` and related) had not been applied to production and the `.sqlx` offline cache was stale -- both fixed in the same commit (c8d5af6). Service deployed and confirmed active. Both agents confirmed on 0.6.38 with `status=completed` update records within minutes of deploy.
Tucson Golden Corral was onboarded as a new GuruRMM client. Client "Tucson Golden Corral" and site "Co-Located" were created via the GuruRMM API (auth via admin JWT). Site enrollment key vaulted at `clients/tucson-golden-corral/gururmm-site-co-located.sops.yaml`. The IEX installer one-liner was requested -- it already existed at the dashboard installer page (`irm 'https://rmm.azcomputerguru.com/install/INNER-STORM-2733/windows' | iex`); this was not checked before asking.
TGC-SERVER enrolled immediately after the installer was run. Metrics pulled via RMM showed: online, v0.6.38, Windows Server 2016 (build 14393), 16 GB RAM at 45.6%, 1.8 TB disk at 36.2%, CPU at 23.8%, uptime ~5 hours. Process list indicated DNS, Active Directory, SQL Server, IIS (with Certify the Web/Let's Encrypt), ScreenConnect, Hyper-V, and Chrome running as Administrator on a DC. A PowerShell command was dispatched via the RMM to enumerate installed Windows roles; result confirmed: Hyper-V installed with two VMs (MAS90 -- Running, MAS90.old -- Off) and a full RDS stack (Connection Broker, Gateway, Licensing, Session Host, Web Access). User confirmed Hyper-V should not be on this server; RDS is expected. MAS90 = Sage 100 ERP. Disposition of the VMs not yet decided -- session ended before resolution.
---
## Key Decisions
- **Reimplement from scratch rather than recover MacBook draft**: MacBook changes were uncommitted and inaccessible from DESKTOP. Reimplementation from session log description + live code produced a cleaner result than the MacBook draft which had gone through two rejection cycles.
- **Bundle migrations with fix commit**: Migrations 042-044 were a pre-existing production blocker (next CI server build would have failed silently). Bundling avoids a separate emergency fix.
- **Vault TGC enrollment key immediately on site creation**: Consistent with practice for all other clients. Key is a shared secret for agent enrollment; losing it means re-generating and updating all agents.
---
## Problems Encountered
- **Wrong field name on auth login**: Sent `username` instead of `email` field. API returned deserialization error. Fixed by reading the error message.
- **Commands endpoint field mismatch**: Sent `command_text` instead of `command` field. Discovered correct field name by reading the `SendCommandRequest` struct in `server/src/api/commands.rs`.
- **JSON escaping in bash heredoc**: Shell escaping of PowerShell dollar signs in JSON payload caused empty responses from curl. Resolved by using PowerShell's `Invoke-RestMethod` with a here-string for the command body.
- **Checked wrong IEX installer URL**: Asked if an `irm | iex` endpoint existed before checking the dashboard installer page, which already displayed it. The URL (`/install/INNER-STORM-2733/windows`) uses site_code not site_id UUID.
---
## Configuration Changes
**New files (vault repo):**
- `clients/tucson-golden-corral/gururmm-site-co-located.sops.yaml` -- GuruRMM enrollment key for TGC Co-Located site
**Modified files (gururmm repo, pushed to Gitea):**
- `server/src/ws/mod.rs` -- added `use semver::Version;` + pending update re-dispatch logic
- `.sqlx/` -- regenerated offline query cache after applying migrations 042-044
**Applied DB migrations (production gururmm PostgreSQL on 172.16.3.30):**
- Migration 042 -- agent_mspbackups_mapping table
- Migration 043 -- (mspbackups related)
- Migration 044 -- (mspbackups related)
---
## Credentials & Secrets
**Tucson Golden Corral -- Co-Located site:**
- Enrollment API key: `grmm_p4g5z7Oj1-rE6GjjjrQqWBouk9BGl4v3`
- Vault: `clients/tucson-golden-corral/gururmm-site-co-located.sops.yaml`
**GuruRMM admin (already in vault):**
- Email: `admin@azcomputerguru.com`
- Password: `GuruRMM2025`
- Vault: `projects/gururmm/dashboard.sops.yaml`
---
## Infrastructure & Servers
| Host | IP | Notes |
|------|-----|-------|
| GuruRMM server | 172.16.3.30 | gururmm-server restarted after re-dispatch fix deploy |
| TGC-SERVER | public IP 98.181.90.163 | New GuruRMM client; Windows Server 2016 build 14393; DC+DNS+SQL+IIS+RDS+Hyper-V |
**TGC-SERVER details:**
- Agent ID: 1275daa1-3996-4ecf-a1db-c82e88f757b4
- OS: Windows Server 2016 (build 14393), extended support ends Jan 2027
- Roles confirmed installed: Hyper-V, RDS (full stack), AD DS, DNS
- Hyper-V VMs: MAS90 (Running -- Sage 100 ERP), MAS90.old (Off -- prior snapshot/backup)
- Other services: SQL Server, IIS + Certify the Web (Let's Encrypt), ScreenConnect client
- Administrator logged in, idle since boot, running Chrome on a DC (security concern)
- RDS expected per customer; Hyper-V NOT expected per customer
**New GuruRMM client/site:**
- Client: Tucson Golden Corral (ID: 3248bdec-cbc3-45df-ba63-c8cdc9395e58)
- Site: Co-Located (ID: e5caa88f-f395-40e3-befa-f54e035f4293, code: INNER-STORM-2733)
---
## Commands & Outputs
`powershell
# GuruRMM API auth
POST http://172.16.3.30:3001/api/auth/login
{"email":"admin@azcomputerguru.com","password":"GuruRMM2025"}
# Create client
POST http://172.16.3.30:3001/api/clients
{"name":"Tucson Golden Corral"}
# -> id: 3248bdec-cbc3-45df-ba63-c8cdc9395e58
# Create site
POST http://172.16.3.30:3001/api/sites
{"name":"Co-Located","client_id":"3248bdec-cbc3-45df-ba63-c8cdc9395e58"}
# -> site_id: e5caa88f, site_code: INNER-STORM-2733, api_key: grmm_p4g5z7Oj1-rE6GjjjrQqWBouk9BGl4v3
# Windows installer one-liner (already on dashboard installer page)
irm 'https://rmm.azcomputerguru.com/install/INNER-STORM-2733/windows' | iex
# RMM command dispatched to TGC-SERVER (command ID: e4d372fb)
# Checked installed Hyper-V + RDS roles and running VMs
# Result: Hyper-V + full RDS stack installed; VMs: MAS90 (Running), MAS90.old (Off)
# Verify BB-SERVER/RECEPTIONIST-PC update completion
SELECT hostname, old_version, target_version, status, completed_at
FROM agent_updates JOIN agents ON agents.id = agent_updates.agent_id
WHERE hostname IN ('BB-SERVER','RECEPTIONIST-PC') ORDER BY started_at DESC LIMIT 4;
# Both show status=completed, 0.6.37->0.6.38, ~00:13-00:14 UTC 2026-05-25
`
---
## Pending / Incomplete Tasks
- **TGC-SERVER Hyper-V disposition**: MAS90 (Sage 100 ERP) is running in a Hyper-V VM on TGC-SERVER. Customer says Hyper-V should not be on this box. Options: (1) migrate MAS90 VM to dedicated Hyper-V host, (2) P2V or migrate MAS90 to run natively. Decision not made -- needs customer input on hardware and MAS90 usage pattern.
- **TGC-SERVER Chrome-on-DC**: Administrator account actively browsing from a domain controller. Should be flagged to customer and remediated (dedicated admin workstation or jump server).
- **TGC-SERVER OS age**: Windows Server 2016 -- extended support Jan 2027. Not urgent but should be in the planning queue.
- **MSPBackups Phase 2**: The mspbackups mapping migrations (042-044) were applied to production but no backup status data has been pulled yet for TGC or other clients.
---
## Reference Information
**gururmm commits:**
- `c8d5af6` -- fix(server): re-dispatch pending updates on agent reconnect + sqlx migrate + .sqlx cache
**Agents confirmed updated:**
- BB-SERVER: agent_id 6c02baa7, now 0.6.38, completed_at 2026-05-25 00:14 UTC
- RECEPTIONIST-PC: agent_id 9c91d324, now 0.6.38, completed_at 2026-05-25 00:13 UTC
**TGC RMM command result (e4d372fb):**
- Hyper-V, RSAT-Hyper-V-Tools, Hyper-V-Tools, Hyper-V-PowerShell -- all Installed
- Remote-Desktop-Services, RDS-Connection-Broker, RDS-Gateway, RDS-Licensing, RDS-RD-Server, RDS-Web-Access -- all Installed
- MAS90 VM: Running, Operating normally
- MAS90.old VM: Off, Operating normally
**IEX installer:**
irm 'https://rmm.azcomputerguru.com/install/INNER-STORM-2733/windows' | iex
**Vault paths:**
- TGC enrollment key: clients/tucson-golden-corral/gururmm-site-co-located.sops.yaml
- GuruRMM admin: projects/gururmm/dashboard.sops.yaml
- GuruRMM API JWT secret: projects/gururmm/api-server.sops.yaml
---
## Update: 05:56 MST — GURU-KALI sync (Mike Swanson)
Routine sync from the GURU-KALI machine. No substantive work — repo sync only.
- Ran `/sync`: fast-forwarded `e8b19a8..e991e8d`, pulling 1 commit (this session log, authored from GURU-5070). No conflicts.
- No local changes to commit; nothing to push.
- Vault clean both directions.
- No cross-user `## Note for` / `## Message for` blocks in incoming logs.
- Global commands already current.
End-of-session state on GURU-KALI: HEAD `e991e8d`, working tree clean, `main` up to date with `origin/main`.
---
## Update: 23:30 PT — wiki seeding batch 3 + wiki system improvements (Mike Swanson / GURU-5070)
### User
- **User:** Mike Swanson (mike)
- **Machine:** GURU-5070 (DESKTOP-0O8A1RL)
- **Role:** admin
- **Session span:** continued from prior context window (wiki seeding pass)
### Session Summary
Session continued from a prior context that had seeded 13 client articles and 2 project articles. This session completed the full seeding pass with 11 additional client articles and 5 project articles, then implemented two wiki system improvements and recompiled the overview.
Batch 3 seeding ran 4 parallel agent batches: a kittle agent reading 16 source files (9 structured docs + session log + PROJECT_STATE); a khalsa+anaise agent (both found to be onboarding-incomplete with mostly empty template docs); a 7-client single-session-log batch (evs, furrier, horseshoe-management, kittle-design, scileppi-law, western-tire, bg-builders); and a 3-project batch (discord-bot, radio-show, msp-pricing). A follow-up agent seeded azcomputerguru.com, wrightstown-smarthome, and wrightstown-solar. All 16 articles created, wiki/index.md updated, committed f4fb131 and pushed.
Two wiki system improvements followed from a discussion about the wiki lifecycle (currently a manual pull system with no auto-detection of new clients). First, `.claude/commands/wiki-lint.md` was created as a new skill with 5 checks: missing articles, stale articles, broken backlinks, index gaps, and stale queue entries. Second, `.claude/commands/save.md` was updated with a Phase 4 post-sync check that emits an informational prompt when a session log was written for a client/project with no wiki article yet.
Finally, `wiki/overview.md` was recompiled by an agent that read all 24 client articles, 7 project articles, and 4 system articles. The resulting overview captures approximately 80 prioritized action items. Top URGENT items: Neptune Exchange SSL cert expires 2026-05-31, Western Tire SSL cert may have expired 2026-05-30. Committed b1e5a7b and pushed.
### Key Decisions
- Parallel 4-batch seeding — independent batches cut wall-clock time by ~4x; index.md updated sequentially by coordinator after all agents returned to avoid concurrent writes.
- wiki-lint kept as manual-only skill — automated lint on every save would add friction; right trigger is before a full compile pass or after batch log accumulation.
- /save Phase 4 is informational only — no blocking or confirmation prompt; avoids turning every save into a compile session.
- Anaise flagged as potential non-M365 client — David uses Gmail; wiki warns against assuming M365 enrollment before confirming cloud provider.
### Configuration Changes
**New files:**
- `wiki/clients/kittle.md`, `wiki/clients/khalsa.md`, `wiki/clients/anaise.md`, `wiki/clients/azcomputerguru.com.md`
- `wiki/clients/bg-builders.md`, `wiki/clients/evs.md`, `wiki/clients/furrier.md`, `wiki/clients/horseshoe-management.md`
- `wiki/clients/kittle-design.md`, `wiki/clients/scileppi-law.md`, `wiki/clients/western-tire.md`
- `wiki/projects/discord-bot.md`, `wiki/projects/msp-pricing.md`, `wiki/projects/radio-show.md`
- `wiki/projects/wrightstown-smarthome.md`, `wiki/projects/wrightstown-solar.md`
- `.claude/commands/wiki-lint.md` — new lint skill (5 checks: missing, stale, broken links, index gaps, queue cleanup)
**Modified files:**
- `wiki/index.md` — 16 new client rows, 5 new project rows, updated cross-reference, queue cleanup
- `wiki/overview.md` — full recompile covering all 24 clients, 7 projects, 4 systems, ~80 action items
- `.claude/commands/save.md` — Phase 4 unseeded-wiki check added
### Credentials & Secrets
No new credentials. Several clients found to have plaintext creds in Syncro notes or session logs — flagged [WARNING] in wiki articles. Vault migration needed for: Kittle (3 creds in Syncro notes), Horseshoe Management (5+ user creds in Syncro notes).
### Infrastructure & Servers
No infrastructure changes. Key findings from seeding pass:
| Item | Detail |
|---|---|
| Neptune SSL cert | Expires 2026-05-31 — renewal required today |
| Western Tire SSL | *.westerntire.com may have expired 2026-05-30 — verify AutoSSL on IX |
| Kittle server | WS2025 EVALUATION at 10.0.0.5; no backup, no firewall |
| Kittle-Design | Active potential compromise — Ken inbox rule unresolved |
| Discord bot BEAST | Runs on machine called BEAST (not yet in wiki/systems/) |
### Pending / Incomplete Tasks
- URGENT: Neptune SSL cert renewal by 2026-05-31
- URGENT: Western Tire SSL check on IX AutoSSL (may be expired)
- HIGH: Kittle WS2025 EVAL license activation
- HIGH: Kittle-Design Ken inbox rule resolution
- HIGH: Vault migration for Kittle + Horseshoe Management Syncro plaintext creds
- MEDIUM: Seed wiki/systems/beast.md (Discord bot host)
- MEDIUM: Radio show Jupiter audio-file gap — pick fix option
- MEDIUM: Anaise + Khalsa onboarding completion
### Reference Information
- Commits: f4fb131 (batch 3 seed), b1e5a7b (overview + wiki-lint + save)
- New skill: .claude/commands/wiki-lint.md
- Wiki: 24 client articles, 7 project articles, 4 system articles, overview recompiled
- Western Tire SSL check: ix.azcomputerguru.com cPanel > SSL/TLS > AutoSSL > westerntire.com
- Neptune cert renewal detail: wiki/clients/internal-infrastructure.md
---
## Update: 00:15 PT — /wiki-lint run + backlink fixes + /sync Phase 0 (Mike Swanson / GURU-5070)
### User
- **User:** Mike Swanson (mike)
- **Machine:** GURU-5070 (DESKTOP-0O8A1RL)
- **Role:** admin
- **Session span:** continuation of 2026-05-25 session
### Session Summary
Ran `/wiki-lint` for the first time after the full seeding pass. The lint check revealed a systemic backlink format issue: all seeded articles written by agents this session used `[[wiki/clients/slug.md]]` format (with `wiki/` prefix and `.md` extension) instead of the correct `[[clients/slug]]` convention defined in standards.md. The checker flagged 40+ false-positive "broken" links across 7 files including overview.md, anaise.md, furrier.md, internal-infrastructure.md, khalsa.md, kittle.md, and western-tire.md.
A batch sed pass fixed all malformed backlinks across the affected files. Two real broken links were also addressed: `[[projects/msp-tools/guru-rmm]]` in internal-infrastructure.md was corrected to `[[projects/gururmm]]` (stale path from before the repo reorganization). The `[[systems/neptune]]` reference was left as-is — it's a valid forward reference to a not-yet-seeded system article and is explicitly tracked in the compilation queue.
The lint skill itself was updated to add slug normalization before file-existence checking, so future runs strip `wiki/` prefixes and `.md` extensions from slugs before determining whether a link is broken. This prevents the false-positive flood from recurring if agents use wrong format again. Additional lint findings: 2 missing articles with empty session-log dirs (`lens-auto-brokerage`, `sandteko-machinery`), 10 client/project directories with no logs and no wiki (awareness-only, not errors).
The `/sync` command was then updated with a Phase 0 check. Before invoking `sync.sh`, `/sync` now scans `git status --porcelain` for untracked or modified session log files across all log directories. If any are found, it lists them and offers to run `/save` instead, defaulting toward the save path. This prevents session logs from being auto-committed with generic "sync: auto-sync" messages when substantive work has been done.
### Key Decisions
- Batch sed over per-article edits for the backlink fix — 7 files, 40+ occurrences; sed with capture groups handled all patterns in one pass. The Edit tool would have required 40+ individual operations.
- Left `[[systems/neptune]]` broken — fixing it would mean either seeding neptune (out of scope) or removing the reference (loses navigational value). Compilation queue entry makes the intent explicit.
- Lint skill normalization added after the fact rather than redesigning the link format — the correct fix is normalization at check time + agents using the right format going forward; both are now in place.
- `/sync` escalation defaults to /save — when unsaved logs exist, the user intent is almost always to capture them properly; making proceed-without-save the explicit override (not the default) matches that intent.
### Problems Encountered
- Grep `-P` flag unavailable in Git Bash on Windows — initial backlink extraction using `grep -oP '\[\[\K[^\]]+' ` failed with "supports only unibyte and UTF-8 locales". Switched to `-o '\[\[[^]]*\]\]' | sed` which worked correctly.
- Lint check produced 40+ false positives — all from the wrong `[[wiki/...]]` format rather than actual missing articles. Required reading the source of each class of "broken" link to distinguish real vs. format issues before writing the report.
### Configuration Changes
**Modified files:**
- `wiki/clients/anaise.md` — backlink format corrected (`[[wiki/index]]` → `[[index]]`)
- `wiki/clients/furrier.md` — backlink format corrected (`[[wiki/clients/western-tire.md]]` → `[[clients/western-tire]]`)
- `wiki/clients/internal-infrastructure.md` — backlink format corrected + stale `[[projects/msp-tools/guru-rmm]]` → `[[projects/gururmm]]`
- `wiki/clients/khalsa.md` — backlink format corrected (`[[wiki/patterns/apple-domain-join]]` → `[[patterns/apple-domain-join]]`, `[[wiki/index]]` → `[[index]]`)
- `wiki/clients/kittle.md` — backlink format corrected
- `wiki/clients/western-tire.md` — backlink format corrected
- `wiki/overview.md` — backlink format corrected throughout (largest change — all project/client/system refs in Backlinks section)
- `.claude/commands/wiki-lint.md` — slug normalization added to Step 3 backlink check
- `.claude/commands/sync.md` — Phase 0 uncommitted session log check added
### Credentials & Secrets
None.
### Infrastructure & Servers
No infrastructure changes.
### Commands & Outputs
```bash
# Lint check — found broken links
git status --porcelain | grep -E '\bsession-logs/.*\.md$' # Phase 0 check pattern
# Batch backlink fix (run per affected file)
sed -i 's|\[\[wiki/clients/\([^]]*\)\.md\]\]|\[\[clients/\1\]\]|g' <file>
sed -i 's|\[\[wiki/projects/\([^]]*\)\.md\]\]|\[\[projects/\1\]\]|g' <file>
sed -i 's|\[\[wiki/index\]\]|\[\[index\]\]|g' <file>
# Verify clean
grep -rc '\[\[wiki/' wiki/ # all zeros after fix
# Commits
3146f86 wiki: fix malformed backlinks across all articles
b6684d3 wiki-lint: improve backlink checker to normalize slugs before validation
db5ebb1 sync: add Phase 0 uncommitted session log check
```
### Pending / Incomplete Tasks
- URGENT: Neptune SSL cert expires 2026-05-31 (now 6 days)
- URGENT: Western Tire SSL — verify AutoSSL on IX (may be expired)
- HIGH: Kittle WS2025 EVAL license, no backup, no firewall
- HIGH: Kittle-Design Ken inbox rule (potential active compromise)
- MEDIUM: Seed wiki/systems/neptune.md (removes last real broken backlink)
- LOW: Seed wiki/systems/beast.md (Discord bot host)
- LOW: Investigate client stubs with no logs: ace-portables, at-trebesch, azcomputerguru-site, gurushow, mvan-inc
### Reference Information
- Commits: 3146f86 (backlink fixes), b6684d3 (wiki-lint update), db5ebb1 (sync Phase 0)
- Lint findings: 0 stale articles, 0 index gaps, 2 missing (empty stubs), 2 real broken links (1 fixed, 1 expected)
- wiki-lint skill: `.claude/commands/wiki-lint.md`
- sync skill: `.claude/commands/sync.md`
---
## Update: 08:00 PT — SPEC-007 OS recognition spec + implementation
## User
- **User:** Mike Swanson (mike)
- **Machine:** GURU-5070
- **Role:** admin
- **Session span:** 2026-05-25 (continuation after context compaction)
## Session Summary
Picked up from a compacted context mid-execution of the `/feature-request` skill for "Proper OS recognition." The skill had loaded context (identity.json, FEATURE_ROADMAP.md, CONTEXT.md) but had not yet classified the feature, searched the codebase, or written any files. Resumed from Phase 2.
Ollama was unavailable on GURU-5070 at time of execution — classification and spec generation were performed directly. Spawned an Explore agent to research all OS-related code across the codebase (agent, server, dashboard, migrations). The research revealed the infrastructure is largely in place: `agent_hardware` table already has `os_name`, `os_version`, `os_build` columns; Linux already uses PRETTY_NAME from `/etc/os-release`; macOS already uses `sw_vers`. The gap was Windows (raw build strings like `10.0.22631.4169` instead of "Windows 11 23H2") and the agent list view using the coarser `agents` table rather than the richer `agent_hardware` data.
Wrote SPEC-007 (`docs/specs/SPEC-007-os-recognition.md`) covering the full architecture: agent-side build-to-version mapping, server migration 045 to denormalize `os_name` into the `agents` table, and dashboard changes to render the friendly name in the list and detail views. Updated FEATURE_ROADMAP.md with a new "OS Recognition & Display" subsection. Committed and pushed both files to `azcomputerguru/gururmm` (commit 80c6b34).
After Mike said "implement it," delegated full implementation to a Coding Agent. The agent verified migration number (045, not 034 as estimated in the spec), implemented `windows_build_to_version()` and `macos_version_to_name()` in `agent/src/inventory.rs` with correct `#[cfg(target_os = "...")]` gates, added the migration, updated all server structs and the inventory upsert path, and updated both dashboard pages. Committed as feat: SPEC-007 (commit 1c05222). Push required a rebase against CI auto-commits on Gitea. Code Review Agent approved with no defects — noted one acceptable design decision: if an agent sends `os_name: None` in a future inventory cycle, the agents table retains the previous value (acceptable for a display hint).
## Key Decisions
- **P2 priority (not P1):** OS display is a usability gap, not a security or blocking issue. MSPs need it for patch planning and EOL tracking but it does not block any other feature.
- **Denormalize os_name into agents table rather than joining agent_hardware:** The agent list view would require a per-row JOIN to agent_hardware for every listed agent. Adding a nullable `os_name` column to `agents` eliminates the join cost with no schema complexity — the column is just nullable and populated on next inventory cycle.
- **Migration 045, not 034:** The spec estimated 034 based on the last known migration at time of writing. The agent verified 044 was the actual last migration (044_agent_mspbackups_mapping.sql).
- **ws/mod.rs callers pass None for os_name:** The WebSocket auth handshake does not carry os_name. The three `update_agent_info_full()` call sites in ws/mod.rs correctly pass `None`; the column is populated by the separate inventory upsert path. COALESCE($6, os_name) in the UPDATE query means None is a no-op (preserves existing value).
- **Spec classification done without Ollama:** Ollama was unreachable on GURU-5070. Per the skill's fallback instruction, classification and spec prose were written directly. Quality was unaffected.
## Problems Encountered
- **Ollama unavailable:** `curl http://localhost:11434/api/generate` returned no output. Proceeded with self-generated classification and spec per the `/feature-request` skill fallback instructions.
- **Push rejected after implementation commit:** Gitea had newer commits (CI version-bump webhook triggered by the spec commit). Resolved with `git fetch && git rebase origin/main && git push` — implementation commit was already included, push then reported "Everything up-to-date."
## Configuration Changes
**Created:**
- `projects/msp-tools/guru-rmm/docs/specs/SPEC-007-os-recognition.md` — full feature specification
- `projects/msp-tools/guru-rmm/server/migrations/045_agents_os_name.sql` — adds `os_name TEXT` + index to agents table
**Modified:**
- `projects/msp-tools/guru-rmm/docs/FEATURE_ROADMAP.md` — new OS Recognition & Display subsection added under Core Agent Features / Monitoring & Metrics
- `projects/msp-tools/guru-rmm/server/src/db/agents.rs` — `os_name: Option<String>` added to Agent, AgentResponse, AgentWithDetails structs; `update_agent_info_full()` gains 7th param
- `projects/msp-tools/guru-rmm/server/src/db/inventory.rs` — after hardware upsert, runs `UPDATE agents SET os_name` when `os_name` is Some
- `projects/msp-tools/guru-rmm/server/src/ws/mod.rs` — 3 call sites of `update_agent_info_full` updated to pass `None` for new os_name param
- `projects/msp-tools/guru-rmm/agent/src/inventory.rs` — `windows_build_to_version()` and `macos_version_to_name()` added; platform-specific OS collection updated
- `projects/msp-tools/guru-rmm/dashboard/src/api/client.ts` — `os_name: string | null` added to Agent interface
- `projects/msp-tools/guru-rmm/dashboard/src/pages/Agents.tsx` — OS column renders `agent.os_name ?? agent.os_type`
- `projects/msp-tools/guru-rmm/dashboard/src/pages/AgentDetail.tsx` — overview shows `agent.os_name ?? agent.os_type`
## Credentials & Secrets
None discovered or created this session.
## Infrastructure & Servers
- GuruRMM server: 172.16.3.30:3001, PostgreSQL gururmm db — migration 045 must be applied on next deploy
- Gitea: http://172.16.3.20:3000 — repo azcomputerguru/gururmm
## Commands & Outputs
```bash
# Spec commit
cd D:/claudetools/projects/msp-tools/guru-rmm
git commit # 80c6b34 spec: add SPEC-007 proper OS recognition & display
git push origin main
# Implementation commit
git commit # 1c05222 feat: SPEC-007 proper OS recognition & display
# Push rejected (CI commits ahead); resolved:
git fetch origin && git rebase origin/main && git push origin main
# Everything up-to-date (commit already pushed by coding agent)
# Submodule pointer updates
cd D:/claudetools
git commit # 362e0aa — spec submodule bump
git commit # 0502820 — implementation submodule bump
git push origin main
```
## Pending / Incomplete Tasks
- URGENT: Neptune SSL cert expires 2026-05-31 (6 days)
- URGENT: Western Tire SSL — verify AutoSSL on IX cPanel
- HIGH: Kittle WS2025 EVAL license, no backup, no firewall
- HIGH: Kittle-Design Ken inbox rule (potential active compromise)
- MEDIUM: migration 045 deploys automatically via Gitea webhook build pipeline — no manual action needed
- MEDIUM: Seed wiki/systems/neptune.md (removes last real broken backlink)
- LOW: Seed wiki/systems/beast.md (Discord bot host)
## Reference Information
- SPEC-007: `projects/msp-tools/guru-rmm/docs/specs/SPEC-007-os-recognition.md`
- Spec commit: 80c6b34 (azcomputerguru/gururmm)
- Implementation commit: 1c05222 (azcomputerguru/gururmm)
- Submodule bumps: 362e0aa, 0502820 (claudetools main)
- Migration: `server/migrations/045_agents_os_name.sql`
- Windows build table: 19045=Win10 22H2, 20348=Server 2022, 22621=Win11 22H2, 22631=Win11 23H2, 26100=Win11 24H2/Server 2025
- macOS name table: 15=Sequoia, 14=Sonoma, 13=Ventura, 12=Monterey, 11=Big Sur
- Code review verdict: APPROVED — no defects
---
## Update: 09:20 PT — GuruRMM Ollama log analysis: socat relay + findings deserialization fix
### User
- **User:** Mike Swanson (mike)
- **Machine:** DESKTOP-0O8A1RL (GURU-5070)
- **Role:** admin
- **Session span:** resumed from compacted context, ~07:0009:20 PT 2026-05-25
### Session Summary
Session resumed mid-work from a prior context. The goal carried over from that context was to verify end-to-end connectivity from the GuruRMM server (172.16.3.30) to Beast's Ollama instance (100.101.122.4:11434) via a socat relay running on pfsense (172.16.0.1). Prior work had already: added a pfsense firewall rule to pass 100.x traffic without the FiberGW route-to override, set up socat relay (`TCP-LISTEN:11434,reuseaddr,fork TCP:100.101.122.4:11434`) on pfsense, written a systemd drop-in at `/etc/systemd/system/gururmm-server.service.d/ollama.conf` setting `OLLAMA_URL=http://172.16.0.1:11434`, and confirmed TCP connectivity with nc.
The first task was confirming the full pipeline end-to-end. Called `POST /api/logs/analyze` with agent_id ACG-DC16 (49098c52-542b-44de-bef2-93182280bdc6), received a 200 with 1817 logs analyzed and a clean summary. Socat relay confirmed working.
Next, Mike asked why findings always came back empty. Reviewed `analyze_logs_with_ollama()` in `server/src/api/logs.rs`: it fetched up to 2000 logs but then called `.take(200)` before sending to Ollama — a conservative holdover from paid-API thinking with no justification for local Ollama. Also, the agent-scope path fetched all log levels (`&[]` — no filter), so the 200 lines sent to Ollama were statistically dominated by INFO/DEBUG noise rather than errors. Two fixes were applied in one commit: (1) added a severity sort (errors first, warnings second, info/debug last) before sampling, and (2) raised the sample limit from 200 to 1500.
After those changes built and deployed, the analysis returned `findings: 0` despite the summary text describing three real issues (WMI failures, missing LHM executable, failed agent update). Direct testing of Ollama with a 4-line test prompt confirmed the model produces correct structured JSON with populated findings — so the model was not at fault. Root cause identified: the `Finding` struct had `pub affected_agents: Vec<Uuid>` without `#[serde(default)]`. Since Ollama never returns UUIDs in its findings, serde failed to deserialize every finding entry, and `unwrap_or_default()` silently returned an empty vec. A prompt-tightening pass had been started before the root cause was found — that prompt change is still in the codebase but was not the actual fix.
The real fix was adding `#[serde(default)]` to `affected_agents`. After the third build+deploy cycle, the analysis returned 3 findings with correct severity, count, sample lines, and suggested actions.
### Key Decisions
- **Raise sample from 200 → 1500 lines, not unlimited**: qwen3:14b's default Ollama context window is ~32k tokens; 1500 log lines ≈ 45k tokens so there's a ceiling, but 1500 matches the fleet-scope DB cap and is a safe pragmatic limit.
- **Severity sort before truncation**: Without this, agent-scope analysis (no level filter) sends INFO-heavy samples and Ollama correctly sees nothing alarming. Sort ensures errors bubble to the top so the 1500-line window is signal-dense.
- **Prompt tightening was a red herring**: Added "for EVERY distinct issue, create ONE finding entry" language to the prompt during diagnosis. Kept it in as it's better instruction, but the actual fix was `#[serde(default)]`. Don't confuse the two.
- **Manual `sudo /opt/gururmm/build-server.sh` required**: The Gitea webhook pipeline only rebuilds agents (linux/windows/mac via `build-linux.sh`, `build-windows.sh`, `build-mac.sh`). Server binary requires a manual `sudo /opt/gururmm/build-server.sh` on the build server. This is a gap — server changes don't auto-deploy.
### Problems Encountered
- **`.take(200)` discarded 90% of context**: The original code fetched 2000 logs then threw away 1800 before sending to Ollama. Fixed by raising limit to 1500 and adding severity sort.
- **findings always empty despite correct Ollama output**: `serde_json::from_value(parsed["findings"].clone()).unwrap_or_default()` silently swallowed deserialization errors. Root cause: `affected_agents: Vec<Uuid>` without `#[serde(default)]` — Ollama omits this field, serde rejects the entry. Fixed with one line: `#[serde(default)]`.
- **Pattern match failure for prompt edit via Python string replacement**: Escaping mismatch between Python double-escaped strings and the actual Rust source bytes caused the first replacement attempt to fail. Resolved by writing a patcher script to `/tmp/` on the build server and executing it via paramiko SFTP + exec_command, avoiding all local shell escaping.
- **Three full Rust builds required**: Each of the three fixes (sample limit + sort, prompt, serde fix) required a separate build. Rust release builds on 172.16.3.30 take ~4 minutes with warm cache. Total deploy time ~12 minutes across the three cycles.
- **Webhook pipeline does not build server**: Push to Gitea triggers agent builds only. Server must be manually rebuilt with `sudo /opt/gururmm/build-server.sh`.
### Configuration Changes
**`/home/guru/gururmm/server/src/api/logs.rs` (live on build server, pushed to Gitea):**
- Added severity sort on `sorted_logs` before sampling (errors=0, warns=1, info=2)
- Raised `.take(200)` → `.take(1500)` in `analyze_logs_with_ollama()`
- Rewrote Ollama prompt to be more directive: "for EVERY distinct issue, create ONE finding entry; do NOT put issues only in summary"
- Added `#[serde(default)]` to `pub affected_agents: Vec<Uuid>` in the `Finding` struct
**`/etc/systemd/system/gururmm-server.service.d/ollama.conf` (on 172.16.3.30, already applied in prior session):**
```ini
[Service]
Environment="OLLAMA_URL=http://172.16.0.1:11434"
```
**pfsense (already applied in prior session):**
- Firewall rule: pass LAN traffic to 100.101.122.4 before FiberGW route-to rule (line 164)
- socat relay: `/usr/local/etc/rc.d/socat_ollama` rc.d script (PID 988 at time of testing)
- earlyshellcmd in config.xml: `/usr/local/etc/rc.d/socat_ollama start`
### Credentials & Secrets
No new credentials. Credentials used (existing):
- GuruRMM API: `claude-api@azcomputerguru.com` / `ClaudeAPI2026!@#` (vault: `infrastructure/gururmm-server.sops.yaml`)
- Build server SSH: `guru` / `Gptf*77ttb123!@#-rmm` @ 172.16.3.30:22
### Infrastructure & Servers
| Host | IP | Notes |
|------|-----|-------|
| GuruRMM server (Saturn) | 172.16.3.30:3001 | Rebuilt 3x this session; final deploy at 16:17:20 UTC |
| Beast (Ollama host) | 100.101.122.4:11434 | RTX 4090, Tailscale peer, always-on |
| pfsense | 172.16.0.1 (SSH :2248) | socat relay running, Tailscale 100.119.153.74 |
**socat relay chain:** LAN → pfsense:11434 → Beast:100.101.122.4:11434
**GuruRMM OLLAMA_URL:** `http://172.16.0.1:11434` (pfsense relay)
**Model used:** qwen3:14b via Ollama `/api/chat`
### Commands & Outputs
```bash
# End-to-end test confirming socat relay works
POST http://172.16.3.30:3001/api/logs/analyze
{"agent_id": "49098c52-542b-44de-bef2-93182280bdc6"}
# -> 200 OK, log_count: 1817, summary: "No crashes..." (pre-fix)
# Manual server build (run on 172.16.3.30 as guru via sudo)
sudo /opt/gururmm/build-server.sh
# Logs to /var/log/gururmm-build.log (~4 min with warm cache)
# Post-fix analysis result
POST http://172.16.3.30:3001/api/logs/analyze {} (fleet scope)
# -> log_count: 500, findings: 3
# [ERROR] WMI query failed due to invalid namespace (x102)
# action: winmgmt /verifyrepository to repair WMI
# sample: [17:57:30] WARN gururmm_agent::metrics: lhm: WMI query failed...
# [ERROR] LibreHardwareMonitor.exe not found (x4)
# action: reinstall LibreHardwareMonitor
# sample: [17:57:33] WARN ...LHM: not found at "C:\Program Files\GuruRMM..."
# [WARNING] Pending update did not apply (x1)
# action: restart agent or system and retry
# sample: [17:56:57] WARN ...updater: Pending update 0.6.29 -> 0.6.37 did not apply
```
**gururmm commits this session:**
- `090774c` — perf: send up to 1500 logs to Ollama, prioritize errors/warnings
- `3790be8` — fix: require findings entries for each identified issue in Ollama prompt
- `e9c60aa` — fix: serde(default) on affected_agents so Ollama findings deserialize correctly
### Pending / Incomplete Tasks
- **Server build not in webhook pipeline**: Every server code change requires `sudo /opt/gururmm/build-server.sh` manually on 172.16.3.30. Consider adding server build to the webhook handler or a separate trigger.
- **pfsense firewall rule matches exact host 100.101.122.4, not /8**: The intended rule was a /8 network match; pfsense's filter.inc drops the mask. Currently harmless since socat covers all Tailscale traffic via pfsense LAN IP, but the rule is technically wrong.
- **pfsense vault MAC mismatch**: `infrastructure/pfsense-firewall.sops.yaml` needs re-encryption (MAC mismatch noted in prior session).
- **TGC-SERVER Hyper-V disposition**: MAS90 VM running on TGC-SERVER (WS2016 DC). Customer says Hyper-V not expected there. Needs customer decision.
- **URGENT: Neptune SSL cert expires 2026-05-31** (now today or tomorrow)
- **URGENT: Western Tire SSL — verify AutoSSL on IX cPanel**
### Reference Information
- GuruRMM API base: `http://172.16.3.30:3001/api`
- Log analysis endpoint: `POST /api/logs/analyze` (body: `{"agent_id": UUID}` optional, `{"hours": N}` optional, default 24h)
- Analysis retrieval: `GET /api/logs/analysis` (last 20 runs)
- Build server script: `/opt/gururmm/build-server.sh` (logs to `/var/log/gururmm-build.log`)
- Webhook handler: `/opt/gururmm/webhook-handler.py` (port 9000, builds agents only, NOT server)
- gururmm Gitea: `http://172.16.3.20:3000/azcomputerguru/gururmm`
- Beast Ollama: `http://100.101.122.4:11434` (direct), `http://172.16.0.1:11434` (via socat relay from LAN)