> Full topic contract, configuration schema, and child-registration filters for `monster`. Source of truth: `src/commands/index.js`, `src/specificClass.js` `configure()`, and the schema at `generalFunctions/src/configs/monster.json`.
>
> For an intuitive overview, return to the [Home](Home).
> [!NOTE]
> Pending full node review (2026-05). Content reflects `CONTRACT.md` and current source only.
---
## Topic contract
The registry lives in `src/commands/index.js`. Each descriptor maps a canonical `msg.topic` to its handler; aliases emit a one-time deprecation warning the first time they fire.
monster has **no allow-list enforcement**. There is no `flowController.handle` equivalent and no `mode.allowedActions` / `mode.allowedSources` config slice. The `set.mode` handler is a placeholder. Compare `rotatingMachine`, which gates every topic through the mode matrix — on monster, every topic dispatches unconditionally.
---
## Data model — `getOutput()` shape
Composed each tick by `src/io/output.js``buildOutput()`. Delta-compressed: consumers see only the keys that changed.
### Flat measurement keys
For every `(type, variant, position)` stored in MeasurementContainer, `getFlattenedOutput()` emits the three-segment key (note: monster does **not** add a `<childId>` segment, unlike `rotatingMachine`):
| Key | Type | Unit | Notes |
|:---|:---|:---|:---|
| `flow.manual.atequipment` | number | m³/h | Last `data.flow` value (after conversion). |
| `flow.measured.upstream` | number | m³/h | Last measured-child reading at this position. |
| `flow.measured.atequipment` | number | m³/h | Same. |
| `flow.measured.downstream` | number | m³/h | Same. |
### Scalar keys
<!-- BEGIN AUTOGEN: data-model — populate via wiki-gen tool (TODO) -->
| Key | Type | Source | Notes |
|:---|:---|:---|:---|
| `running` | boolean | `m.running` | True between `_beginRun` and `_endRun`. |
| `pulse` | boolean | `m.pulse` | True only on the tick a pulse is emitted; false otherwise. |
| `bucketVol` | number (L) | `m.bucketVol` | Composite volume accumulated this run. |
| AQUON sample name | `functionality.aquonSampleName` | _unset_ | Forwarded to `source.aquonSampleName` in `extraSetup`. Falls back to `'112100'` in `_initState`. |
| SubType | `asset.subType` | `"pressure"` | Schema only; misleading default for a sampling cabinet (flag). |
| Model | `asset.model` | `"Unknown"` | Schema only. |
| Empty bucket weight (kg) | `asset.emptyWeightBucket` | `3` | Used in `bucketWeight = bucketVol + emptyWeightBucket` and in `maxVolume = maxWeight - emptyWeightBucket`. |
### Constraints (`config.constraints`)
| Form field | Config key | Default | Range | Notes |
|:---|:---|:---|:---|:---|
| Sampling time (hr) | `constraints.samplingtime` | `0` | ≥ 0 | Run length. Used as `samplingtime · 3600 · 1000` ms for `stop_time`. |
| Sampling period (hr) | `constraints.samplingperiod` | `24` | ≥ 1 | Documented as the fixed composite-collection period; not enforced by `samplingProgram` — the AQUON schedule arms the run. |
| Min volume (L) | `constraints.minVolume` | `5` | ≥ 5 | Used in `targetVolume = minVolume · √(maxVolume / minVolume)`. |
| Nominal flow min (m³/h) | `constraints.nominalFlowMin` | `0` | ≥ 0 | Lower bound of rain-driven flow prediction. |
| Flow max (m³/h) | `constraints.flowMax` | `0` | ≥ 0 | Upper bound of rain-driven flow prediction. |
| Max rain reference | `constraints.maxRainRef` | `10` | > 0 | Rain index that maps to `flowMax`. |
| Min sample interval (s) | `constraints.minSampleIntervalSec` | `60` | ≥ 0 | Cooldown guard. |
> [!WARNING]
> **Default `flowMax = 0` blocks every run.** `validateFlowBounds` requires `0 ≤ nominalFlowMin < flowMax`. Out of the box the bounds are invalid and `_beginRun` never fires. Set `flowMax` to a realistic upper bound before deploying.
### Unit policy
monster has **no `requireUnitForTypes` policy** declared in `specificClass`. Conversions happen at the boundary:
| Flow (measured-child) | as supplied | as supplied | `flowTracker.handleMeasuredFlow` defaults to `'m3/h'` when `unit` is missing; **does not convert**. Wire children that emit in m³/h. |
| Volume (`bucketVol`) | L | L | Output also exposes m³ derivations (`targetVolumeM3`, `targetDeltaM3`). |
| Weight | kg | kg | |
| Time | s (timers) / ms (timestamps) | mixed | `timePassed` / `timeLeft` in s, `nextDate` in epoch ms. |
---
## Child registration
Source: `src/specificClass.js``_wireMeasurementChild`. The registrar subscribes to all three `flow.measured.<position>` events on the child's measurement emitter as long as the child's `config.asset.type` is `'flow'` or unset.
| Software type | Filter | Wired to | Side-effect |
|:---|:---|:---|:---|
| `measurement` | `asset.type='flow'` (or missing) | `flowTracker.handleMeasuredFlow` (handles all three positions) | Latches latest measured flow per position; `getEffectiveFlow` blends across positions and with `manualFlow`. |
| `measurement` | `asset.type` anything else | _ignored_ | The branch returns early; no listener is attached. |
monster has **no position-based filtering**. Unlike `rotatingMachine` (which routes upstream pressure separately from downstream), all three flow positions are wired to the same handler and the latest value per position wins.
There are **no auto-registered virtual children** (no `dashboard-sim-*` equivalent). Inject simulated flow via `data.flow` instead.