--- type: project name: guruconnect display_name: GuruConnect last_compiled: 2026-06-12 compiled_by: GURU-5070/claude-main sources: - projects/msp-tools/guru-connect/ (origin/main @ ded99c5) - .claude/memory/project_guruconnect.md - projects/msp-tools/guru-connect/session-logs/2025-12-29-session.md - session-logs/2026-05-29-session.md - session-logs/2026-05-30-guruconnect-specs.md - session-logs/2026-05-31-mike-guruconnect-h264-decoder.md - session-logs/2026-06-02-mike-guruconnect-enrollment.md backlinks: - gururmm - msp-tools --- # GuruConnect ## Summary GuruConnect is ACG's proprietary remote-access and remote-support tool (ScreenConnect/ConnectWise Control class). It provides real-time screen capture, remote control, and managed-agent enrollment for Windows endpoints, serving MSP technicians. It is a **standalone product** with its own Gitea repo (`azcomputerguru/guru-connect`), independent release cadence, and versioned integration contract with [[gururmm]] (ADR-001). Sibling project to GuruRMM within the [[msp-tools]] portfolio. Current version: **v0.3.0** (live in production at `connect.azcomputerguru.com` since 2026-06-01). v2 Phase 1 (secure session core) exited 2026-05-31 with a clean security audit (0 CRITICAL/HIGH/MEDIUM/LOW). Active development continues on Phase 2 (dashboard enrichment) and SPEC-018 Phase 2 (SYSTEM service session broker, the critical-path item for true unattended access). --- ## Capabilities / Feature Set ### Connectivity / Session - **Attended (support-code) sessions:** agent presents or receives a support code (`XXX-XXX-XXX` format, high-entropy base32-style, single-use via `consumed_at`); technician enters code in dashboard to initiate a viewer session. - **Unattended (persistent) sessions:** agent registers with a per-machine credential (`cak_` key) and maintains a persistent WSS connection. Accessible any time from the operator console without requiring an active user. - **Zero-touch per-site enrollment (SPEC-016, shipped Phase A + B):** a signed per-site installer carries a rotatable enrollment key (`cek_` prefix, hashed Argon2id server-side). On first run the agent calls `POST /api/enroll` with `site_code` + enrollment key + hardware-derived `machine_uid`; server mints a per-machine `cak_` returned once in the response. Auto-approve posture; `machine_uid` collision (e.g. template-cloned VMs) drops the machine to `enrollment_state = 'pending'` and alerts the operator. Site-key rotation blocks new enrollments from old installers; already-enrolled agents (holding `cak_`) are unaffected. - **Deterministic machine identity (SPEC-004):** `machine_uid` is derived from SMBIOS UUID (primary) with motherboard/disk serial fallback, hardware-salted — survives OS re-image on the same hardware. Unique per-tenant `(tenant_id, machine_uid)` in the DB. - **Session lifecycle management (SPEC-004):** stale persistent sessions reaped automatically (TTL); same-machine supersede for racing connect-path agents; operator soft-delete (`?purge=true` on DELETE /api/machines/:id or DELETE /api/sessions/:id sets `deleted_at`) preserves audit history while removing ghost rows from the console. - **Attended-consent state:** schema column `consent_state` (not_required / pending / granted / denied) on sessions. `not_required` for managed/unattended sessions; attended consent flow (Task 5) shipped; dashboard surfaces "awaiting consent" state. - **Session source tagging:** `source` column on sessions: `standalone` (ad-hoc support code) vs `gururmm` (managed via RMM integration contract). - **Tenancy-ready schema:** `tenant_id` (nullable, backfilled to fixed default `00000000-0000-0000-0000-000000000001`) on every scoped table (machines, sessions, support_codes, events, users, agent_keys). Multi-tenancy not enforced until Phase 4; schema flip requires no migration rewrite. ### Remote Control (Agent Surface, Windows only) - **Screen capture:** DXGI primary (GPU-accelerated, handles D3D content), GDI fallback. `agent/src/capture/` module. - **Input injection:** mouse (absolute/relative movement, button events) and keyboard (Win32 `SendInput`). Full key fidelity including Win+R, Ctrl+C/V, and Ctrl+Alt+Del (via separate `guruconnect-sas-service` SAS helper service). `agent/src/input/` module. - **Video encoding:** H.264 hardware encoder via Windows Media Foundation async-MFT (`agent/src/encoder/h264.rs`) with raw+Zstd fallback. Current shipping default: **raw+Zstd** (`DEFAULT_PREFER_H264=false`); H.264 enabled per-agent via `h264-test` tag on the server pending cross-GPU validation (beast→5070 run not yet completed). - **Native viewer:** windowed Rust viewer (`agent/src/viewer/`) using winit + softbuffer for rendering, decodes H.264 via Windows Media Foundation. Three viewer bugs fixed 2026-05-31 (MF back-pressure handling, output-type negotiation, event-loop pump removal). Launched via `guruconnect://` protocol handler or `guruconnect view ` CLI. - **Transport:** Protobuf-over-WSS. Wire format: `proto/guruconnect.proto`. Frames compressed with Zstd (raw path) or H.264 NAL units. - **Agent run modes:** - `agent` — background persistent agent (currently `HKCU Run` autostart; SPEC-018 Phase 1 shipped the SYSTEM service scaffolding, but the session broker / capture worker is Phase 2) - `view ` — viewer window - `install` — install and register `guruconnect://` protocol handler - `launch ` — handle protocol URL - `service-run` — internal service entry point (SPEC-018 Phase 1, LocalSystem, SCM-controlled) - **SYSTEM service (SPEC-018 Phase 1, shipped):** the managed agent registers as an auto-start LocalSystem Windows service (`GuruConnectAgent`, `sc failure` restart/5000ms). Phase 1 covers service install/lifecycle and SCM dispatch; Phase 2 (session broker — `CreateProcessAsUserW` into the active desktop, ACL'd named pipe IPC, `SERVICE_CONTROL_SESSIONCHANGE`) is the critical-path next item. - **Agent binary:** single `.exe` (`guruconnect.exe`); also ships a separate `guruconnect-sas-service.exe` for Ctrl+Alt+Del injection. Statically linked, no .NET/VC++ runtime dependencies. - **Per-machine credential store (SPEC-016 Phase B):** `cak_` key stored DPAPI-machine-encrypted at `%ProgramData%\GuruConnect\credentials\agent.cak`, ACL'd SYSTEM + Administrators. Readable only when the agent runs as SYSTEM; interim fail-fast guard in `agent/src/main.rs:resolve_agent_credential` until SPEC-018 Phase 2 is live. ### Security - **Authentication model (dashboard/viewer):** JWT (HS256, short-lived). Login via `POST /api/auth/login` (Argon2id password verify). Logout revokes the JWT blacklist entry and stops log-tail. - **Viewer tokens:** session-scoped JWTs (sub = viewer, `session_id` claim, `access: control | view-only` split). Issued server-side; viewer presents token in `?token=` query param on the WS handshake. Wrong-session tokens → 403. - **Agent authentication:** per-agent `cak_`-prefixed high-entropy key (SHA-256 hashed in `connect_agent_keys`). Relay (`server/src/relay/mod.rs::validate_agent_api_key`) accepts `cak_` keys and falls back with a deprecation warning to the old shared `AGENT_API_KEY` env var. Login-JWT rejected on the agent plane (401). - **Enrollment security:** two-tier — per-site `cek_` (rotatable gate) vs per-machine `cak_` (durable operating credential). Argon2id KDF for both. Timing-oracle mitigated by a constant-time dummy verify on all reject paths. Cross-site enroll default-refused (409 `ENROLL_SITE_CONFLICT`). - **Rate limiting:** in-memory per-IP limiter (`server/src/middleware/rate_limit.rs`; replaced `tower_governor`). Applied to login and enrollment endpoints. - **Trusted-proxy IP extraction:** `CONNECT_TRUSTED_PROXIES` env var (comma-separated; required for correct client-IP audit keying when behind NPM). Fail-closed to loopback if unset. - **Security headers:** applied via `tower-http`. - **Code signing:** Windows agent `.exe` signed via Azure Trusted Signing (`jsign`, TRUSTEDSIGNING) in `release.yml`, fail-closed. Reuses GuruRMM's cert profile (ADR-002). `beta` channel input signs and publishes a prerelease tag without semver bump. - **Security audit (2026-05-31):** `/gc-audit --pass=security` — PASS, 0 CRITICAL/HIGH/MEDIUM/LOW. Three CRITICALs from the May 29 pre-reset audit closed: (1) session-scoped viewer tokens + session-claim match, (2) JWT blacklist on WS logout, (3) agent plane rejects user-JWTs via `cak_` key check. ### Dashboard - **Tech:** React/TypeScript SPA (`dashboard/src/`), Vite build, served from `server/static/app/` (SPA deep-link fallback). API client (`dashboard/src/api/`). - **Machines list:** live status, hostname, OS, tags, company/site columns. Operator purge (soft-delete) UI. - **Sessions view:** active + historical sessions, viewer count, consent state. - **Support codes view:** create, list, revoke support codes. - **Users admin:** create/edit/disable users, role assignment (admin / viewer). - **Authentication context:** JWT-based, AdminRoute guard for admin-only views. ### Server API Surface | Endpoint | Auth | Purpose | |---|---|---| | `POST /api/auth/login` | None | Issue JWT | | `DELETE /api/auth/logout` | JWT | Revoke JWT + stop WS log | | `GET/POST/DELETE /api/users` | JWT (admin) | User management | | `GET/POST/PUT/DELETE /api/machines` | JWT | Machine list + management | | `GET/POST/DELETE /api/sessions` | JWT | Session history + management | | `GET/POST /api/codes` | JWT | Support code CRUD | | `POST /api/enroll` | None (cek_) | Zero-touch agent enrollment | | `GET/POST /api/sites` | JWT (admin) | Site management | | `POST /api/sites/:id/keys` | JWT (admin) | Issue/rotate enrollment keys | | `GET/POST /api/machine_keys` | JWT (admin) | Manage per-machine cak_ keys | | `GET /api/releases` | JWT | Agent release metadata | | `GET /api/changelog` | JWT | Version changelogs | | `GET /api/downloads` | None | Agent binary download | | `WS /ws/agent` | cak_ / support-code | Agent relay plane | | `WS /ws/viewer` | session-scoped JWT | Viewer relay plane | ### Integrations (Planned) - **GuruRMM native remote control (ADR-001):** versioned contract `/api/integration/v1/` + capability discovery. GuruRMM acts as broker; GuruConnect owns the contract. Spec: `docs/specs/native-remote-control/`. Status: planned (v2 Phase 3). - **End-user (sub-user) portal (SPEC-017):** deny-by-default `end_user` login role; locked-down portal listing only granted machines. Grant primitive already in DB (`user_client_access`, migration 002). Spec authored by Mike. Status: proposed, post v2-console. --- ## Architecture ### Components | Component | Location | Tech | State | |---|---|---|---| | Relay server | 172.16.3.30 port 3002, systemd `guruconnect.service` | Rust, Axum 0.7, sqlx 0.8 | Live, v0.3.0 | | PostgreSQL DB | 172.16.3.30 `guruconnect` DB (shared PG cluster with GuruRMM) | Postgres 14 | Live | | Dashboard SPA | Served from server/static/app/ | React 18, TypeScript, Vite | Live, v0.3.0 | | Windows agent | Distributed .exe (Pluto CI build) | Rust, winit, Win32 APIs | v0.3.0 signed | | SAS helper service | Bundled with agent | Rust, Windows service | Shipped | | NPM reverse proxy | Jupiter 172.16.3.20 | Nginx Proxy Manager | Live | | CI runners | 172.16.3.30 (ubuntu-latest), Pluto 172.16.3.36 (windows-msvc) | Gitea Actions | Active | ### Key Files and Repos - **Repo (Gitea):** `azcomputerguru/guru-connect` (standalone, not a pinned submodule) - **ClaudeTools pointer:** `projects/msp-tools/guru-connect/` (local clone, NOT a pinned submodule — working tree may differ from origin/main) - **Proto schema:** `proto/guruconnect.proto` - **Migrations:** `server/migrations/001`–`010` - **Specs:** `docs/specs/SPEC-001` through `SPEC-018` - **ADRs:** `docs/ARCHITECTURE_DECISIONS.md` - **Roadmap:** `docs/FEATURE_ROADMAP.md` - **Server env:** `server/.env` (gitignored; contains `JWT_SECRET`, `DATABASE_URL`, `CONNECT_TRUSTED_PROXIES`) - **DB credentials:** vault at `projects/guruconnect/database.sops.yaml` - **Portal credentials:** vault at `projects/guruconnect/portal.sops.yaml` - **Service unit installed:** `/etc/systemd/system/guruconnect.service` — NOTE: do NOT overwrite with the repo's `server/guruconnect.service` (repo unit has `WatchdogSec=30s`; installed unit does not — copying it causes restart loops) - **Public URL:** `https://connect.azcomputerguru.com` - **Agent WS:** `wss://connect.azcomputerguru.com/ws/agent` - **Viewer WS:** `wss://connect.azcomputerguru.com/ws/viewer` - **Issue tracker:** `https://git.azcomputerguru.com/azcomputerguru/guru-connect/issues` --- ## Development ### Current Focus - **SPEC-018 Phase 2 (critical path):** session broker — `CreateProcessAsUserW` into the active interactive desktop session, ACL'd named pipe IPC between the SYSTEM service and per-session worker, `SERVICE_CONTROL_SESSIONCHANGE` handling for logon/logoff/fast-user-switch. Required before the managed agent can capture and inject on a machine running as SYSTEM. Requires a Windows VM for integration testing (not runnable on dev host alone). - **H.264 cross-GPU validation:** beast→5070 run pending. Three viewer-side bugs fixed 2026-05-31 (MF back-pressure, output-type negotiation, event-loop freeze). Raw+Zstd remains the shipping default; `DEFAULT_PREFER_H264=false` until sign-off. - **v2 Phase 2 (dashboard + data layer):** machine inventory (SPEC-003), machines list view parity (SPEC-005), universal search (SPEC-006), installer builder (SPEC-007). ### Architecture Decisions | ADR | Decision | |---|---| | ADR-001 | GuruConnect is standalone; integrates with GuruRMM via versioned `/api/integration/v1/` contract. GuruConnect owns the contract; GuruRMM is a consumer only. | | ADR-002 | Release engineering via Gitea Actions; reuses GuruRMM's Azure Trusted Signing cert profile (same ACG identity, no new Azure provisioning). | ### Patterns and Anti-Patterns - **Per-agent `cak_` keys, not a shared secret.** The legacy `AGENT_API_KEY` shared env var is deprecated; relay warns on use. All new enrollments mint per-machine keys. - **Migrations are idempotent and runtime-applied.** `sqlx::migrate!()` runs on server startup (`db.migrate()`). Never pre-apply via psql. Never edit a committed migration (breaks sqlx checksum, crash-loops the server on startup — confirmed in production 2026-05-30 via `0059b21`). - **`DEFAULT_PREFER_H264=false`.** Do not flip to `true` without a confirmed cross-GPU validation run. - **Soft-delete, not hard-delete, for machines and sessions.** `deleted_at IS NULL` is the live filter; purged rows stay for the audit trail. The legacy hard-delete path on `/api/machines/:agent_id` still exists (no `?purge=true`), but new removal UI uses the soft-delete path. - **Sign once, wrap per-site.** The agent binary is signed once by CI (`release.yml`). Per-site config is delivered via a per-site wrapper/MSI that writes the site config around the signed bytes — never appended into the PE (which voids Authenticode). The legacy `download_agent` append path is a known signing-incompatible debt (SPEC-007). - **Agent logging requires `--verbose`.** `FmtSubscriber::with_max_level(INFO)` in `agent/src/main.rs` ignores `RUST_LOG`. Debug output only via `--verbose` / `-v` CLI flag. This burned an investigation session (2026-05-31). - **Non-interactive SSH for build.** The server's cargo/protoc are only on PATH in a login shell (`bash -lc "..."`). A non-interactive shell silently misses them — confirmed on the 2026-05-30 deploy. - **sqlx uses runtime queries.** No `query!` macros, no `.sqlx` cache directory. Server builds without a live DB connection; no `DATABASE_URL` required at compile time. ### Build and Deploy #### Windows agent (CI, Pluto) ``` CI: .gitea/workflows/release.yml Runner: pluto-guruconnect (windows-msvc, Pluto 172.16.3.36) Signing: jsign Azure Trusted Signing (GuruRMM cert profile) Output: guruconnect.exe + guruconnect-sas-service.exe ``` - Beta builds: `channel: beta` workflow_dispatch input → prerelease tag, signed, no semver bump. - Build locally on GURU-5070 (for dev/test): `cargo build -p guruconnect --release --target x86_64-pc-windows-msvc` (~36-43s). #### Linux server (on 172.16.3.30 itself) ```bash # Login shell required (PATH includes cargo/protoc only in login context): ssh guru@172.16.3.30 'bash -lc "..."' # Backup DB before deploy: pg_dump "$DATABASE_URL" | gzip > ~/backups/guruconnect/pre-deploy-$(date +%F-%H%M).sql.gz # Sync to live origin/main (if history was rewritten, ff-only will refuse — use reset): git fetch origin && git reset --hard origin/main # Build dashboard SPA: cd dashboard && npm ci && npm run build # Emits to ../server/static/app/ (gitignored) # Build server binary (scoped to server crate; skips Windows-only agent crate): cargo build --release -p guruconnect-server --target x86_64-unknown-linux-gnu # ~3 min. Output: target/x86_64-unknown-linux-gnu/release/guruconnect-server # Cutover (migrations auto-run on startup via sqlx::migrate!()): sudo systemctl restart guruconnect # Watch: journalctl -u guruconnect -f # Expect: "Migrations complete" then "Server listening" ``` #### Gotchas (all hit 2026-05-30 deploy) - **Installed service unit has no `WatchdogSec`.** The repo's `server/guruconnect.service` has `WatchdogSec=30s`. The installed `/etc/systemd/system/guruconnect.service` does not. Do NOT run `setup-systemd.sh` or copy the repo unit — it causes restart loops every 30s. - **`CONNECT_TRUSTED_PROXIES` must include Jupiter (172.16.3.20).** Without it, every agent's client IP logs as 172.16.3.20 instead of the real public IP. Set `CONNECT_TRUSTED_PROXIES=127.0.0.1,::1,172.16.3.20` in `server/.env`. (Jupiter is the NPM host, not the relay host.) - **`connect_machines.tags` NULL rows** break the startup machine reconcile and the machines list. Migration 007 added a NOT NULL constraint + backfill; if a row somehow regains NULL (e.g. old agent insert path), hot-patch: `UPDATE connect_machines SET tags='{}' WHERE tags IS NULL`. #### Rollback ```bash git reset --hard # Rebuild binary + restart # If migration is incompatible: psql < backup ``` --- ## Active State v0.3.0 deployed and stable. SPEC-018 Phase 2 (session broker) is the next major milestone. GuruConnect is in active development; the feature roadmap is at `docs/FEATURE_ROADMAP.md` in the repo. Key open items: - SPEC-018 Phase 2: session broker + per-session capture worker (blocks true unattended access as SYSTEM; blocks SPEC-013 session selection) - H.264 cross-GPU validation (beast→5070); raw+Zstd is current shipping default - ~18s StartStream latency in agent message pump (`agent/src/transport/websocket.rs:try_recv` 1ms-timeout spin) - Branch protection on `guru-connect` `main` not yet enabled (auto-sync bypassed it once in June 2026) - GC agent not yet deployed to Howard's test machines (parked until SPEC-018 Phase 2) --- ## History Highlights | Date | Event | |---|---| | 2025-12-29 | Machine deletion API added; AdminCommand protobuf message; 12 offline ghost machines purged from DB. | | 2026-01-17 | Phase 1 security/infrastructure work begins; project in early MVP state. | | 2026-05-29 | Security audit finds 3 CRITICALs in relay auth; v2 modernization direction set (SPEC-002). Operational tooling parity sprint begins: Gitea Actions CI, Azure Trusted Signing, Pluto windows-msvc runner, `/gc-feature-request` skill, coord project registration. | | 2026-05-30 | v2 Phase 1 "secure session core" Task 1–7 committed and deployed. 3 audit CRITICALs closed. v2 dashboard (React SPA) replaces v1 portal. Specs SPEC-010/011/012/013/014/015 authored. First production deployment under the new architecture at `connect.azcomputerguru.com`. | | 2026-05-31 | v2 Phase 1 formally exited. `/gc-audit --pass=security` PASS, 0 findings. v0.3.0 released. SPEC-004 (machine_uid, session reaping, operator removal) fully implemented and deployed; 11 live ghost rows purged. H.264 viewer bugs (MF back-pressure, output-type negotiation, event-loop freeze) diagnosed and fixed on GURU-5070. | | 2026-06-01 | v0.3.0 tag published; changelogs finalized. | | 2026-06-02 | SPEC-016 Phase A (enrollment backend, `POST /api/enroll`, per-site key issue/rotate, migration 010) built, reviewed, merged via PR #5. SPEC-016 Phase B (hardware-salted `machine_uid`, first-run enroll client, DPAPI+ACL `cak_` store, run-mode wiring) built, reviewed, merged. SPEC-017 (end-user access, authored by Mike) added. SPEC-018 Phase 1 (LocalSystem service scaffolding, SCM dispatch) built, reviewed, merged via PR #7. Beta release channel added to `release.yml`. | --- ## Backlinks - [[gururmm]] — sibling product; planned integration via `/api/integration/v1/` contract (ADR-001 / GuruRMM ADR-008) - [[msp-tools]] — portfolio context; both products share the 172.16.3.30 host and PG cluster