---
module: devices.device-detail
title: Device Detail
tab: Architect
order: 30
audience: dev
---

How the single-device page is built. Component `AtmDetail.svelte` (~4.2k lines) is a tabbed shell; the "Details" surface is owned by **hiveops-devices**, but the page is a **multi-service aggregator** — most tabs fan out to other backends. See [platform.service-ownership] and [technical.devices].

## Services involved

| Service | Port | Role for this page |
|---------|------|--------------------|
| hiveops-devices (backend) | 8096 | **Owner** of the device record; serves Details/Agent/Hardware/Software/Cassettes/Config-Changes + logs; publishes device events |
| hiveops-incident | 8081 | Journal events, cassette inventory, incident history |
| hiveops-fleet | — | CONFIG_UPDATE fleet task, packet captures, fitted module status, task history |
| hiveops-journal | 8087 | Transactions / replenishments |

Frontend axios clients (`lib/api.ts`): `devicesApi` (base `apiUrl`, default `:8096`), `incidentApi` (`<gateway>/incident`), `gatewayApi` (gateway root, used for `/fleet/*` and `/journal/*`). `gatewayUrl` is derived by stripping a trailing `/devices` from the configured `apiUrl` (`window.__APP_CONFIG__.apiUrl`).

## Data flow

```
AtmDetail.svelte
  ├─ devicesApi  GET /api/atms/{id}  (+ /properties)      ← Atm + AtmProperties (source of truth)
  ├─ devicesApi  GET /api/atms/{id}/hardware|config/changes
  ├─ devicesApi  GET /api/atms/{id}/latest-log | log-collects   (FileSystemResource off disk)
  ├─ incidentApi GET /journal-events/atm/{id}/paginated | /cassette-inventory
  ├─ incidentApi GET /incidents/atm/{id}
  ├─ gatewayApi  GET /fleet/captures?atmId | /fleet/modules/atm/{id} | /fleet/tasks/history?atmId
  └─ gatewayApi  GET /journal/transactions?atmId&type=REPLENISHMENT

Auto-refresh: setInterval 60_000ms → loadAtms() + loadAtmProperties(id); skipped while editing.
```

## Write paths (all admin-gated) and the executor → Kafka pattern

Every mutating device call runs through `AtmService`, persists to the `atms`/`atm_properties` tables, then publishes to Kafka so the four **device_summary mirrors** stay in sync. There is **no reconciler** — a write that skips `DeviceEventPublisher` silently desyncs mirrors.

| Endpoint | Service method | Kafka |
|----------|----------------|-------|
| `PUT /api/atms/{id}` | `updateAtm` | `publishUpdated` → `DEVICE_UPDATED` |
| `PATCH /api/atms/{id}/institution` | `moveInstitution` (upper-cases key) | `publishUpdated` |
| `PATCH /api/atms/{id}/status` | `updateAtmStatus` | `publishUpdated` |
| `POST /api/atms/{id}/reset-state` | `resetAtmState` (IN_SERVICE, clears supervisor) | *(status write; see code)* |
| `PUT /api/atms/{id}/hardware-profile` | `setHardwareProfile` | `publishUpdated` |
| `DELETE /api/atms/{id}` | `deleteAtm` (soft if active, hard if INACTIVE) | `publishDeleted` → `DEVICE_DELETED` (even on soft-delete) |

### Kafka (see [platform.kafka])

- **Produced:** `hiveops.device.events` (`KafkaProducerConfig.DEVICE_EVENTS_TOPIC`), **keyed by `atmId`**, payload `DeviceEventType ∈ {DEVICE_CREATED, DEVICE_UPDATED, DEVICE_DELETED}` via `DeviceEventPublisher`.
- **Consumers:** the `device_summary` mirrors in incident / fleet / reports / recon (and vault). This page's Details tab is the write origin; the other apps consume its events. See [platform.data-architecture].

## The hardware-profile CONFIG_UPDATE flow (frontend-orchestrated)

Distinct from a pure backend action — the UI does two calls in sequence:

```
1. devicesApi PUT /api/atms/{id}/hardware-profile   { profileKey }      → persists profile, publishUpdated
2. gatewayApi POST /fleet/tasks                                          → queues the machine update
     { atmIds:[id], taskKind:'CONFIG_UPDATE',
       configPatch:{ 'atm.hardware.profile': profileKey } }
```

Step 2 is what actually reaches the ATM; the fleet task executor delivers the config patch. The devices backend does **not** create the fleet task itself.

## DB tables (in `hiveiq_devices`, read/written for this page)

- `atms` (`Atm`) — core record; `atm_properties` (`AtmProperties`) — service state, supervisor flags, custom fields.
- `atm_hardware_changes` / `atm_config_changes` — change audit + review badges (acknowledged here).
- `atm_log_collects` (`AtmLogCollect`) — LOG_COLLECT bundle index; log/bundle bytes live on disk (`atmLogService`).
- `device_cassette_config` + `cassette_configurations`/`cassette_slots` — Cassettes tab.
- Cross-service reads hit incident/journal/fleet DBs, not `hiveiq_devices`.

## Cross-links

- [platform.service-ownership] — devices owns the device record; incident/journal/fleet own their tabs.
- [platform.kafka] — `hiveops.device.events` topic + mirror consumers.
- [platform.data-architecture] — device_summary mirror pattern (publish-on-every-change, no reconciler).
