> Full topic contract, configuration schema, child-resolution rules, and Port-0 envelope spec for `dashboardAPI`. Source of truth: `src/commands/index.js`, `src/commands/handlers.js`, `src/specificClass.js`, `src/nodeClass.js`, and `dependencies/dashboardapi/dashboardapiConfig.json`.
>
> Pending full node review (2026-05). Content reflects `CONTRACT.md` and current source only.
>
> For an intuitive overview, return to [Home](Home).
---
## Topic contract
The registry lives in `src/commands/index.js`. dashboardAPI has **one** canonical input topic.
The `registerChild` alias logs a one-time deprecation warning on first use. There is **no HTTP endpoint contract** for dashboardAPI as a Node-RED node — it is an input-on-wire only. The outbound HTTP call shape is documented in [Port-0 envelope](#port-0-envelope-data-model) below.
### Payload resolution rules
| Payload shape | Resolved as | Source code |
|:---|:---|:---|
| `{source: {config: {...}}, ...}` | `payload.source` — use directly | `handlers.js``resolveChildSource` line 6 |
| anything else | `null` → throws `'Missing or invalid child node'` | `handlers.js``registerChild` line 30 |
`msg.includeChildren` (default `true`) controls graph-walk depth: `true` walks `extractChildren(rootSource)` and emits one dashboard per discovered child plus the root; `false` emits just the root dashboard.
---
## Data model — Port-0 envelope
<!-- BEGIN AUTOGEN: data-model — populate via wiki-gen tool (TODO) -->
dashboardAPI **has no domain output** — it does not extend `BaseDomain` and does not implement `getOutput()`. Port 0 carries one **HTTP request envelope** per generated dashboard, shaped for a downstream `http request` core node:
Port 1 (InfluxDB telemetry) and Port 2 (registration / control plumbing) are **unused** — dashboardAPI has no measurements and does not register with a parent.
**`msg` propagation:** inbound `msg.*` fields are merged via `{...msg, topic:'create', ...}` spread — caller-supplied correlation / trace fields (e.g. `msg._msgid`, `msg.requestId`) survive the hop.
### Dashboard composition
For each generated dashboard, `buildDashboard({nodeConfig, positionVsParent})` performs:
1.**Template load** — `loadTemplate(softwareType)` from `config/<softwareType>.json` (case-insensitive fallback, `machineGroupControl → machineGroup.json` alias). Missing template → logs `warn` and returns `null` (the dashboard is skipped from the output).
5.**Templating var fill** — `dashboard.templating.list[]` entries named `measurement` and `bucket` are mutated in place:
-`measurement` ← `${softwareType}_${nodeId}` (used as InfluxDB measurement name in panel queries).
-`bucket` ← resolved bucket (see [Bucket resolution](#bucket-resolution) below).
6.**Links append** (root dashboard only, when `includeChildren=true` and `children.length > 0`) — one `{type:'link', title, url:'/d/<uid>/<slug>', keepTime, keepVariables}` entry per direct child.
If `dashboard.templating.list` is not an array or the named variable doesn't exist, the templating step is a no-op (no error).
### Bucket resolution
`bucket` (the InfluxDB bucket templating var) is resolved in priority order:
| Priority | Source | When applied |
|:---:|:---|:---|
| 1 | `config.defaultBucket` (editor field or `INFLUXDB_BUCKET` env) | When set to a non-empty string |
| 2 | `config.bucketMap[positionVsParent]` | When the position has an entry |
> Priorities 1 and 2 read order from `specificClass.js` `buildDashboard`. Verify against the editor's intended semantics during full review — "global override beats per-position map" is the current behaviour. Flagged.
---
## Configuration schema — editor form to config keys
Source of truth: `dependencies/dashboardapi/dashboardapiConfig.json` + `src/nodeClass.js``_buildConfig`. The runtime config slice is built by `configManager.buildConfig(name, uiConfig, nodeId, overrides)`.
### General (`config.general`)
| Form field | Config key | Default | Notes |
|:---|:---|:---|:---|
| Name | `general.name` | `'dashboardapi'` | Display label; falls through to nodeId in `meta.title`. |
| InfluxDB Bucket | `defaultBucket` | `''` → falls back to `process.env.INFLUXDB_BUCKET` → position default | Set in `_buildConfig`; consumed by `buildDashboard` templating fill. |
| (no editor field) | `bucketMap` | `{}` | Programmatic only — pass via `uiConfig.bucketMap` or future editor field. |
### Editor menu / logger fields
The `dashboardapi.html` template invokes `window.EVOLV.nodes.dashboardapi.loggerMenu.initEditor / saveEditor` via the shared `MenuManager`-served `/dashboardapi/menu.js` endpoint. The logger fields (`enableLog`, `logLevel`) are persisted on the node via the standard EVOLV editor menu pattern.
> [!WARNING]
> **Editor `defaults` use legacy field names.** `dashboardapi.html` declares `{enableLog, logLevel}` as Node-RED defaults but the runtime config reads `general.logging.{enabled, logLevel}`. The bridge is the shared logger menu (`MenuManager`) — confirm during full review that the editor menu correctly maps `enableLog` → `general.logging.enabled`.
If none of the candidates exist in `config/`, the logger emits `No dashboard template found for softwareType=<st>` at `warn` level and `loadTemplate` returns `null`. `buildDashboard` then logs `Skipping dashboard generation: no template for softwareType=<st>` and returns `null`; `generateDashboardsForGraph` skips that node and continues with the rest of the graph walk.
Adding support for a new EVOLV node type = drop a `config/<newType>.json` file matching the `softwareType` lowercase name (or add an alias arm to `_templateFileForSoftwareType`).
---
## Child resolution (NOT a registry)
dashboardAPI does **not** maintain a child registry of its own. There is no `_registeredChildren` map, no `child.register` → `child.unregister` lifecycle, no parent → child emitter wiring. Every inbound `child.register` is a **one-shot** dashboard generation:
walk --> emit["emit one msg per dashboard<br/>topic='create'"]
emit --> http[(downstream<br/>http request node)]
classDef neutral fill:#dddddd,color:#000
classDef other fill:#ffffff,stroke:#666
```
### What graph walk reads from the child source
`extractChildren(rootSource)` reads `rootSource.childRegistrationUtils.registeredChildren` (a Map). For each `entry`:
-`entry.child` — the child source object (must have `.config`).
-`entry.position` (or `child.positionVsParent`) — used for the bucket fallback and tag composition.
Children without a `.config` are silently skipped. If `rootSource.childRegistrationUtils` is absent or `registeredChildren.values` is not a function, the result is an empty array — just the root dashboard is emitted.
| Inbound softwareType | Filter | Side effect |
|:---|:---|:---|
| any | child has `functionality.softwareType` AND the matching `config/*.json` exists | Loads template; emits one upsert msg per dashboard in the walk. |
| any | child has `functionality.softwareType` but the template is missing | Warns and skips that node's dashboard. No error thrown. Graph walk continues. |