# Native Remote Control — Code References

> Two repos. **GC** = guru-connect (`D:\claudetools\projects\msp-tools\guru-connect`, lives
> in the claudetools repo). **RMM** = GuruRMM (`projects/msp-tools/guru-rmm`, a git submodule
> tracking `azcomputerguru/gururmm`). Paths below are relative to each repo root.

## Files that will be touched

### guru-connect (GC)

- `server/src/main.rs` — route table; `create_code` `:382`, `list_sessions` `:425`, `get_session`
  `:433`, `list_machines` `:467`, `/health` `:254`, public `/api/version` `:300`. **Add** the
  `/api/integration/v1/` namespace: `GET .../capabilities`, `POST .../sessions`,
  `POST .../sessions/:id/viewer-token`, `POST .../agents/:agent_id/keys`; register the
  server-to-server integration auth layer. Model the (unauthenticated) capabilities endpoint on the
  existing `/api/version` route.
- `CONTRACT.md` (new — GC repo root or `docs/`) — the semver'd integration contract doc both teams
  keep front of mind. Source of truth for the surface; tested in CI (Task 11).
- `server/src/api/releases.rs:76` — `GET /api/version` handler (no auth, for agent polling). Pattern
  to model `GET /api/integration/v1/capabilities` on.
- `server/static/viewer.html` — the existing **web viewer**; gets an `?embed=1` mode (hide standalone
  chrome, accept host-provided session/token, emit `postMessage` lifecycle events for the RMM host).
- `server/src/middleware/security_headers.rs:30` (`frame-ancestors 'none'`) and `:37-39`
  (`X-Frame-Options`) — **the embedding blocker.** Add a per-route scoped allowlist for the viewer
  path only (RMM origin from env); leave every other route at `'none'`.
- `server/src/session/mod.rs` — in-memory `SessionManager`; `register_agent()` `:95`,
  `join_session()` `:254`. **Change** to allow a session to be pre-created/keyed by `agent_id`
  before the agent connects, then bound when the agent registers.
- `server/src/db/sessions.rs` — `create_session()` `:22`. **Change/add** to persist pre-created
  sessions and a `is_managed`/`source` marker; reconcile in-memory state on startup.
- `server/src/db/support_codes.rs` — `create_support_code()` `:24`, `get_support_code()` `:43`.
  Reused as-is for the attended path (broker calls `POST /api/codes`).
- `server/src/relay/mod.rs` — agent WS handler `:55`/`:236`; `validate_agent_api_key()` `:187`
  (currently JWT-or-shared-`AGENT_API_KEY`, comment at `:200` flags DB keys as future).
  **Change** to validate against the new per-machine key table.
- `server/src/auth/jwt.rs` — JWT signing/validation. **Add** a short-lived, session-scoped
  viewer token mint.
- `server/migrations/` — **add** `connect_agent_keys` (per-machine keys) and session columns;
  follow the existing `001_initial_schema.sql` / `003_auto_update.sql` style. Idempotent
  (`IF NOT EXISTS`).
- `proto/guruconnect.proto` — `SessionRequest` `:8`, `StartStream` `:261`, `AgentStatus` `:271`,
  `AdminCommand` `:286`. **Add** `ConsentRequest` / `ConsentResponse` messages.
- `agent/src/session/mod.rs` — `SessionState` `:71`, persistent-vs-support logic. **Change** to
  register against a broker-assigned `agent_id` (= GuruRMM `device_id`).
- `agent/src/transport/websocket.rs` — `connect()` `:32` (builds `?agent_id=&api_key=&support_code=`).
  Pass the per-machine key.
- `agent/src/tray/mod.rs` + a new consent dialog — **add** the attended-mode consent prompt
  (handle `ConsentRequest`).
- `agent/src/install.rs` — `register_protocol_handler()` `:131` (`guruconnect://<session>?token=&server=`).
  Reused for native-viewer launch URLs the broker returns.

### GuruRMM (RMM)

- `server/src/api/commands.rs:87-157` — `POST /api/agents/{agent_id}/command` dispatch
  (online → WS `ServerMessage::Command`; offline → queued). **Reuse** to push the
  "ensure + launch guru-connect" instruction to the endpoint agent.
- `server/src/api/mod.rs:162` — route registration site. **Add** the new broker route.
- `server/src/api/` — **add** `remote_control.rs`: `POST /api/agents/:agent_id/remote-control`
  (body selects `unattended|attended`); talks to the GC server API, returns a viewer launch URL.
