---
module: agent.remote
title: Remote Control Module — Claude quick reference
tab: Claude
order: 40
audience: dev
---

> Terse, act-without-guessing reference. Everything below is confirmed in `hiveops-module-remote` source (parent v4.4.3) unless marked `(unverified)`.

## Identity

- **Module name (SPI `getName()` + JAR `Module-Name`):** `hiveops-remote`
- **SPI class:** `com.hiveops.remote.module.RemoteModule`
- **Maven artifact:** `com.hiveops:hiveops-module-remote` (pom version `1.0.0`)
- **Deployed JAR:** `modules/hiveops-module-remote.jar` (drop-in; Jackson shaded in, hiveops-core `provided`)
- **`getVersion()` returns:** `1.0.0-SNAPSHOT` (also the hardcoded `moduleVersion` sent at registration — mismatched with pom `1.0.0`)

## Config keys (all `remote.*`)

| Key | Default | Notes |
|---|---|---|
| `remote.enabled` | `false` | Must be `true` or module won't load |
| `remote.server.url` | `http://localhost:8090` | Required; init throws if blank. Prod = hiveops-remote (`/api/remote/`, `:8090`) |
| `remote.poll.interval.ms` | `5000` | Poll delay |
| `remote.poll.timeout.ms` | `15000` | Declared default but **unused in code** |
| `remote.chunk.size.bytes` | `1048576` | 1 MiB upload chunks |
| `remote.max.file.size.bytes` | `104857600` | 100 MiB cap on FILE_DOWNLOAD |
| `remote.machine.id` | *(not in defaults)* → hostname → random UUID | Registration identity |
| `remote.allowed.paths` | *(not in defaults; empty = allow all)* | Comma/semicolon list; allowlist base dirs |
| `remote.blocked.paths` | *(not in defaults; OS default block list)* | Comma/semicolon list, `*` wildcard |

## Command types (`CommandType` enum)

- Advertised capabilities / handled: `SCREENSHOT`, `FILE_LIST`, `FILE_DOWNLOAD`
- In enum but **NOT** advertised and **NOT** handled → replies `FAILED: Unknown command type`: `SYSTEM_INFO`

## Command status (`CommandStatus`)

- `IN_PROGRESS` (set before execution), `COMPLETED`, `FAILED`
- Factory helpers: `CommandStatusUpdate.inProgress() / completed(result) / completedWithBinary(totalChunks) / failed(msg)`

## Server protocol (base = `remote.server.url`)

| Purpose | Method + path | Auth |
|---|---|---|
| Register | `POST /api/v1/agents/register` | none → returns Bearer token |
| Poll | `GET /api/v1/agents/poll` | Bearer |
| Status | `POST /api/v1/agents/commands/{id}/status` | Bearer |
| Upload chunk | `POST /api/v1/agents/commands/{id}/upload` (multipart `chunkIndex`,`totalChunks`,`checksum`,`data`) | Bearer |
| Heartbeat | `POST /api/v1/agents/heartbeat` | Bearer (**method exists, never called**) |

- Upload checksum = **SHA-256** hex, per chunk.
- Screenshot = AWT Robot → JPEG (default quality `0.85`).

## Deploy / enable

- Enable: `CONFIG_UPDATE` fleet task → `remote.enabled=true` (+ `remote.server.url`, and `remote.allowed.paths` on locked-down ATMs).
- Retrofit JAR: `INSTALL_MODULE` fleet task (filename must end `.jar`; drops into `modules/`, restarts agent).

## Gotchas

- Emits **NO** incident/journal events — no `IAT_*`, no `EventType`. Reporting is only remote-command status/binary to hiveops-remote.
- Talks to **hiveops-remote** (port 8090 / `/api/remote/`), **not** agent-proxy or hiveops-incident.
- Registration failure is logged, **not retried** → token stays null → polls 401 until agent restart.
- `heartbeat()` is dead code (never invoked).
- `remote.poll.timeout.ms` has no effect (declared, unreferenced).
- SCREENSHOT throws `UnsupportedOperationException` on headless JVMs.
- Binary uploads have **no retry**; a failed chunk → command `FAILED`.
- `getVersion()`/registration report `1.0.0-SNAPSHOT` while the pom is `1.0.0`.

## Do NOT

- Do NOT expect `remote.enabled` on by default — it is `false`.
- Do NOT expect it to route through agent-proxy/incident or appear in the journal-event pipeline.
- Do NOT assume `SYSTEM_INFO` works — it is unhandled.
- Do NOT rely on `remote.allowed.paths`/`remote.machine.id`/`remote.blocked.paths` being set from defaults — they aren't in `getDefaultConfiguration()`; set them explicitly.
- Do NOT invent event strings — this module has none.
