---
module: devices.agent-scripts
title: Agent Scripts
tab: Architect
order: 30
audience: dev
---

> **Architect · internal.** Written 2026-07-01 from source. Describes both what is **built** (a devices-owned CRUD store) and the **intended** signed-delivery pipeline that partly exists in hiveops-fleet + the agent but is **not yet connected** to this module's Push action.

## Ownership & shape

`devices.agent-scripts` is a thin CRUD feature fully owned by **hiveops-devices** (Spring Boot, port 8096, DB `hiveiq_devices`). No Kafka topics, no consumers, no cross-service calls, no agent contact originate from this module. See [platform.service-ownership].

```
Devices SPA (AgentScripts.svelte, Settings)
      │  agentScriptAPI → devicesApi (baseURL = window.__APP_CONFIG__.apiUrl, e.g. api.bcos.cloud/devices)
      ▼
hiveops-devices : AgentScriptController  (/api/fleet/scripts)
      ▼
AgentScriptService  →  AgentScriptRepository (JPA)
      ▼
Postgres hiveiq_devices : agent_scripts
```

## Components (hiveops-devices)

| Layer | Class |
|-------|-------|
| Controller | `com.hiveops.devices.controller.AgentScriptController` (`@RequestMapping("/api/fleet/scripts")`) |
| Service | `com.hiveops.devices.service.AgentScriptService` (`@Transactional`) |
| Repository | `com.hiveops.devices.repository.AgentScriptRepository extends JpaRepository<AgentScript, Long>` |
| Entity | `com.hiveops.devices.entity.AgentScript` (`@Table("agent_scripts")`) |
| DTO | `com.hiveops.devices.dto.AgentScriptRequest` (`name`, `description`, `content`, `institutionKey`) |

## Data — `agent_scripts` (Flyway `V15__create_agent_scripts.sql`)

| Column | Type | Notes |
|--------|------|-------|
| `id` | BIGSERIAL PK | `GenerationType.IDENTITY` |
| `name` | VARCHAR(200) NOT NULL | file name; extension implies type |
| `description` | TEXT | nullable |
| `content` | TEXT NOT NULL | raw script body |
| `institution_key` | VARCHAR(50) | nullable; indexed (`idx_agent_scripts_institution_key`) |
| `created_at` | TIMESTAMPTZ | `@CreationTimestamp` / `DEFAULT now()` |
| `updated_at` | TIMESTAMPTZ | `@UpdateTimestamp` / `DEFAULT now()` |

Repository queries: `findAllByOrderByNameAsc()` and `findByInstitutionKeyOrderByNameAsc(String)`. This is the **only** persistence — no mirror tables, no `device_summary` involvement. See [platform.data-architecture].

## API surface

| Method + Path | Behaviour |
|---------------|-----------|
| `GET /api/fleet/scripts?institutionKey=` | Lists all, or filtered by institution key. |
| `POST /api/fleet/scripts` | 400 if name/content blank; else 201 with saved entity. Sets `institutionKey` from body. |
| `PUT /api/fleet/scripts/{id}` | 404 if missing; updates name (if non-blank), description, content. |
| `DELETE /api/fleet/scripts/{id}` | 404 if missing; else 204. |
| `POST /api/fleet/scripts/{id}/push` | **Stub** — returns `200` with `List.of()`. Comment in code: "requires fleet service integration (not yet implemented)". |

All gated by `@PreAuthorize("hasAnyRole('MSP_ADMIN','BCOS_ADMIN')")` at class level.

## Kafka

**None for this module.** It neither produces nor consumes. (The devices service overall publishes `hiveops.device.events` for device inventory, but agent-scripts writes never touch that path.) See [platform.kafka].

## Intended signed-delivery pipeline (NOT wired to this module)

The Overview/SPA copy promises "signed server-side with RSA-SHA256, agent verifies before writing" and "SCRIPT_UPDATE fleet tasks." That machinery exists — but in **hiveops-fleet** and the **agent**, not behind the devices Push endpoint:

```
(intended)  devices push ──X──▶ hiveops-fleet  FleetTaskService (kind = UPDATE_SCRIPT)
                                     │  ScriptSigningService.sign(scriptBytes)   [SHA256withRSA, private key]
                                     ▼  configPatch += signature
                                 fleet_tasks  ──▶ agent-proxy  ──▶ ATM agent
                                                                      │
                            hiveops-agent  hiveops-file-collection  ProcessScriptUpdate
                                     verifySignature(...)  [SHA256withRSA public key] ──▶ writes to scripts dir
```

- **hiveops-fleet:** `FleetTaskKind.UPDATE_SCRIPT` (and `RUN_SCRIPT`); `ScriptSigningService` loads a PKCS8 RSA private key from `script.signing.private-key` (env `SCRIPT_SIGNING_PRIVATE_KEY`) and signs `SHA256withRSA`. Signed tasks flow through the standard fleet-task → agent-proxy path (executor→task-store pattern).
- **agent:** `com.hiveops.filemgmt.ProcessScriptUpdate` requires `scriptName`, `scriptContent` (base64), `scriptSignature`; rejects unsigned scripts; verifies `SHA256withRSA` against an embedded public key; only writes files with `.cmd`/`.sh`/`.ps1` extensions.

The gap: the devices `push` endpoint is a no-op stub and never calls hiveops-fleet, so nothing in this module actually creates a task or signs anything today. There is also a **config-patch key mismatch** between the fleet signer and the agent consumer (see Claude tab / uncertainties) that would need reconciling if the two are ever wired together.

## Cross-links

- [platform.data-architecture] — where device-domain data lives.
- [platform.kafka] — device event bus (not used by this module).
- [platform.service-ownership] — devices owns `agent_scripts`; fleet owns task delivery + signing.
