diff --git a/.claude/memory/MEMORY.md b/.claude/memory/MEMORY.md
index 7fc1f494..d11d079d 100644
--- a/.claude/memory/MEMORY.md
+++ b/.claude/memory/MEMORY.md
@@ -19,6 +19,7 @@
- [Syncro API — Invoice Verification Pattern](syncro_invoice_verification_pattern.md) — /invoices?customer_id=X returns no ticket linkage; query /invoices/{number} for ticket_id. Compare by ticket ID, not number.
- [Syncro asset/RMM IS API-drivable](reference_syncro_rmm_api_gui_only.md) — CORRECTED 2026-07-10: asset read/create/update, patches, installed_apps, rmm_alerts all work via API; ONLY policy folders + `policy_folder_id` move need the `Assets - Policy Change` token scope (401 today). Archive stays GUI-only. Full catalog: `.claude/standards/syncro/api-reference.md`.
- [Datto EDR detection behavior](reference_datto_edr_detection_behavior.md) — alert `sourceType`: `av`=Datto AV signature, `rule`=EDR reputation detection (both via `edr.py detections`). EDR is reputation-based not structural (wire known-bad file as autostart exe to trip it; loose files aren't surveyed). AV is tamper-protected (console-only disable); disabling Datto AV uninstalls it + Defender auto-reactivates (AMSI blocks scripts with literal EICAR → build from char codes). Verified live on RMM-TEST-MACHINE.
+- [Bug & Project Tracker (Gitea Issues)](project_bug_tracker_gitea.md) — Official tracker for bugs/features/skills across GuruRMM, GuruConnect, ClaudeTools. Dashboard + `bug-tracker` skill. Set up 2026-07-17.
- [Approval Workflow: Tools vs Projects](approval-workflow-tools-vs-projects.md) — Tools (remediation, scripts): Howard/Claude with approval. Projects (GuruRMM): Mike approval for architecture/features; Howard can handle merges/deploys himself (2026-06-21); bugs→bug list.
- [CDP Chrome driver](reference_cdp_chrome_driver.md) — Drive Chrome via DevTools Protocol (.claude/scripts/cdp.py): visible window + screenshots-to-disk so Gemini/Grok can SEE the live site. Use localhost not 127.0.0.1; dedicated profile. Antigravity-style.
- [Firefox driver (ff.py)](reference_ff_firefox_driver.md) — PREFERRED browser driver. Drive Firefox via Playwright (.claude/scripts/ff.py): daemon on :9333, persistent profile, nav/shot/click/type/eval/console/network. Mike dislikes Chrome; claude-in-chrome connector disabled 2026-06-06.
diff --git a/.claude/memory/project_bug_tracker_gitea.md b/.claude/memory/project_bug_tracker_gitea.md
new file mode 100644
index 00000000..b493f58e
--- /dev/null
+++ b/.claude/memory/project_bug_tracker_gitea.md
@@ -0,0 +1,27 @@
+---
+name: Bug & Project Tracker (Gitea Issues)
+description: Gitea Issues is the official bug/feature/skill/project tracker for GuruRMM, GuruConnect, and ClaudeTools. Dashboard at projects/msp-tools/bug-tracker/dashboard.html. Skill at .claude/skills/bug-tracker/skill.md.
+type: project
+---
+
+Mike and Howard want centralized tracking of bugs, features, skills, and project work across
+GuruRMM, GuruConnect, and ClaudeTools — the wiki was not working as a tracker (no status,
+no history, things got forgotten). Set up 2026-07-17.
+
+**Why:** Bug fixes and feature work were getting dropped because there was no visible tracker.
+Unless someone remembered to ask, work just disappeared. The forum (Discord ct-forum) is good
+for discussion/decisions but has no status fields, filtering, or board view.
+
+**How to apply:**
+- When a bug surfaces during a session, file it as a Gitea issue (use the `bug-tracker` skill)
+- Repos tracked: `gururmm`, `guru-connect`, `claudetools` (owner: `azcomputerguru`)
+- Standard labels: bug/feature/skill/project, P1-P4 priority, component tags, status tags (in-progress/blocked/dropped/fixed/verified)
+- Milestones: "Active Sprint" and "Backlog" on each repo
+- **Live dashboard:** `https://tracker.azcomputerguru.com/dashboard.html` (SSL via NPM on Jupiter)
+ - Docker container `acg-tracker` on Jupiter (172.16.3.20:8089), nginx:alpine, files at `/mnt/user/appdata/acg-tracker/`
+ - NPM proxy host ID 12, SSL cert ID 13 (Let's Encrypt DNS challenge via Cloudflare)
+ - Cloudflare DNS A record: tracker.azcomputerguru.com -> 72.194.62.10
+ - Login with Gitea username/password (stored in localStorage via Basic auth)
+ - To update dashboard: re-upload `dashboard.html` to Jupiter via SFTP, `docker restart acg-tracker`
+- Kanban board (Open / In Progress / Blocked / Done), list view, create/comment/close issues from the dashboard
+- Source files: `projects/msp-tools/bug-tracker/`
diff --git a/.claude/skills/bug-tracker/skill.md b/.claude/skills/bug-tracker/skill.md
new file mode 100644
index 00000000..01a6ebc2
--- /dev/null
+++ b/.claude/skills/bug-tracker/skill.md
@@ -0,0 +1,90 @@
+---
+name: bug-tracker
+description: "File, list, update, and close bugs/features/tasks in the Gitea issue tracker across GuruRMM, GuruConnect, and ClaudeTools repos. Visual dashboard at projects/msp-tools/bug-tracker/dashboard.html."
+triggers:
+ - "file a bug"
+ - "track a bug"
+ - "open an issue"
+ - "bug tracker"
+ - "what bugs are open"
+ - "list bugs"
+ - "close issue"
+ - "update issue"
+ - "mark fixed"
+ - "what's being worked on"
+---
+
+# Bug & Project Tracker — Gitea Issues
+
+Manage the ACG bug/feature/skill/project tracker via Gitea Issues API.
+Dashboard: `projects/msp-tools/bug-tracker/dashboard.html`
+
+## Setup
+
+- Gitea: `https://git.azcomputerguru.com/api/v1`
+- Owner: `azcomputerguru`
+- Token: vault `services/gitea` → `credentials.api.api-token`
+- Tracked repos: `gururmm`, `guru-connect`, `claudetools`
+
+## Label scheme
+
+| Label | Purpose |
+|-------|---------|
+| `bug` | Something broken |
+| `feature` | New capability |
+| `skill` | Skill work |
+| `project` | Project-level |
+| `P1-critical` / `P2-high` / `P3-normal` / `P4-low` | Priority |
+| `in-progress` | Being worked |
+| `blocked` | Blocked |
+| `dropped` | Stalled |
+| `fixed` | Fix applied |
+| `verified` | Confirmed working |
+| `security` | Security-related |
+| `component:rmm-agent` / `rmm-server` / `rmm-dashboard` / `guruconnect` / `harness` | Component |
+
+## Operations
+
+### File a new issue
+Read the token from vault. POST to `/repos/{owner}/{repo}/issues`:
+```json
+{
+ "title": "...",
+ "body": "## Problem\n...\n\n## Steps to reproduce\n...\n\n## Expected behavior\n...\n\n## Session context\nFiled from session on MACHINE at DATE",
+ "labels": [label_id, label_id, ...]
+}
+```
+Always include: one type label (bug/feature/skill/project), one priority label, component label(s) if applicable.
+
+### List open issues
+GET `/repos/{owner}/{repo}/issues?state=open&type=issues&limit=50`
+For cross-repo view, query all tracked repos and merge results.
+
+### Update an issue (add progress, change status)
+- Add a comment: POST `/repos/{owner}/{repo}/issues/{number}/comments` with `{"body": "..."}`
+- Change labels: PATCH `/repos/{owner}/{repo}/issues/{number}` or use label endpoints
+- Close: PATCH with `{"state": "closed"}`
+
+### Mark fixed
+Add `fixed` label, add a comment describing the fix, reference the commit if available.
+
+### Mark verified
+Add `verified` label, close the issue.
+
+## Auto-filing (IMPORTANT)
+
+When a bug surfaces during a Claude session — a command fails unexpectedly, a skill hits an error that needs follow-up, or the user reports something broken — **file it as an issue automatically** (with user confirmation for the title/priority). This is the whole point: bugs should not require someone to remember them.
+
+The issue body should include:
+- What happened (error message, unexpected behavior)
+- Context (what was being attempted, which machine, which session)
+- Any workaround applied
+- Links to relevant session logs if available
+
+## Dashboard
+
+Open `projects/msp-tools/bug-tracker/dashboard.html` in a browser.
+Pass the Gitea token as a URL parameter: `?token=xxx`
+It shows a kanban board (Open / In Progress / Blocked / Done) with filtering by repo and type.
+
+$ARGUMENTS
diff --git a/projects/msp-tools/bug-tracker/Dockerfile b/projects/msp-tools/bug-tracker/Dockerfile
new file mode 100644
index 00000000..3e77b405
--- /dev/null
+++ b/projects/msp-tools/bug-tracker/Dockerfile
@@ -0,0 +1,4 @@
+FROM nginx:alpine
+COPY nginx.conf /etc/nginx/conf.d/default.conf
+COPY dashboard.html /usr/share/nginx/html/dashboard.html
+EXPOSE 80
diff --git a/projects/msp-tools/bug-tracker/dashboard.html b/projects/msp-tools/bug-tracker/dashboard.html
new file mode 100644
index 00000000..99c3d88f
--- /dev/null
+++ b/projects/msp-tools/bug-tracker/dashboard.html
@@ -0,0 +1,655 @@
+
+
+
+
+
+ACG Tracker
+
+
+
+
+
+
+
ACG Tracker
+
Bug tracking, project status, and skill work across all Guru repos
+
+
+
+
+
+ Mike: azcomputerguru · Howard: howard
+
+
+
+
+
+
+
ACG Tracker
+
+
+ sign out
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/projects/msp-tools/bug-tracker/nginx.conf b/projects/msp-tools/bug-tracker/nginx.conf
new file mode 100644
index 00000000..c33ee774
--- /dev/null
+++ b/projects/msp-tools/bug-tracker/nginx.conf
@@ -0,0 +1,19 @@
+server {
+ listen 80;
+ server_name localhost;
+ root /usr/share/nginx/html;
+ index dashboard.html;
+
+ # Proxy Gitea API to avoid CORS issues
+ # Use host gateway to reach Gitea on the Docker host at port 3000
+ location /gitea/ {
+ proxy_pass http://host.docker.internal:3000/;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ }
+
+ location / {
+ try_files $uri $uri/ /dashboard.html;
+ }
+}
diff --git a/projects/msp-tools/bug-tracker/serve.sh b/projects/msp-tools/bug-tracker/serve.sh
new file mode 100644
index 00000000..79cc9f6c
--- /dev/null
+++ b/projects/msp-tools/bug-tracker/serve.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+# Serve the bug tracker dashboard on localhost:8089
+# Usage: bash projects/msp-tools/bug-tracker/serve.sh [--open]
+#
+# Pass the Gitea token as ?token=xxx in the browser URL, or set GITEA_TOKEN env var
+# to have it injected automatically.
+
+DIR="$(cd "$(dirname "$0")" && pwd)"
+PORT=8089
+
+echo "[INFO] Serving bug tracker dashboard at http://localhost:$PORT/dashboard.html"
+
+if [[ -n "$GITEA_TOKEN" ]]; then
+ echo "[INFO] Open: http://localhost:$PORT/dashboard.html?token=$GITEA_TOKEN"
+else
+ echo "[INFO] Add ?token=YOUR_GITEA_TOKEN to the URL for authenticated access"
+fi
+
+if [[ "$1" == "--open" ]]; then
+ (sleep 1 && start "" "http://localhost:$PORT/dashboard.html") &
+fi
+
+cd "$DIR"
+python -m http.server $PORT --bind 127.0.0.1
diff --git a/session-logs/2026-07/2026-07-17-howard-bug-tracker-deployment.md b/session-logs/2026-07/2026-07-17-howard-bug-tracker-deployment.md
new file mode 100644
index 00000000..bd767e7a
--- /dev/null
+++ b/session-logs/2026-07/2026-07-17-howard-bug-tracker-deployment.md
@@ -0,0 +1,122 @@
+# 2026-07-17 — Bug & Project Tracker Deployment
+
+## User
+- **User:** Howard Enos (howard)
+- **Machine:** Howard-Home
+- **Role:** tech
+
+## Session Summary
+
+Howard relayed Mike's request for a real bug and project tracker. The existing wiki-based bug tracking was insufficient — bugs and feature work were getting dropped and forgotten because there was no visible tracker with status, priority, history, or assignment. The Discord ct-forum was considered but rejected as a tracker because it lacks status fields, filtering, and board views.
+
+Evaluated options and chose Gitea Issues as the backend since the infrastructure was already running (Gitea on Jupiter, 33 repos, issues enabled everywhere). Created a standardized label scheme (type: bug/feature/skill/project/security; priority: P1-P4; status: in-progress/blocked/dropped/fixed/verified; components: rmm-agent/server/dashboard/guruconnect/harness) and applied it across 4 tracked repos: gururmm, guru-rmm, guru-connect, and claudetools. Created milestones (Active Sprint, Backlog) on each.
+
+Built a single-page dashboard (dashboard.html) that reads/writes directly to the Gitea API — no separate database, no sync jobs, no cache. The dashboard provides: login via Gitea credentials (stored in localStorage), kanban board view (Open/In Progress/Blocked/Done), sortable list view, sidebar sections (RMM Bugs/Features/Security, GuruConnect, Harness Bugs, Skill Work, Projects), search, issue creation, commenting, status changes (in-progress/blocked/fixed/close/reopen), and detail modals with full comment history.
+
+Deployed the dashboard as a Docker container (nginx:alpine) on Jupiter (172.16.3.20:8089), proxied through NPM with Let's Encrypt SSL at tracker.azcomputerguru.com. The nginx config proxies /gitea/ to the local Gitea instance to avoid CORS issues. Created Cloudflare DNS A record pointing to 72.194.62.10. Verified round-trip: create -> read -> comment -> close all work through the proxy, both Howard and Mike logins confirmed working.
+
+Backfilled 60+ issues from existing sources: errorlog.md bugs became ClaudeTools issues (vault leak, remediation-tool failures, discord-dm attachment bug, msp360 licensing, syncro parse errors, unifi-wifi truncation, coord API errors); RMM Thoughts features became gururmm issues (14 features from the backlog); skill work items filed (8 skills needing work + 3 project-level items). All backfilled issues include a "Discovered: YYYY-MM-DD" field in the body so the dashboard shows accurate ages rather than the filing date. Fixed label gaps on all older issues (12 closed gururmm, 5 older guru-connect, guru-rmm #1) so every issue across all repos has type + priority labels. DM'd Mike the tracker URL and login instructions.
+
+## Key Decisions
+
+- **Gitea Issues over Discord forum or external tools** — Gitea was already running, has issues/labels/milestones/API, and requires no new accounts or services. The forum is good for discussion but lacks status fields and board views.
+- **Single HTML dashboard with Gitea API proxy** — no separate database or sync mechanism. The dashboard IS Gitea's data displayed as a board. Changes in Gitea appear immediately in the dashboard and vice versa.
+- **nginx proxy for /gitea/ path** — avoids CORS issues by proxying API calls through the same origin. Uses host.docker.internal to reach Gitea on the Docker host.
+- **Basic auth (localStorage) over API tokens in URLs** — users log in with their Gitea username/password once, stored in browser localStorage. No tokens in URLs or embedded in the page.
+- **"Discovered" date field in issue body** — Gitea doesn't support backdating created_at via API. Added `**Discovered:** YYYY-MM-DD` to issue bodies and the dashboard parses it to show accurate ages.
+- **Strict separation: our code bugs only, no client machine issues** — the tracker only holds bugs in our code (skills, scripts, harness, RMM, GuruConnect) and our feature/project work. Client machine problems, vendor API limitations, and client-specific quirks stay in the wiki and errorlog.
+- **Sidebar sections by domain** — not just repo filters. RMM Bugs / RMM Features / RMM Security / GuruConnect / Harness Bugs / Skill Work / Projects give focused views into each area.
+
+## Problems Encountered
+
+- **Gitea project boards API not exposed in v1.25** — the built-in project board feature exists in the web UI but has no REST API. Solved by building the custom dashboard with its own kanban rendering.
+- **CORS on local file:// origin** — the initial dashboard opened as a local file couldn't make fetch calls to the Gitea API. Solved by serving via a local HTTP server, then deploying as a proper Docker container behind NPM.
+- **nginx proxy 502 to git.azcomputerguru.com** — the container couldn't resolve the external hostname. Fixed by proxying to host.docker.internal:3000 (Gitea's internal port) with --add-host flag on docker run.
+- **NPM SSL cert creation API schema** — the meta field validation was strict. Required dns_challenge + dns_provider + dns_provider_credentials in exact format (no extra fields like letsencrypt_email).
+- **Local DNS at Cascades (10.137.42.1) not resolving new record** — the UniFi DNS server hadn't picked up the new Cloudflare record. Public DNS (8.8.8.8, 1.1.1.1) resolved fine. Workaround: direct IP access; DNS propagation resolves naturally.
+- **sshpass not available on Windows** — needed for SSH to Jupiter with password. Solved using Python paramiko for all SSH/SFTP operations.
+- **Backfilled issues showing "7m" age** — all newly created issues showed minutes-old ages instead of their real discovery dates. Solved by adding Discovered dates to issue bodies and updating the dashboard to parse and display them.
+
+## Configuration Changes
+
+### Files Created
+- `projects/msp-tools/bug-tracker/dashboard.html` — main tracker dashboard (single-page app)
+- `projects/msp-tools/bug-tracker/nginx.conf` — nginx config with Gitea API proxy
+- `projects/msp-tools/bug-tracker/Dockerfile` — nginx:alpine container definition
+- `projects/msp-tools/bug-tracker/serve.sh` — local development server script
+- `.claude/skills/bug-tracker/skill.md` — bug-tracker skill definition
+- `.claude/memory/project_bug_tracker_gitea.md` — project memory for the tracker
+
+### Files Modified
+- `.claude/memory/MEMORY.md` — added bug tracker memory entry
+
+### Gitea Changes (via API)
+- Created 20 labels on each of 4 repos (gururmm, guru-rmm, guru-connect, claudetools)
+- Created 2 milestones per repo (Active Sprint, Backlog) on gururmm, guru-connect, claudetools
+- Filed 10 ClaudeTools bug issues from errorlog
+- Filed 14 GuruRMM feature issues from RMM Thoughts
+- Filed 8 skill work issues + 3 project issues on ClaudeTools
+- Added type/priority labels to all 12 closed gururmm issues, 5 older guru-connect issues, guru-rmm #1
+- Added Discovered dates to all 35 backfilled issue bodies
+
+### Infrastructure Changes
+- Docker container `acg-tracker` created on Jupiter (172.16.3.20:8089)
+- Files deployed to `/mnt/user/appdata/acg-tracker/` on Jupiter
+- NPM proxy host ID 12 created: tracker.azcomputerguru.com -> 172.16.3.20:8089
+- SSL cert ID 13 created: Let's Encrypt via Cloudflare DNS challenge
+- Cloudflare DNS A record: tracker.azcomputerguru.com -> 72.194.62.10
+
+## Credentials & Secrets
+
+- Gitea API token (azcomputerguru): `9b1da4b79a38ef782268341d25a4b6880572063f` (vault: services/gitea)
+- Gitea Howard login: howard / q6Uh-qZCq-d6mMf_DrJ64pf7WQQ6 (vault: services/gitea-howard)
+- Jupiter SSH: root / Th1nk3r^99## (vault: infrastructure/jupiter-unraid-primary)
+- NPM admin: mike@azcomputerguru.com / JcmO6nERg4m5JtD6 (vault: services/npm)
+- Cloudflare DNS token: DRRGkHS33pxAUjQfRDzDeVPtt6wwUU6FwtXqOzNj (vault: services/cloudflare)
+
+## Infrastructure & Servers
+
+- **Jupiter:** 172.16.3.20, Docker host, Unraid
+ - `acg-tracker` container: nginx:alpine, port 8089, restart unless-stopped
+ - Files: `/mnt/user/appdata/acg-tracker/dashboard.html`, `nginx.conf`
+ - `--add-host host.docker.internal:host-gateway` for Gitea proxy
+- **Gitea:** 172.16.3.20:3000 (Docker container `gitea`)
+- **NPM:** 172.16.3.20:7818 (admin), ports 1880/18443
+ - Proxy host #12: tracker.azcomputerguru.com -> 172.16.3.20:8089 (SSL, cert #13)
+- **Cloudflare:** zone 1beb9917c22b54be32e5215df2c227ce
+ - A record: tracker -> 72.194.62.10 (not proxied)
+- **Public URL:** https://tracker.azcomputerguru.com/dashboard.html
+
+## Commands & Outputs
+
+```bash
+# Deploy tracker to Jupiter (via paramiko)
+docker run -d --name acg-tracker --restart unless-stopped \
+ --add-host host.docker.internal:host-gateway \
+ -p 8089:80 \
+ -v /mnt/user/appdata/acg-tracker/dashboard.html:/usr/share/nginx/html/dashboard.html:ro \
+ -v /mnt/user/appdata/acg-tracker/nginx.conf:/etc/nginx/conf.d/default.conf:ro \
+ nginx:alpine
+
+# Update dashboard on Jupiter
+# Upload via SFTP, then: docker restart acg-tracker
+```
+
+## Pending / Incomplete Tasks
+
+- Local DNS at Cascades (10.137.42.1) may still not resolve tracker.azcomputerguru.com — will propagate naturally
+- The bug-tracker skill needs integration into session error-handling workflows so bugs are auto-filed (tracked as claudetools issue #13)
+- Consider adding assignee support to the dashboard (Gitea supports assignees)
+- RMM Thoughts backlog (RMM_THOUGHTS.md) now has corresponding Gitea issues — future feature requests should go directly to Gitea via the tracker
+
+## Reference Information
+
+- Dashboard source: `projects/msp-tools/bug-tracker/dashboard.html`
+- Skill definition: `.claude/skills/bug-tracker/skill.md`
+- Memory: `.claude/memory/project_bug_tracker_gitea.md`
+- Live URL: https://tracker.azcomputerguru.com/dashboard.html
+- Direct URL: http://172.16.3.20:8089/dashboard.html
+- Gitea repos tracked: gururmm, guru-rmm, guru-connect, claudetools
+- NPM proxy host ID: 12, SSL cert ID: 13
+- Discord DM to Mike: message_id 1527866483869220864
+- Final issue counts: gururmm 21 open / 12 closed, guru-connect 18 open, claudetools 21 open, guru-rmm 1 open