# hiveops-remote Implementation Plan ## Overview Create **hiveops-remote** at `/source/hiveops-src/hiveops-remote/` with: - **hiveops-remote-server** - Java/Spring Boot control plane + module JAR hosting - **hiveops-remote-module** - Java module JAR implementing AgentModule SPI The module JAR is downloaded from the server when the remote feature is activated, then loaded dynamically into hiveops-agent. ## Requirements - **Screen Capture**: Static on-demand screenshots - **File System**: Browse directories + download files - **License Tiers**: Pro and Enterprise only - **Distribution**: Module JAR downloaded from server on activation - **Location**: `/source/hiveops-src/hiveops-remote/` --- ## Architecture ``` ┌─────────────────────────────────────────────────────────────────┐ │ hiveops-remote │ ├─────────────────────────────┬───────────────────────────────────┤ │ hiveops-remote-server │ hiveops-remote-module │ │ (Spring Boot) │ (AgentModule JAR) │ │ │ │ │ - Control Plane API │ Downloaded & loaded into │ │ - Command Queue │ hiveops-agent at runtime │ │ - Module JAR Hosting │ │ │ - Binary Result Storage │ - Screen Capture │ │ │ - File Browser │ │ PostgreSQL │ - File Download │ │ │ - Result Upload │ └─────────────────────────────┴───────────────────────────────────┘ Flow: 1. Agent checks for remote module availability 2. Downloads hiveops-remote-module.jar from server 3. ModuleLoader loads JAR via SPI 4. Module starts polling server for commands ``` --- ## Project Structure (Maven Multi-Module) ``` /source/hiveops-src/hiveops-remote/ ├── pom.xml # Parent POM ├── hiveops-remote-server/ # Spring Boot server │ ├── pom.xml │ └── src/main/java/com/hiveops/remote/server/ │ ├── RemoteServerApplication.java │ ├── config/ │ │ └── SecurityConfig.java │ ├── controller/ │ │ ├── AgentController.java # Agent endpoints (poll, upload) │ │ ├── RemoteController.java # User endpoints (commands) │ │ └── ModuleController.java # Module JAR download │ ├── service/ │ │ ├── AgentService.java │ │ ├── CommandService.java │ │ └── BinaryService.java │ ├── repository/ │ │ ├── AgentRepository.java │ │ ├── CommandRepository.java │ │ └── BinaryChunkRepository.java │ ├── entity/ │ │ ├── RemoteAgent.java │ │ ├── RemoteCommand.java │ │ └── BinaryChunk.java │ └── dto/ │ ├── AgentRegistrationRequest.java │ ├── CommandRequest.java │ └── ... ├── hiveops-remote-module/ # Agent module JAR │ ├── pom.xml │ └── src/main/java/com/hiveops/remote/module/ │ ├── RemoteModule.java # AgentModule SPI implementation │ ├── RemoteCommandProcessor.java # Command polling & dispatch │ ├── RemoteHttpClient.java # Server communication │ ├── screen/ │ │ ├── ScreenCaptureService.java │ │ ├── LinuxScreenCapture.java # X11/Wayland capture │ │ └── WindowsScreenCapture.java # Robot/GDI capture │ ├── filesystem/ │ │ ├── FileSystemService.java │ │ ├── DirectoryLister.java │ │ ├── FileDownloader.java │ │ └── PathValidator.java # Security │ └── dto/ │ ├── RemoteCommand.java │ ├── ScreenshotParams.java │ └── FileListParams.java ├── hiveops-remote-common/ # Shared DTOs between server & module │ ├── pom.xml │ └── src/main/java/com/hiveops/remote/common/ │ ├── dto/ │ │ ├── CommandType.java │ │ ├── CommandStatus.java │ │ ├── PollResponse.java │ │ └── FileEntry.java │ └── security/ │ └── PathSecurityUtils.java └── deployments/ ├── docker/ │ └── Dockerfile └── docker-compose.yml ``` --- ## Module Loading Flow ### 1. Check for Remote Module (in hiveops-agent) The existing hiveops-agent can be enhanced to check for downloadable modules: ```java // In AgentApplication or a new ModuleDownloader public void checkRemoteModules() { String moduleUrl = config.get("remote.module.url"); if (moduleUrl != null && remoteEnabled) { Path modulePath = downloadModule(moduleUrl, "hiveops-remote-module.jar"); moduleLoader.loadFromPath(modulePath); } } ``` ### 2. Module SPI Registration **File**: `hiveops-remote-module/src/main/resources/META-INF/services/com.hiveops.core.module.AgentModule` ``` com.hiveops.remote.module.RemoteModule ``` ### 3. RemoteModule Implementation ```java public class RemoteModule implements AgentModule { private RemoteCommandProcessor processor; @Override public String getName() { return "hiveops-remote"; } @Override public void initialize(ModuleContext context) { String serverUrl = context.getConfig("remote.server.url"); String agentToken = context.getConfig("remote.agent.token"); RemoteHttpClient client = new RemoteHttpClient(serverUrl, agentToken); processor = new RemoteCommandProcessor(client, context); } @Override public void start() { processor.startPolling(); } @Override public void stop() { processor.stopPolling(); } } ``` --- ## Database Schema (PostgreSQL) ```sql -- In hiveops-remote-server CREATE TABLE remote_agents ( id BIGSERIAL PRIMARY KEY, agent_id UUID UNIQUE NOT NULL DEFAULT gen_random_uuid(), name VARCHAR(255) NOT NULL, hostname VARCHAR(255), platform VARCHAR(50) NOT NULL, agent_version VARCHAR(50), module_version VARCHAR(50), status VARCHAR(50) NOT NULL DEFAULT 'OFFLINE', last_heartbeat_at TIMESTAMPTZ, capabilities JSONB, license_key VARCHAR(255), machine_id VARCHAR(255), created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(license_key, machine_id) ); CREATE TABLE remote_commands ( id BIGSERIAL PRIMARY KEY, command_id UUID UNIQUE NOT NULL DEFAULT gen_random_uuid(), agent_id BIGINT NOT NULL REFERENCES remote_agents(id) ON DELETE CASCADE, type VARCHAR(50) NOT NULL, status VARCHAR(50) NOT NULL DEFAULT 'PENDING', parameters JSONB, result JSONB, error_message TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), started_at TIMESTAMPTZ, completed_at TIMESTAMPTZ ); CREATE TABLE binary_chunks ( id BIGSERIAL PRIMARY KEY, command_id BIGINT NOT NULL REFERENCES remote_commands(id) ON DELETE CASCADE, chunk_index INT NOT NULL, total_chunks INT NOT NULL, data BYTEA NOT NULL, checksum VARCHAR(64), UNIQUE(command_id, chunk_index) ); -- Module JAR versions CREATE TABLE module_versions ( id BIGSERIAL PRIMARY KEY, version VARCHAR(50) NOT NULL UNIQUE, filename VARCHAR(255) NOT NULL, checksum VARCHAR(64) NOT NULL, size_bytes BIGINT NOT NULL, released_at TIMESTAMPTZ DEFAULT NOW(), is_latest BOOLEAN DEFAULT FALSE ); ``` --- ## API Endpoints ### Module Distribution ``` GET /api/v1/modules/latest - Get latest module version info GET /api/v1/modules/{version}/download - Download module JAR ``` ### Agent-Facing (for hiveops-remote-module) ``` POST /api/v1/agents/register - Register agent with module GET /api/v1/agents/poll - Poll for pending commands (long-poll 15s) POST /api/v1/agents/heartbeat - Heartbeat update POST /api/v1/commands/{id}/status - Update command status POST /api/v1/commands/{id}/upload - Upload binary result (chunked) ``` ### User-Facing ``` GET /api/v1/agents - List registered agents GET /api/v1/agents/{id} - Get agent details DELETE /api/v1/agents/{id} - Unregister agent POST /api/v1/commands/screenshot - Request screenshot POST /api/v1/commands/file-list - List directory POST /api/v1/commands/file-download - Download file GET /api/v1/commands/{id} - Get command status GET /api/v1/commands/{id}/result - Download binary result ``` --- ## Screen Capture Implementation ### Linux (X11) ```java public class LinuxScreenCapture { public byte[] capture(ScreenshotParams params) throws Exception { // Option 1: Use Robot (requires X11 display) Robot robot = new Robot(); Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); BufferedImage image = robot.createScreenCapture(screenRect); // Encode as JPEG ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(image, "jpg", baos); return baos.toByteArray(); } } ``` ### Windows ```java public class WindowsScreenCapture { public byte[] capture(ScreenshotParams params) throws Exception { // Same Robot API works on Windows Robot robot = new Robot(); Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); BufferedImage image = robot.createScreenCapture(screenRect); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(image, "jpg", baos); return baos.toByteArray(); } } ``` **Note**: Java's `Robot` class works cross-platform for basic screen capture. For headless Linux, may need Xvfb or alternative approach. --- ## File System Service ```java public class FileSystemService { private final PathValidator pathValidator; public List listDirectory(String path) { pathValidator.validate(path); // Security check Path dir = Paths.get(path); return Files.list(dir) .map(p -> new FileEntry( p.getFileName().toString(), Files.isDirectory(p) ? "DIRECTORY" : "FILE", Files.size(p), Files.getLastModifiedTime(p).toInstant(), getPosixPermissions(p) )) .collect(Collectors.toList()); } public InputStream downloadFile(String path) { pathValidator.validate(path); pathValidator.checkMaxSize(path); return Files.newInputStream(Paths.get(path)); } } ``` --- ## Security 1. **Agent Auth**: License key + machine ID → JWT token (validated against hiveops-mgmt or standalone) 2. **Path Security**: Configurable allowed paths, block `..` traversal 3. **File Size Limit**: Configurable max (default 100MB) 4. **Module Signature**: JAR checksum verification on download --- ## Maven Configuration **Parent pom.xml**: ```xml com.hiveops hiveops-remote 1.0.0-SNAPSHOT pom hiveops-remote-common hiveops-remote-module hiveops-remote-server 21 3.4.1 ``` **hiveops-remote-module pom.xml**: ```xml com.hiveops hiveops-core 3.0.1-SNAPSHOT provided com.hiveops hiveops-remote-common ${project.version} org.apache.maven.plugins maven-shade-plugin ``` --- ## Docker Deployment **docker-compose.yml**: ```yaml version: '3.8' services: hiveops-remote-server: build: . ports: - "8090:8090" environment: - DB_HOST=postgres - DB_NAME=hiveops_remote volumes: - ./modules:/app/modules # Module JARs served from here depends_on: - postgres postgres: image: postgres:16-alpine environment: - POSTGRES_DB=hiveops_remote - POSTGRES_USER=hiveops - POSTGRES_PASSWORD=hiveops volumes: - pgdata:/var/lib/postgresql/data volumes: pgdata: ``` --- ## Implementation Order ### Phase 1: Project Setup 1. Create Maven multi-module project at `/source/hiveops-src/hiveops-remote/` 2. Setup parent pom.xml with modules 3. Create hiveops-remote-common with shared DTOs ### Phase 2: Server (hiveops-remote-server) 4. Create Spring Boot application 5. Create entities and repositories 6. Create AgentController (register, poll, upload) 7. Create RemoteController (user commands) 8. Create ModuleController (JAR download) 9. Database migrations ### Phase 3: Module (hiveops-remote-module) 10. Create RemoteModule implementing AgentModule SPI 11. Create RemoteCommandProcessor (polling loop) 12. Implement ScreenCaptureService 13. Implement FileSystemService + PathValidator 14. SPI registration in META-INF/services ### Phase 4: Agent Integration 15. Add module download capability to hiveops-agent 16. Test dynamic module loading 17. Configuration for remote module URL ### Phase 5: Docker & Testing 18. Create Dockerfile 19. Create docker-compose.yml 20. End-to-end testing --- ## Configuration **hiveops-agent hiveops.properties** (to enable remote module): ```properties # Remote module configuration remote.enabled=true remote.server.url=http://localhost:8090 remote.module.download.url=http://localhost:8090/api/v1/modules/latest/download remote.license.key=${LICENSE_KEY} remote.machine.id=${MACHINE_ID} ``` **hiveops-remote-server application.yml**: ```yaml server: port: 8090 spring: datasource: url: jdbc:postgresql://${DB_HOST:localhost}:5432/${DB_NAME:hiveops_remote} username: ${DB_USER:hiveops} password: ${DB_PASSWORD:hiveops} remote: poll-timeout-ms: 15000 command-timeout-ms: 300000 module-storage-path: /app/modules ``` --- ## Verification 1. **Build**: `mvn clean package` in hiveops-remote 2. **Start Server**: `docker-compose up -d` 3. **Test Module Download**: ```bash curl http://localhost:8090/api/v1/modules/latest curl -O http://localhost:8090/api/v1/modules/1.0.0/download ``` 4. **Integration Test**: - Start hiveops-agent with remote.enabled=true - Verify module downloads and loads - Request screenshot via API - Verify result returned