chore: sync repository to current working state
Some checks failed
Build and Test / Build Server (Linux) (push) Has been cancelled
Build and Test / Build Agent (Windows) (push) Has been cancelled
Build and Test / Security Audit (push) Has been cancelled
Build and Test / Build Summary (push) Has been cancelled
Run Tests / Test Server (push) Has been cancelled
Run Tests / Test Agent (push) Has been cancelled
Run Tests / Code Coverage (push) Has been cancelled
Run Tests / Lint and Format Check (push) Has been cancelled
Some checks failed
Build and Test / Build Server (Linux) (push) Has been cancelled
Build and Test / Build Agent (Windows) (push) Has been cancelled
Build and Test / Security Audit (push) Has been cancelled
Build and Test / Build Summary (push) Has been cancelled
Run Tests / Test Server (push) Has been cancelled
Run Tests / Test Agent (push) Has been cancelled
Run Tests / Code Coverage (push) Has been cancelled
Run Tests / Lint and Format Check (push) Has been cancelled
Brings azcomputerguru/guru-connect up to the authoritative working copy that had been maintained in the claudetools monorepo: Phase 1 security and infrastructure (middleware, metrics, utils, token blacklist, deployment scripts, security audits) plus the native-remote-control integration spec. Preserves the repo .gitignore, .cargo, and server/static/downloads. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
115
specs/native-remote-control/references.md
Normal file
115
specs/native-remote-control/references.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user