---
module: devices.sw-deploy
title: Software Deployment
tab: Claude
order: 40
audience: dev
---

**Owner service:** `hiveops-fleet` (port **8097**, DB **`hiveiq_fleet`**). Frontend `DeviceSoftwareTab.svelte` lives in `hiveops-devices` but calls fleet only. NOT owned by devices/incident.

## Task kinds (this tab)
- `SOFTWARE` = download (has `FleetArtifact`).
- `SOFTWARE_INSTALL` = install; **no artifact**; agent runs `softwareinstall.cmd` on staged `software/` dir.

## Endpoints (METHOD path — auth)
| Method | Path | Auth |
|--------|------|------|
| GET | `/api/fleet/tasks/paginated?atmId={id}&taskKind=SOFTWARE&taskKind=SOFTWARE_INSTALL` | USER…BCOS_ADMIN |
| POST | `/api/fleet/tasks` body `{atmIds:[id],taskKind,artifactId?,scheduledAt?}` | ADMIN/MSP_ADMIN/BCOS_ADMIN |
| POST | `/api/fleet/tasks/{id}/approve` \| `/reject` | `FLEET_APPROVER` or `BCOS_ADMIN` |
| POST | `/api/fleet/tasks/{id}/cancel` \| `/retry` | ADMIN/MSP_ADMIN/BCOS_ADMIN |
| GET | `/api/fleet/tasks/history?atmId={id}` | USER…BCOS_ADMIN |
| GET | `/api/fleet/tasks/pending-approval` | FLEET_APPROVER or MSP_ADMIN/BCOS_ADMIN |
| GET | `/api/internal/fleet/tasks/next?atm={atmId}&kind=` | `INTERNAL` (agent) |
| POST | `/api/internal/fleet/tasks/{id}/status` body `{status,downloadOffset,totalSize}` | `INTERNAL` |

Frontend routes hit gateway prefix `/fleet/...` (nginx). Controller base = `/api/fleet`, internal base = `/api/internal/fleet`.

## Tables (`hiveiq_fleet`)
- `fleet_tasks` — live tasks (tab read source).
- `fleet_task_history` — archived terminal tasks (completed downloads/installs land here).
- `device_summary` — device mirror; target lookup + online check + institution scope.
- `fleet_artifacts` — software package (enabled flag, cdn_url, sha256).
- patch window: `PatchWindow` / `PatchWindowDevice`.

## Kafka topics
- Produce: `hiveops.fleet.task.events` (`FleetTaskEventPublisher`; TASK_CREATED/TASK_STATUS/TASK_ARCHIVED).
- Consume: `hiveops.device.events` (group `hiveops-fleet-device-sync` → upsert `device_summary`).

## Status lifecycle
`PENDING_APPROVAL → PENDING → QUEUED → RUNNING → COMPLETED|FAILED` (also `CANCELLED`,`REJECTED`).
- `SOFTWARE`/`SOFTWARE_INSTALL` START in `PENDING_APPROVAL` (need approve).
- On `COMPLETED`: row archived to `fleet_task_history` and **deleted** from `fleet_tasks` (`archiveAndDelete`). 5-min sweep archives COMPLETED/CANCELLED/REJECTED; FAILED stays live.

## Gotchas
- Completed SOFTWARE tasks are gone from `fleet_tasks` → the tab (live paginated) won't show them; use `/tasks/history`.
- Create silently skips a device if: not in `device_summary`, `lastHeartbeat` >15 min old (offline), or out of institution scope. No error surfaced — check fleet logs.
- Disabled artifact → `400 "Artifact '…' is disabled"`.
- `SOFTWARE_INSTALL` with no `scheduledAt` auto-defers to device patch window if assigned.
- Only `PENDING` + (scheduledAt null/past) tasks dispatch; `PENDING_APPROVAL` never reaches agent.
- Create is admin-role; approve is a SEPARATE authority (`FLEET_APPROVER`/`BCOS_ADMIN`) — MSP_ADMIN alone can create but not approve.
- Frontend `getTasksPaginated` sends repeated `taskKind` params (`paramsSerializer indexes:null`).

## Do NOT
- Do NOT look in `hiveops-devices` / `hiveops-incident` DB for these tasks — they are in `hiveiq_fleet`.
- Do NOT expect completed installs in `/tasks/paginated` — query `/tasks/history`.
- Do NOT assume creating the task dispatches it — it sits in `PENDING_APPROVAL` until approved.
- Do NOT invent an artifact for `SOFTWARE_INSTALL` — it has none.
- Do NOT create tasks for offline/out-of-scope devices expecting success — they are skipped silently.
