Outputs and pipelines
What run(ctx) returns and how dataserviceOutput is configured determine the stored node output and what downstream nodes can consume.
Runtime envelope (always computed)
Every run produces an arbex envelope in memory:
| Field | Description |
|---|---|
output | Your return value (or error object) |
console | Captured log lines |
status | SUCCESS or ERROR |
metrics.executionTimeMs | Wall time |
configurationSnapshotBase64 | Node config at run time (for auditing script versions) |
profiler | Optional CPU/heap samples (see system-arbex-js) |
What gets persisted depends on dataserviceOutput.persistence.
dataserviceOutput configuration
| Property | Default | Effect |
|---|---|---|
enabled | false | Validate returns that contain $heat-dataservice |
persistence | arbex-envelope | Stored file shape (table below) |
dashboardUsers | [] | Applied when the script omits dashboard_users |
layoutOutput.enabled | false | Validate suggestedLayoutConfiguration when present |
layoutOutput.require | false | Fail if layout required but missing (needs layoutOutput.enabled) |
Persistence modes
persistence | Stored artifact | When to use |
|---|---|---|
arbex-envelope | Full envelope JSON | Debugging, custom parsers of output / console / profiler |
dataservice-root | { $heat-dataservice, dashboard_users, suggestedLayoutConfiguration?, $heat-arbex-run } | Parent for heat-system-next-dimension and Next direct ingest |
dataservice-in-envelope | Envelope with output replaced by validated dataservice root | Tools that need envelope metadata plus canonical data in output |
When enabled is true and persistence is dataservice-root, the script must return an object with $heat-dataservice (use heat.dataservice.buildRoot()).
With dataservice-root, sibling $heat-arbex-run retains envelope fields that would otherwise be dropped.
v2 Next pipeline (typical)
upstream → system-arbex-js → heat-system-next-dimension → v2 API / ui/dashboardArbex node:
{
"script": "... async function run(ctx) { ... }",
"parent": { "nodeInstanceName": "upstream", "mode": "latest" },
"dataserviceOutput": {
"enabled": true,
"persistence": "dataservice-root"
}
}Dimension node (heat-system-next-dimension):
- Reads parent
$heat-dataservice - Merges realms into layout
- Persists dimension + storage for the dashboard SPA
With layout from arbex:
{
"layoutFromParentOutput": true,
"layoutConfiguration": { "version": "1.0.0", "realms": ["default"], "components": { "rows": [] } }
}Arbex returns buildRoot({ suggestedLayoutConfiguration: layoutBuilder }) with layoutOutput.enabled (and require when layout is mandatory).
Shipped example: preset sample-arbex-composable-dashboard (sample-input → sample-arbex → sample-dashboard).
Arbitrary JSON (no dataservice)
With dataserviceOutput.enabled: false, return any JSON shape. Downstream nodes must understand that contract. The Next dashboard will not ingest it automatically.
Production layout guidance
| Pattern | Recommendation |
|---|---|
| Stable production dashboards | Keep layoutConfiguration on heat-system-next-dimension |
| Script iteration | Use suggestedLayoutConfiguration + layoutFromParentOutput |
| Channel ids | Keep ids stable; layout only references ids, not inline data |
Legacy v1 dashboards
v1 facade blobs ({ Map: { ... } }, mount ids, combined charts) target heat-system-legacy-dimension and ui/legacy, not $heat-dataservice. Set dataserviceOutput.enabled: false and use types in heat-arbex-v1-dashboard.d.ts. Authoring notes live in the monorepo under tools/arbex/rag/12-v1-dashboard-payload-authoring.md.