docs(guide): XFS hardware-events coverage (EPIC hiveops-agent#102)
New agent.xfs module (architect/internal/claude tabs) documenting native CEN/XFS hardware monitoring via the 32-bit helper, framed built-on-develop / ships-dark / not-yet-enabled. Adds hiveops.hardware.events to the Kafka topic catalog, and hardware-events pointers to technical.agent / .devices / .incident / .agent-proxy overviews. coverage-reconcile green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
---
|
||||
module: agent.xfs
|
||||
title: XFS Device — native CEN/XFS hardware monitoring (MoniManager replacement)
|
||||
tab: Architect
|
||||
order: 30
|
||||
audience: bcos
|
||||
---
|
||||
|
||||
> **Internal · architecture view.** Confirmed against `hiveops-module-xfs` + `native/hiveops-xfs`
|
||||
> on develop 2026-07-23. **Built + on dev, ships DARK: `xfs.enabled=false`, and the module is NOT
|
||||
> in `hiveops-app`'s deps so it is not in the shipped agent JAR yet.** Nothing enabled in prod.
|
||||
> Groundwork to retire Hyosung **MoniManager**. See [technical.agent] and [platform.kafka].
|
||||
|
||||
## Why
|
||||
|
||||
MoniManager reads the ATM hardware through the CEN/XFS layer. This module gives HiveIQ the same
|
||||
native access, so device status and faults come from XFS (a vendor-neutral standard) rather than
|
||||
from journal/log scraping. It feeds the [hardware-events pipeline](platform.kafka) that turns
|
||||
faults into incidents.
|
||||
|
||||
## Shape — a Java module fronting a native helper
|
||||
|
||||
The on-ATM XFS stack is **32-bit** (confirmed on the Nautilus Hyosung lab ATM: `msxfs.dll` is x86,
|
||||
no 64-bit variant). A 64-bit JVM cannot load it in-process, and XFS service providers fault often,
|
||||
so the module drives a **separate 32-bit native helper process**.
|
||||
|
||||
| Component | Role |
|
||||
|---|---|
|
||||
| `XfsModule` (`AgentModule`) | lifecycle, status-change polling, POST orchestration |
|
||||
| `XfsHelperClient` | spawns the native helper, one-shot commands, line-protocol parse |
|
||||
| `XfsDeviceStatus` / `HardwareEvent` | status value object / POST payload DTO |
|
||||
| `HardwareEventClient` | authed POST to agent-proxy (mirrors `IncidentEventClient`) |
|
||||
| `native/hiveops-xfs/hiveops-xfs.exe` | 32-bit C helper: dynamically loads `msxfs.dll`, `startup`/`open`/`status`/`monitor` |
|
||||
|
||||
## Flow (MVP = status-change polling)
|
||||
|
||||
`start()` schedules a poller. Each pass, for every configured logical service, the helper runs
|
||||
`status <logicalName> <category>` (a `WFSGetInfo` read) and returns the device state. On a **state
|
||||
change** the module builds a `HardwareEvent` and POSTs it to
|
||||
`agent-proxy /api/atms/hardware-events`. Change-detection is the agent-side debounce.
|
||||
|
||||
```
|
||||
XfsHelperClient.status(name, cat) → WFSGetInfo → deviceState (online/offline/hwerror/…)
|
||||
if state changed:
|
||||
HardwareEventClient.post(HardwareEvent{deviceClass, logicalName, deviceState, …})
|
||||
→ agent-proxy → devices → Kafka hiveops.hardware.events → incident
|
||||
```
|
||||
|
||||
Status polling (not the async event stream) is the MVP because `WFSGetInfo` returns the device
|
||||
state directly, which is what the incident consumer keys off. The helper also has a long-lived
|
||||
`monitor` mode (`WFSAsyncRegister` event stream) built + lab-tested — the foundation for a later
|
||||
real-time layer (decode `dwEventID`), not yet wired into the module.
|
||||
|
||||
## Native bridge — the four load-bearing CEN/XFS facts
|
||||
|
||||
All standard CEN/XFS 3.x, all confirmed on real Hyosung hardware:
|
||||
|
||||
1. Async completions are numeric **`WM_USER + N`** window messages (`WFS_OPEN_COMPLETE = WM_USER+1`),
|
||||
NOT `RegisterWindowMessage` strings.
|
||||
2. `WFSRESULT` must be **`#pragma pack(1)`** or the field offsets are wrong.
|
||||
3. Request the service version **exactly 3.00** — these Hyosung `BSSpi` SPs reject a range whose
|
||||
high exceeds their max.
|
||||
4. `WFSOpen` takes **`LOGICAL_SERVICES`** names (`CardReader`, `Sensors`, `Encryptor`,
|
||||
`VendorDependentMode`) from `HKEY_USERS\.DEFAULT\XFS`, not the `SERVICE_PROVIDERS` keys.
|
||||
|
||||
**Coexistence:** HiveIQ opens devices and reads status **concurrently with the live terminal
|
||||
application** (MoniPlus2) — the SPs allow multiple apps, `WFSOpen` is non-exclusive. So status
|
||||
monitoring needs no VDM.
|
||||
|
||||
## Not yet built (follow-ups, epic hiveops-agent#102)
|
||||
|
||||
Real-time async event stream wired into the module · **VDM in/out-of-service control**
|
||||
(`WFS_CMD_VDM_ENTER/EXIT_MODE`, the original MoniManager control function) · device resets ·
|
||||
per-class status structs (vendored CEN/XFS SDK headers) · descending version negotiation ·
|
||||
native-helper CI/signing.
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
module: agent.xfs
|
||||
title: XFS Device — native CEN/XFS hardware monitoring (MoniManager replacement)
|
||||
tab: Claude
|
||||
order: 10
|
||||
audience: bcos
|
||||
---
|
||||
|
||||
> **Read this to act.** Built on develop, ships DARK (`xfs.enabled=false`, not in the agent JAR).
|
||||
> Full design: `hiveops-agent/docs/xfs-integration.md` + `docs/hardware-events-pipeline.md`. Epic
|
||||
> hiveops-agent#102.
|
||||
|
||||
## What it does
|
||||
|
||||
On-ATM module that reads Nautilus Hyosung ATM hardware natively via CEN/XFS (through a 32-bit helper
|
||||
process), and POSTs device faults into the [hardware-events pipeline](platform.kafka) so they become
|
||||
incidents. Replaces MoniManager's hardware monitoring.
|
||||
|
||||
## Key files
|
||||
|
||||
- `hiveops-agent/hiveops-module-xfs/` — `XfsModule` (poller), `XfsHelperClient`, `HardwareEvent`,
|
||||
`HardwareEventClient`.
|
||||
- `hiveops-agent/native/hiveops-xfs/` — `xfs_helper.c`, `xfs_min.h`, build scripts, README.
|
||||
|
||||
## To test the helper on an ATM (read-only, safe)
|
||||
|
||||
Lab ATM `AT000999` (`NHAdministrator@192.168.200.43`) has the helper at
|
||||
`C:\hiveops-agent\native\hiveops-xfs.exe`. **That box points at PROD** — `startup`/`open`/`status`
|
||||
are read-only and emit nothing to the backend, but do NOT run the agent module against it.
|
||||
|
||||
```
|
||||
hiveops-xfs.exe startup # negotiate CEN/XFS version
|
||||
hiveops-xfs.exe open CardReader # open a service provider
|
||||
hiveops-xfs.exe status CardReader 201 # read device state (online/offline/hwerror)
|
||||
```
|
||||
|
||||
## To simulate the backend pipeline (no ATM needed)
|
||||
|
||||
Produce a `HardwareEvent` JSON to `hiveops.hardware.events`, or POST to devices
|
||||
`/api/internal/atms/hardware-events` (header `X-Internal-Secret`). A fault deviceState
|
||||
(offline/hwerror/nodevice/fraud) raises an incident; `online` resolves it. See [technical.incident].
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Ships dark** — enabling requires un-darking the module (add to `hiveops-app`) + `xfs.enabled=true`.
|
||||
- The `EVENT` lines from the helper's `monitor` mode carry `dwEventID`, not device state — decoding
|
||||
them needs the CEN/XFS SDK; the module uses `status` polling for the MVP.
|
||||
- 32-bit helper only (the fielded `msxfs.dll` is x86). Cross-build with MinGW-i686.
|
||||
- Never issue a PIN/EPP command (PCI). See the Architect tab for the four native-bridge facts.
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
module: agent.xfs
|
||||
title: XFS Device — native CEN/XFS hardware monitoring (MoniManager replacement)
|
||||
tab: Internal
|
||||
order: 20
|
||||
audience: bcos
|
||||
---
|
||||
|
||||
> **Internal · config & ops.** Built on develop, ships DARK. Not enabled anywhere yet.
|
||||
|
||||
## Configuration (`hiveops.properties`)
|
||||
|
||||
| Property | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `xfs.enabled` | `false` | master switch; module is fully inert until `true` |
|
||||
| `xfs.helper.path` | `native/hiveops-xfs.exe` | path to the 32-bit helper (relative to the agent install dir) |
|
||||
| `xfs.status.devices` | `CardReader:201,Sensors:801,Encryptor:401` | `logicalName:WFS_INF_category` pairs to poll |
|
||||
| `xfs.poll.seconds` | `60` | status-change poll interval |
|
||||
|
||||
The module **self-disables** (stays inert, logs why) unless ALL of: `xfs.enabled=true`, Windows,
|
||||
an `agent.endpoint` is set, and the helper binary exists. Safe to ship to the whole fleet dark.
|
||||
|
||||
## What it emits
|
||||
|
||||
On a device state change it POSTs a `HardwareEvent` (bearer-authed, via `HttpClientHelper`) to
|
||||
`agent-proxy /api/atms/hardware-events`. `deviceClass` is derived from the logical name
|
||||
(CardReader→IDC, Sensors→SIU, Encryptor→PIN, CashDispenser→CDM, ReceiptPrinter→PTR,
|
||||
VendorDependentMode→VDM). `severity` is advisory only — the incident consumer derives its own.
|
||||
|
||||
## Enabling / go-live (not done yet)
|
||||
|
||||
1. Add `hiveops-module-xfs` to `hiveops-app` deps (un-dark → in the JAR).
|
||||
2. Cross-build the 32-bit helper: `native/hiveops-xfs/build-win32.sh` (needs `gcc-mingw-w64-i686`)
|
||||
or `build-win32.cmd` on Windows. `build-dist.sh` bundles `hiveops-xfs.exe` into the Windows dist
|
||||
when present.
|
||||
3. Ship the agent build, set `xfs.enabled=true` per-ATM.
|
||||
|
||||
## Category constants (confirmed on the lab ATM)
|
||||
|
||||
`WFS_INF_IDC_STATUS = 201` (CardReader), `WFS_INF_SIU_STATUS = 801` (Sensors),
|
||||
`WFS_INF_PIN_STATUS = 401` (Encryptor). Service version negotiated is **3.00**. The encryptor is
|
||||
read for status only — **never commanded** (keeps the EPP/PIN path out of active PCI scope).
|
||||
|
||||
## Debug
|
||||
|
||||
Set env `HIVEOPS_XFS_DEBUG=1` when running the helper directly for message-pump tracing. Manual
|
||||
helper commands on an ATM: `hiveops-xfs.exe startup` · `open <logicalName>` ·
|
||||
`status <logicalName> <category>` · `monitor <seconds> <logicalName...>`.
|
||||
@@ -26,6 +26,7 @@ relevant `technical.<service>` guide.
|
||||
| Topic | Producer | Consumers | Payload |
|
||||
|-------|----------|-----------|---------|
|
||||
| `hiveops.device.events` | devices | incident, fleet, reports, recon, vault (`DeviceEventConsumer`) | device create/update/delete → `device_summary` mirrors |
|
||||
| `hiveops.hardware.events` | devices | incident (`HardwareEventConsumer`) | native-XFS device faults/status from the agent → fault→incident. **Stream topic (retention, not compacted).** Built on develop, ships dark — see [agent.xfs], epic hiveops-agent#102. |
|
||||
| `hiveops.fleet.task.events` | fleet | devices | fleet task lifecycle → device-scoped `DeviceFleetTask` history |
|
||||
| `hiveops.incidents.created` | incident | (see service guide) | incident opened |
|
||||
| `hiveops.incidents.updated` | incident | (see service guide) | incident changed |
|
||||
|
||||
@@ -56,3 +56,9 @@ Not exhaustive — see the controller package for the full set.
|
||||
- **Uses `DB_HOST`/`DB_PORT`/`DB_NAME`, not `DB_URL`.** (Contrast with hiveops-journal, which expects a full `DB_URL`.) Dev profile has Flyway disabled and uses H2 `create-drop`; prod uses Flyway + `ddl-auto: validate`.
|
||||
- **`atm_id` / `device_id` dual-write.** V2 migration keeps both columns in sync via a BEFORE INSERT/UPDATE trigger (device-rename Phase 1). `MachineFingerprintService` should accept both `atmId` and `deviceId` JSON field names from heartbeats.
|
||||
- **Exact `@PutMapping`/`@PostMapping` paths win over the `/api/agent/**` wildcard** in `AgentProxyController` — the wildcard is only a fallback for paths not yet given a native controller.
|
||||
|
||||
## Hardware events (XFS) — built on develop, ships DARK
|
||||
|
||||
New agent-authed `POST /api/atms/hardware-events` (AgentAuthFilter) injects `institutionKey` and
|
||||
forwards to devices `/api/internal/atms/hardware-events`. Thin gateway, no Kafka here — mirrors the
|
||||
existing `hardware/sync` path. Not yet enabled. See [agent.xfs], [technical.devices].
|
||||
|
||||
@@ -99,3 +99,10 @@ From `hiveops-app/src/main/resources/hiveops.properties`:
|
||||
- **ATM id resolution order matters:** property → `HIVEOPS_ATM_ID` env → Windows registry → hostname fallback; a mis-imaged box can silently take the wrong identity/institution.
|
||||
- **`agent.update.enabled=false` customers** (e.g. ATEC Java 8 branch) get patches by hand via share.bcos.cloud, not CDN auto-update.
|
||||
- **`hiveops-module-tcr-events` directory exists but is not a listed `<module>`** in the root `pom.xml` (the built TCR modules are `atec-tcr-events` / `nh-tcr-events`); treat the bare `tcr-events` dir as legacy. (unverified intent)
|
||||
|
||||
## XFS device monitoring — built on develop, ships DARK
|
||||
|
||||
New module `hiveops-module-xfs` ([agent.xfs]) reads Nautilus Hyosung ATM hardware natively
|
||||
via CEN/XFS (through a 32-bit native helper), and POSTs device faults into the hardware-events
|
||||
pipeline. Inert by default (`xfs.enabled=false`, not yet in the shipped JAR). Groundwork to retire
|
||||
Hyosung MoniManager. See [platform.kafka] (`hiveops.hardware.events`), epic hiveops-agent#102.
|
||||
|
||||
@@ -129,3 +129,10 @@ Base paths: `/api/atms` + `/api/devices`, `/api/atm-groups`, `/api/atm-makes`, `
|
||||
else `IN_SUPERVISOR` if `inSupervisor`, else `serviceState || status`. An offline agent therefore **masks**
|
||||
supervisor mode in the UI.
|
||||
- **Error message hiding.** `server.error.include-message=never` and `management.endpoint.health.show-details=never` — expect terse errors in prod.
|
||||
|
||||
## Hardware events (XFS) — built on develop, ships DARK
|
||||
|
||||
Devices is the **producer** of `hiveops.hardware.events`. Internal endpoint
|
||||
`POST /api/internal/atms/hardware-events` (agent-proxy forwards agent events to it) →
|
||||
`HardwareEventPublisher` → Kafka topic (stream, not compacted). Consumed by incident. Not yet
|
||||
enabled. See [agent.xfs], [platform.kafka], epic hiveops-agent#102.
|
||||
|
||||
@@ -81,3 +81,11 @@ Not exhaustive — the service has ~26 controllers (ATM groups, cassette config,
|
||||
- **`/atm/...` vs `/device/...` dual mappings** exist on incident and journal-event controllers (Phase 2 device-rename aliases) — both resolve to the same handler.
|
||||
- `JWT_SECRET` is shared with hiveops-auth and hiveops-mgmt; `mgmt.service.url` is used for agent token validation.
|
||||
- Server port is **8080** (NGINX path `/api/incident/`); multipart uploads allow up to 500MB (agent log files).
|
||||
|
||||
## Hardware events (XFS) — built on develop, ships DARK
|
||||
|
||||
`HardwareEventConsumer` consumes `hiveops.hardware.events` and maps native-XFS device faults to
|
||||
incidents via existing trigger types (IDC→CARD_READER_FAIL, CDM→DISPENSER_JAM(CRIT),
|
||||
fraud/tamper→SECURITY_INVESTIGATION(CRIT), else HARDWARE_ERROR), reusing `IncidentAutoCreationService`
|
||||
(`createDeviceFaultIncident`) for dedup + severity. `online` recovery auto-resolves the matching
|
||||
fault. Benign events are history-only. Not yet enabled. See [agent.xfs], [platform.kafka].
|
||||
|
||||
Reference in New Issue
Block a user