- `server/src/db/` + `server/migrations/` — **add** a `remote_control_sessions` record (or reuse
  `tech_sessions` from `010_tunnel_sessions.sql`) for audit (`agent_id`, `tech_id`, `connect_session_id`,
  `mode`, timestamps).
- `agent/src/transport/websocket.rs` — `run_command()` `:1050`, `execute_command()` `:971`.
  **Add** a `RemoteControl`/launch path (or a dedicated command_type) that, on Windows, ensures
  the guru-connect agent binary is present (download + SHA-256 verify) and launches it in the
  requested mode passing `device_id` as the GC `agent_id`.
- `agent/src/device_id.rs:1-99` — source of the stable cross-product identity. Read-only.
- `dashboard/src/pages/AgentDetail.tsx:1893-1931` — tab/header + action-button area.
  **Add** the "Remote Control" button (open viewer URL on success).
- `dashboard/src/components/CommandTerminal.tsx:60-106` — the canonical
  button→`api.post()`→`useQuery` action pattern to copy.
- `dashboard/src/api/client.ts:293-310` — `commandsApi` pattern. **Add** `remoteControlApi.start(agentId, mode)`.

## Similar existing implementations (patterns to follow)

- **Per-agent action dispatch (RMM):** `server/src/api/commands.rs:87-157` + agent reception
  `agent/src/transport/websocket.rs:570-573` → `execute_command()` `:971` → `run_command()` `:1050`.
  The broker's "launch guru-connect" instruction follows this exact send-command path.
- **Dashboard action button → poll (RMM):** `dashboard/src/components/CommandTerminal.tsx:82-105`
  (`useMutation` → `commandsApi.send` → `useQuery` poll). The Remote Control button mirrors this.
- **Per-agent credential issuance (RMM):** `server/src/api/enroll.rs:38-139` — `generate_api_key("agk_")`
  `:103`, `hash_api_key()` `:104`, plaintext returned once `:138`. Model `connect_agent_keys`
  provisioning on this.
- **Support-code minting (GC):** `server/src/main.rs:382` `create_code` + `server/src/db/support_codes.rs:24`.
  The attended path reuses this directly.
- **Agent WS auth handshake (RMM):** `agent/src/transport/websocket.rs:100-197` — how api_key/device_id
  are presented; the per-machine GC key provisioning should align with this lifecycle.
- **Half-built generic tunnel (RMM), for reference only:** server `server/src/api/tunnel.rs:1-232`
  (routes NOT registered), `server/src/db/tunnel.rs:1-152`, `server/migrations/010_tunnel_sessions.sql`,
  agent `agent/src/tunnel/mod.rs:62-197`, WS msgs `server/src/ws/mod.rs:287-300`. The
  `tech_sessions`/`tunnel_audit` schema is a usable model for the remote-control audit record.

## Database schema

### guru-connect (existing — `server/migrations/`)
- `connect_machines` (`001_initial_schema.sql:8`) — `agent_id` UNIQUE, `hostname`, `is_persistent`,
  `status`, plus `agent_version`/`organization`/`site`/`tags` from `003_auto_update.sql`.
- `connect_sessions` (`001_initial_schema.sql:27`) — `id`, `machine_id`, `is_support_session`,
  `support_code`, `status`. **Add** `is_managed` / `source` marker for broker-initiated sessions.
- `connect_support_codes` (`001_initial_schema.sql:59`) — reused unchanged for attended.
- `connect_session_events` (`001_initial_schema.sql:43`) — audit; emit broker/consent events here.
- `releases` (`003_auto_update.sql:9`) — has `checksum_sha256`; reuse for the verify-before-launch
  supply-chain guard.
- **New:** `connect_agent_keys` — `id`, `agent_id` FK, `key_hash`, `created_at`, `revoked_at`.
  Idempotent migration, hashed keys only (mirror RMM enroll pattern).

### GuruRMM (existing — `server/migrations/`)
- Agent identity: `agent_id` (UUID, assigned at WS auth), `device_id` (`agent/src/device_id.rs`),
  `site_id`, per-agent `agk_` key (hashed) from `server/src/api/enroll.rs`.
- `tech_sessions` / `tunnel_audit` (`010_tunnel_sessions.sql`) — model for the new
  `remote_control_sessions` audit table (or extend `tech_sessions` with a `mode`).

> Migration discipline for both Rust servers: idempotent `IF NOT EXISTS`, let the server binary
> apply migrations on startup, `cargo sqlx prepare` if any `query!()` macro changes. See
> `gururmm/sqlx-migrations` standard.
