Operating Pipelines
What an operator sees and does once pipelines are running: the dashboard's pages for them, the actions it offers on units, the engine instances behind it, and the inputs refused before they were stored. For units, statuses and items themselves, see Units and State. For the same actions from a script, see Admin Scripts.
The dashboard
Pipelines and Instances appear in the sidebar once at least one pipeline has created its tables. From then on Dashboard opens on the 2.0 home, Sources shows the pipelines' sources, and the sidebar no longer lists the 1.x message pages. Reference stays listed: one entry for the HL7v2 and C-CDA references, with a switch between the two in the page header. Code mappings stays listed too, and lists the codes the pipelines' units wait on. Units and Unit open from the Pipelines page, the Dashboard and the Sources page.
A page that refreshes gives up on a read that has no answer within 20 seconds, or within two of its refresh intervals when that is longer, and shows it as a read that failed until a later one succeeds.
Dashboard
The 2.0 home covers every pipeline. Its live rates refresh every second, its counts every 10 seconds, and its top error kinds every minute:
| part | what it shows |
|---|---|
| Received, Processed, Sent | one tile each: the units per second that were created, moved on from received, and reached sent, labelled units/s, each averaged over the last 5 seconds. Processed also gives the sender items queued per second, and Sent the items delivered per second, labelled items/s |
| Error rate | the units at error as a percent of all units, of every status, summed over the pipelines that were counted, with a note such as 1 pipeline not counted saying how many were left out (see below) |
| Throughput | the last 60 seconds, one point a second, in four lines labelled inbound, mapped, sent and errors: the three rates of the tiles, and the units that reached error per second |
| Queue depths | the units at received, processed, sent and error now, from the same totals, each as a count and a bar that fills at 50,000 |
| Top error kinds · 24h | the most frequent error_kinds among the units at error created in the last 24 hours, one row per pipeline and kind. A row opens its pipeline's units at error of that kind created in those same 24 hours, so the list matches the count. The row of units with no kind opens all the units at error instead, as no filter selects a missing kind. All pipelines opens the Pipelines page, which gives every pipeline's errors and error kinds side by side |
| System health | Postgres, Aidbox, the engine, naming any stage that is not running or crashed in the last 5 minutes, code mapping coverage, and the workspace |
Each tile also draws its last 60 seconds and gives the change from the first
half of that minute to the second. On Error rate both are of the units that
reached error per second, not of the percent.
-
The rates count units as the engine moves them. Each stage of a pipeline counts a unit once the transaction that moved it commits, and the counts of every engine instance are summed:
- Received: the units ingest created, at
receivedorskipped, and the units a bridge created in the pipeline it feeds; - Processed: the units whose steps closed them out of
received, toprocessed,skippedorsent, or on through a bridge. A unit that fails counts as an error instead. Itsitems/sis the sender items those units queued; - Sent: the units that reached
sent, whether the sender closed them, they had nothing to send, or a bridge passed them on. Itsitems/sis the items the sender delivered; - errors, the chart's fourth line and Error rate's sparkline: the units
that reached
error, from a step or from the sender. A unit counts once however many of its items were rejected.
Nothing is counted for a
notReady(), an item answeredwait, or a failure that is retried, until the retries run out and the unit reacheserror. Nor are an operator's Retry, Defer and item deletion: the API makes those moves, and only the engine counts. - Received: the units ingest created, at
-
The rates keep no history. Each engine holds the last few minutes of its counts in memory and writes them every second. Anything that restarts the pipeline workers — an engine restart, a reload after a new commit, or a worker restarted after a crash — starts them from zero, and the chart fills again over its minute. When the rates cannot be read, the three rate tiles read
—and saylive rates unavailable, and the chart and the sparklines stay empty until the rates can be read again. -
Error rate and the queue depths count every unit, so they cost a full count. Each pipeline's units are counted in full at most once every 15 seconds, so a total can be that old when it is shown. A pipeline's count is given up after 2 seconds and then not tried again for a minute, and all the counts together are given up after half the request's time budget,
INTERBOX_API_READ_TIMEOUT_MS. The rest of the page still loads. Both then add up the pipelines that were counted and say how many were not, such as1 pipeline not counted, and read—only when none was. -
Each pipeline counts its own units. An input a bridge passes on becomes a unit in each pipeline it reaches, so the rates, the error rate and the queue depths count it once per pipeline.
-
When Postgres cannot be reached, System health reads Postgres and the engine down and the mapping coverage unknown.
When no pipeline has created its tables, or the pipeline list cannot be read, Dashboard is the 1.x home.
Pipelines
One row per pipeline. Its run status and its graph refresh every 5 seconds, the other columns every 10 seconds:
| column | what it shows |
|---|---|
| pipeline | its run status, running, degraded or stopped, then its name |
| last 5 min | a graph of the units received per second over the last 5 minutes, with the units that reached error per second drawn over it in the error colour, and the received rate now beside it, such as 2.0/s. Hovering the graph gives the run status and both rates now |
| created in last 24 h, by status | the units created in the last 24 hours, counted by the status each is at now |
| queued | units resting at the store, waiting to be taken or being worked on |
| errors, all time | units at error now, of any age; it exceeds the error count in the 24-hour column when errors older than 24 hours are still waiting for a retry or a defer |
| sender items | items waiting in the sender queue |
| error kinds, last 24 h | the most frequent error_kind among the units at error created in the last 24 hours, with its count and how many other kinds there are; hovering the cell lists the kinds with their counts, up to 50 |
| refused at ingest | inputs refused before the store, with the last reason and its time — see Refused inputs |
- The run status comes from the engine instances' heartbeats. A pipeline
runs as up to three stages,
chain-source:,chain-segment:andchain-sender:followed by its name. A pipeline fed by another through a bridge has no source stage, and one that bridges on into another has no sender stage. Only fresh, licensed instances count — see Engine instances:running: every instance that runs any of the pipeline's stages runs all of them, eachrunning;degraded: an instance has one of its stagesstarting, inbackofforexited, or runs some of its stages but not all;stopped: no instance runs any of its stages, because the engine is stopped, parked after a failed load, or not licensed.
- The graph comes from the engine's live counters, the ones behind the Dashboard's rates, drawn as one point per 5 seconds. A pipeline fed by a bridge counts as received the units the bridge created in it. Like the Dashboard's rates, the graph keeps no history: anything that restarts the pipeline workers — an engine restart, a reload after a new commit, or a worker restarted after a crash — starts them from zero, and the graph fills again over its 5 minutes.
- When the live status cannot be read, the row shows no status and its graph
reads
—until it can be read again, rather than the last one drawn.
Units opens the pipeline's units. Sender opens its sender backlog under the table, refreshed every 5 seconds:
| field | what it counts |
|---|---|
| Items | every item in the queue |
| not tried yet | items no send has tried |
| waiting | items whose last try answered wait, which counts no error, such as a reference not yet at the destination |
| retrying | items whose last try was an error, which counts toward the sender's retry limit |
| Oldest waiting since | when the oldest waiting item was queued |
| Next try at | the earliest next try still to come |
Below them the items are grouped by outcome and kind, such as
wait · sender/reference_missing, so you can see what a backlog waits for.
Units
The units of one pipeline, newest first, 50 at a time. The newest page refreshes every 4 seconds; loading older pages with Load more pauses that until you go Back to live. When a refresh fails, the list keeps the rows it last read and says what time they are from and why the refresh failed, and the field counts do the same, until a refresh succeeds.
- The field list counts the units by
status,error_kind,error_stepand each attr the store declares inindex. Click a value to filter by it, and negate a filter to exclude it. The counts come from the newest 10,000 matching units, and say so when they reach that. - Search appears when the store declares
search: "trgm". It finds units whose search text contains every word typed; each word needs at least three letters or digits. - The time window bounds the units by when they were created.
- The filters, the search, the time window, the bounds of a
created_atororder_atchip and the similar-error filter are kept in the page's address, so opening that address again opens the list the same way. A chip read back from the address is labelled with its bounds. A link from the Dashboard's top error kinds sets thecreated_atbounds to the 24 hours its count covers, with the time window at All. - Click a row to expand it in place:
- its error, if it has one. An error that carries a FHIR
OperationOutcomeshows its diagnostics under the request that failed, with the whole message one click away; - its fields as chips. Click
status,error_kind,error_stepor a declared attr to filter by its value. Thecreated_atandorder_atchips filter to within 1, 5 or 30 minutes of the unit's own time, or to before or after it; - the first 20 lines of its value, loaded when the row opens. JSON is shown indented, and a value over 1 MiB or stored as bytes is shown by its size. When the store declares a search index, clicking a token of an HL7v2 value adds it to the search, or takes it out again, and so does selecting text in it;
- Code mapping on a unit that failed on an unmapped observation code, as on the Unit page.
- its error, if it has one. An error that carries a FHIR
- Similar errors. The
~ error_messagechip of an expanded unit aterrorkeeps the units aterrorwhose message is at least 50% similar to it, as trigram similarity rates it over the first 512 characters of each message. It applies together with every other filter. It looks only among the newest 5,000 units aterrorin the time range, and when there are more, the list says that older matches are not listed. - The row menu offers Retry and Defer when the unit can take them,
Open details, Filter to this patient when the store declares
patient_idinindex, Filter to this sender when it declaressending_facility, and Copy unit id. - Selecting. Click a row's checkbox to select it. Shift-click another to select every row between the two, or drag over the checkboxes to select, or unselect, every row you pass.
- Retry and Defer act on the selected units. Each appears when at least one selected unit can take it. The rows that moved flash, and the result says how many units moved and lists those left unchanged by reason (see Operator actions).
- Ask assistant appears beside Retry and Defer while the assistant is closed.
The selected units go to the assistant as their id, pipeline, status,
error_kindandmessage_typeattr, never their value or error message. - Columns chooses which columns show. Views offers All, Errors and Deferred, and Save keeps the current filters, search, time chips and similar-error filter as a view of your own. Both are kept per pipeline, in your browser only.
Unit
One unit, opened from the Units page or by id, in one inspector: a top bar, the unit's error under it, and tabs.
The top bar names the unit, such as Unit #1042, then gives its status and
type, its patient, MRN, sender and creation time, each when it is known, and
its actions. Its back and close buttons return to the pipeline's Units page:
to the list the unit was opened from, with its filters, search, time window,
time chips and similar-error filter, or to the whole list for a unit opened any
other way.
- Type is an HL7v2 message's MSH-9 type and trigger event, such as
ADT^A01. Any other unit, and an HL7v2 message with an empty MSH-9, takes itsmessage_typeattr, joined to itsevent_typeattr with^when it has one. - Patient is PID-5's given and family name, and sender the first
component of MSH-4, both read from an HL7v2 value's text. MRN is the
first component of PID-3, or else the
patient_idattr. - Code mapping appears on a unit that failed on an unmapped observation code, and opens that code's concept map on the Code mappings page at that code.
- Reprocess runs Retry and Defer runs Defer (see Operator actions), each when the unit's status allows it.
Ask assistant, in the page header, opens the assistant with this unit as its context, shared as on the Units page. Refresh reads the unit again.
A unit that holds an error shows it in a strip under the top bar: at error,
and at deferred, which keeps the error it was deferred at. It names the
error_kind and error_step, and gives the message. The result of Reprocess,
Defer or an item deletion appears in the same strip.
- An Aidbox rejection is shown as its status line and the diagnostics of its issues, each once. raw response opens the whole message as stored.
- An unmapped observation code (
code/unmapped_observation_code) names the code, its display and system, and the concept map. - Any other error is shown as its message.
The tabs, in this order:
| tab | what it shows |
|---|---|
| the value's own | for an HL7v2 message, Raw HL7v2 and Parsed; for a C-CDA document, Document and Raw XML; for JSON, Message. Any other value gets Raw — see below |
| FHIR resources | the FHIR resources the unit's sender items carry, a resourceType in their payload, both the items still waiting and the finished ones: the resources grouped by type in item order, from the first 500 waiting and the first 500 finished items by id. Each says it is waiting to be sent, or how it finished and when. Pick one for its summary and, where its body is kept, its JSON; a payload over 1 MiB gives no JSON. The tab is locked while there are none, and says why |
| Details | the cards below: the unit's fields, its queue state, its sender items and its lineage |
The value's own tabs follow what it holds. A value over 1 MiB gets Raw, whatever it holds.
- An HL7v2 message, text that starts
MSH|. Raw HL7v2 shows its segments, each field with its spec, its table's meaning and the spec warnings; Parsed shows the parsed value with its TypeScript accessor. - A C-CDA document, XML whose root element is
ClinicalDocument. Document shows its document tree, parsed on read; Raw XML the text. - JSON, a
jsonvalue or text holding a JSON object or array. Message shows it pretty-printed. - Anything else gets Raw: the stored value, a
bytesvalue as the start of its base64, and a value over 1 MiB as its size only.
The FHIR resources are read from the unit's sender items, the waiting ones and a record of the finished ones. A waiting item shows its JSON. Once the sender is done with an item, the record of it shows how it finished:
- sent: a summary, its type, id and when it was sent, and no JSON, as the body of a sent resource is not kept. Open it in Aidbox links to the resource in Aidbox, when its id or its key names it and Aidbox's address is one your browser can reach.
- rejected: its JSON as it was, and the reason as an error, its kind and message. An item is rejected by the sender, and this is the only finished item whose body is kept.
- dropped: a summary and no JSON, and the reason as an error, its kind and message. An item is dropped when its unit was rejected or deferred before it was sent. Its body is not kept: the unit's stored value still holds the input it came from, and Reprocess sends the unit again.
- deleted: a summary and no JSON, and a plain note, not an error, that an operator action deleted it — deleting waiting items, Reprocess or Defer — with the reason recorded, if any.
An item listed as waiting that has finished by the time you pick it is shown as finished.
Finished items are kept for INTERBOX_DONE_ITEMS_RETENTION_DAYS days, 7 by
default, and then purged. It takes from one hour, 0.042, to 36500 days, and
0 keeps them for good. A unit at sent or error
with no item left says that no record of its resources is kept any more: they
were purged after the retention period, or it never had any. When the items
cannot be read the tab says so, and when the inspector itself cannot load it
asks you to reload the page.
The cards of the Details tab:
| card | what it shows |
|---|---|
| Unit | pipeline, created, order at, updated, error step and kind, origin unit, correlation, and every attr |
| Queue | while the unit rests at the store, its failed attempts and when it may be taken next; otherwise "Not in the queue" |
| Sender items | the first 100 of the unit's waiting items and their total, each with its key, order_at, tries, next try, the older version of its key it waits behind, and its last error. Open an item for its payload and meta; one that has finished since the list was read shows how and when it finished, its reason, and its payload where it is kept |
| Lineage | the units that share this unit's correlation id, across pipelines: the first 50 by id, and the total |
On a unit at processed, the Sender items card offers Delete selected and
Delete all waiting items.
Sources
A tree of the pipelines' sources, in the order of the Pipelines page, each
followed by its senders, refreshed every 10 seconds. A source's row shows its
kind — mllp or webhook for a pipeline that listens, bridge for one fed by
fromPipeline() — its address or upstream, how many senders its breakdown lists
(see below), and its health:
| health | when |
|---|---|
| up | a fresh instance runs its source stage, or reports a bridge, which has no source stage of its own |
| down | fresh instances report it, but none of them runs it: each is starting, backoff or exited |
| unreported | no fresh instance reports it |
Collapse a source to hide its senders. Selecting a source, or one of its senders, opens the source's detail. With a sender selected, its Status mix and Volume are that sender's own:
| part | what it shows |
|---|---|
| Kind | mllp, webhook or bridge |
| Format | HL7v2 for an MLLP source, MLLP being HL7v2's framing, or for one whose store declares the message_type and event_type attrs hl7v2Parser() sets; otherwise — |
| Listens on / Fed from | the host and port the source listens on, or the pipeline a bridge is fed from |
| Instances | each fresh instance that runs the source, with the state of its source stage (starting, running, backoff, exited). A bridge's instances carry no state |
| Refused at ingest | the same line as on the Pipelines page — see Refused inputs |
| Status mix | the units created in the last 24 hours, counted by the status each is at now, and each status's share of them |
| Volume | of those units: the total, those created in the last minute and the last hour, and those at error. Last received is when the pipeline's newest unit was created, however old |
| Senders | the sender breakdown, below |
The address is the source's configuration with its env() values resolved. A
webhook source takes no host and listens on every interface, shown as [::].
No secret is shown. Units opens the pipeline's units.
The sender breakdown appears for a pipeline whose store declares a btree
index on sending_facility in index; without one there is none. It lists the
50 senders with the most units created in the last 24 hours, busiest first,
and says so when there are more; the sender count in the page header then
reads "top". Each sender has the same counts as the source, and is active
with a unit in the last minute, recent with one in the last hour, and idle
otherwise. Units with no sending_facility count under (not set).
Units on a sender opens the pipeline's units filtered to that sender.
A source's volume or sender breakdown that cannot be counted in time is shown as
not counted, never as zero: its figures read — and say not counted in time,
its tree row reads — snd, and the page header says some senders were not
counted. When the indexes a pipeline's store declares cannot be read in time
either, its breakdown is shown as not counted too, whether or not it declares
one on sending_facility.
Until the volume has loaded, and while its latest read is failing, the detail
shows it the same way rather than as zero or as the last figures read: the
Status mix and the Volume read —, and Senders says the breakdown has not
loaded. A failed read also says not read: the latest read failed, and the
page header says volume unavailable.
Simulate ingest sends messages to an MLLP source the way a sender would, and
shows the ACK each one got. Paste them, or add or drop files; a line starting
MSH| starts a new message, and blank lines are dropped. Up to 100 messages and
4 MiB in all go in one request. They are framed one at a time over one
connection, each waiting up to 10 seconds for its ACK. A message answered AE
or AR shows the ACK's text. When the connection fails or an ACK does not come,
that message shows the reason and the messages after it are not sent.
The connection is made from the API to the address the source's instance reports, never to one given in the request, and only ever over the loopback, so the API must run on the same host as the engine:
| the source is bound to | the API connects to |
|---|---|
a wildcard address: 0.0.0.0, :: (also written ::0 or in full), or an empty host | 127.0.0.1 |
a loopback address: anything in 127.0.0.0/8, or ::1 | that address, as written |
localhost | the first loopback address the name resolves to on the API's host; if it resolves to none, nothing, and the request answers 400 |
| anything else, such as a network address or another host name | nothing: the request answers 400 |
A port outside 1 to 65535, 0 included, also answers 400, and so does a
webhook or bridge source: a webhook's secret never leaves the engine, and a
bridge listens on nothing. A source whose instance does not report it
running answers 409; try again once it runs. A refused request sends
nothing.
Who may simulate follows the login. With it on, that is a signed-in dashboard
session. With INTERBOX_AUTH_ENABLED=false it is anyone who can reach the
dashboard, within the Host allowlist and cross-site rules that stay on — see
Turning it off. A service token cannot
simulate either way. Each API process runs at most four simulations at once,
each ending within about a minute of starting; another answers 429 until one
ends.
The messages are ingested like any other, so they become real units. Each
simulation request that gets past the login and the cross-site checks writes
an audit line to the API's log, whether it was answered, refused, aborted or
failed. It records the session's short reference (none with the login off),
the route, the client's agent and forwardedFor when it sends them, and
under simulation the pipeline, how many messages and bytes, the outcome, the
HTTP status (499 when the client went away) and how many messages were
answered AA, AE, AR or not at all. A refused or failed request adds its
reason. The line never holds a message or an ACK's text:
{"at":"2026-10-01T12:00:00.000Z","audit":"source-simulated",
"route":"POST /api/pipelines/adt-feed/source/test","session":"3f9a1c2e",
"simulation":{"pipeline":"adt-feed","outcome":"exchanged","status":200,
"messages":3,"bytes":1536,"acks":{"AA":2,"AE":1,"AR":0,"none":0}}}
Instances
One row per engine instance seen in the last hour, fresh ones first, refreshed
every 5 seconds: its id, whether it is fresh or stale, its version, whether it
is licensed, when it was last seen and started, its stages by state
(starting, running, backoff, exited), the inputs it refused, and its
reload error. See Engine instances below for what these mean.
Code mappings
The concept maps under src/terminology/concept-maps/ in the workspace, and
the codes the pipelines' units wait on, refreshed every 4 seconds. A unit waits
on a code when a step failed it with domainError("code", "unmapped_observation_code", …) and a message laid out as
unmapped code "<code>" (<display>) · system <system> · map <concept map id>
where (<display>) may be left out and (none) stands for no system, such as
unmapped code "NA" (SODIUM) · system (none) · map hl7v2-lab-hosp-observation-code-loinc.
- The queue lists each code once per concept map, with its display and
how many units wait on it: the units at
errorof that kind whose message names it, from every pipeline, counted together. A code the map already holds is not listed, nor is a message laid out any other way, nor a unit atdeferred.GET /api/unmapped-codesanswers the same list, and adds when the first of those units was created and, aspipeline:<name>origins, how many wait in each pipeline. - Each pipeline is read within 2 seconds, and all of them within the
request's budget,
INTERBOX_API_READ_TIMEOUT_MS. A pipeline whose units take longer, or that is reached once the budget is spent, is left out of that refresh, and the others are still listed. - A sender is named only by the map. The page labels a concept map with its
title, or else with the part of its id between
hl7v2-and the mapping type, sohl7v2-lab-hosp-observation-code-loincreads Lab Hosp. The step builds that id from the sending application and facility, and the page shows neither on its own. - A map no file holds yet reads no file yet where the others show how many codes they map. It is listed only because units wait on its codes, and approving one of them creates its file.
- Approve writes the code's target into the concept map's file, and
creates the file when the map has none yet, with no
useContext: a step finds the map by its id. It then retries, in every pipeline, every unit aterrorwhose message names that map and that code, as Retry does and in runs of 1000, and says how many it re-queued. Each run takes only the units still aterroron that code when it starts, so a unit another retry has moved on is not sent again. Nothing has to be reloaded first: a step'sctx.translatethat misses reads the map files again when they changed, so a retried unit finds the new mapping. - When Approve fails after writing the mapping, for example when the units
cannot be read within the budget, the mapping stays written and the code
leaves the queue, but units not yet retried stay at
error. Find them on the pipeline's Units page byerror_kindcode/unmapped_observation_codeand retry them there. - Approve needs a
worktreeworkspace, the directory the engine bundles in place. Acloneworkspace is reset by the engine on its next reload, so Approve refuses it with409: add the element to the concept map in the workspace repository and push it instead, then retry the units once the engine has reloaded.
Operator actions
Three actions change units: Retry, Defer, and deleting waiting items. Each runs in one short transaction and answers at once.
Retry and Defer
| action | takes a unit at | the unit becomes |
|---|---|---|
| Retry | error, deferred or sent | received, with its error cleared, and a fresh place at the store: no attempts counted, due at once |
| Defer | error | deferred, keeping its error. Nothing happens to it until it is retried |
A Retry runs every step after the store again, with the workspace's current code. The Aidbox sender does not send again a resource it already delivered unchanged, so retrying a unit that partly reached Aidbox sends only what is missing or changed.
- Up to 1000 units per request. The dashboard sends a larger selection in parts of 1000.
- Leftover items are cleared first. A unit the sender rejected may still
have items that another send held at that moment. Both actions delete those
before they change the unit. An item a send still holds stays, and the unit is
left unchanged as
item_in_flight.
Each answers the units it moved, and the units it left unchanged with a reason:
| reason | means |
|---|---|
status | the unit is at a status this action does not take; the answer names it |
busy | another transaction holds the unit, or its place at the store: the engine is working on it, or another action is. Try again |
item_in_flight | a send holds one of the unit's items. Try again once it settles |
not_found | no unit with that id in this pipeline |
Deleting waiting items
A unit at processed stays there until its last item is delivered. An item that
waits counts no error and has no limit, so a unit whose resource waits for a
reference that will never arrive stays processed until someone acts. Deleting
its waiting items closes it.
- Only a unit at
processed. Any other status is refused withunit is not processedand the unit's status. - All, or the ones you name, up to 1000 item ids.
- Items a send holds right now are
kept. A named item already gone, delivered or deleted meanwhile, ismissing. The rest are deleted. - When no item is left, the unit becomes
sent. - A named item of another unit refuses the request, and so does a unit another
transaction holds (
busy).
The answer lists the items deleted, kept and missing, and the unit's
status after it.
They never wait on the engine
The actions do not wait for a lock. A unit the engine is working on is in work,
and an operator does not change it underneath; waiting could also deadlock
against the engine, and would spend the request's time. So a unit another
transaction holds is answered busy at once, and the action can simply be
repeated.
Every statement of an action, and of the reads behind the Dashboard's totals and
error kinds and the Pipelines, Units and Unit pages, runs within one time budget
for the whole request,
INTERBOX_API_READ_TIMEOUT_MS. A request that spends it answers 504, and
changes nothing.
A unit an action moves enters a new status, so a pipeline that subscribes to
received, deferred or sent gets that event, delivered by the engine shortly
after the action — see Webhooks.
Retry and Defer are also open to the dashboard assistant's grant; deleting items is not — see Securing Dashboard Access.
Engine instances
Every engine instance writes a heartbeat row of its own every 2 seconds, keyed
by its id: INTERBOX_INSTANCE_ID, or the host name when that is unset. The row
carries its version, license state, CPU and memory, the state of each of its
stages, its pipelines' sources, the inputs it refused, and its reload error.
- Fresh or stale. An instance seen within the last 6 seconds is fresh. A stale one has stopped, crashed, or cannot reach the database. Rows not seen for an hour are deleted, so an instance that is gone for good, such as one that restarted under a new host name, drops off within the hour.
- One id, one instance. Two live engines with the same id write one row, and
the dashboard shows them as one. When an instance finds its row written by
another live process, it logs one error naming the duplicate
INTERBOX_INSTANCE_ID. Give each engine its own. - Reload error. The instance's current workspace load failure:
{ at, ref, message }, with credentials in URLs removed. It is set when the workspace fails to load, at boot or on a hot reload, and cleared when a load succeeds. A failed hot reload leaves the previous workspace running. A failed boot leaves the engine parked: the dashboard stays up, no stage runs, and the instance keeps beating with no stages. Either way the engine loads the workspace again when a new commit arrives, on Sync now, or onSIGHUP— see The Workspace.
Refused inputs
An input refused before the store left nothing behind: no unit, no stored value. The sending side was told, and retries on its own. Each refusal is counted, so a feed whose every input is refused can be told apart from one that went quiet.
| code | the input | the sending side gets |
|---|---|---|
too_large | was an MLLP frame or a request body over the source's maxBytes | MLLP: the connection is dropped. HTTP: 413 |
store_misfit | produced a row the store's codec cannot hold | MLLP AE, HTTP 500 |
step_error | made a step before the store throw, or answer notReady | MLLP AE, HTTP 500 |
not_ready | made a step before the store read a reference table that is not ready, or throw NotReady | MLLP AE, HTTP 500 |
timeout | passed its deadline, in a step or in a statement | MLLP AE, HTTP 500 |
db_error | was refused by Postgres, or Postgres could not be reached | MLLP AE, HTTP 500 |
- Per pipeline, per instance, since its source started. Each instance keeps its counts in memory and writes them with its heartbeat. A restart starts from zero, and the refusals of the last seconds before a crash are lost.
- The Pipelines page sums the fresh instances, with the reason and time of the latest refusal. The Instances page shows each instance's total.
- Only the code is counted. The refusal itself, with its message, is in the engine's log, naming the pipeline.
- Not counted: an input abandoned before its commit by a stop or by an HTTP client that disconnected, and a request refused for its method or its secret.
Environment
2.0 adds these variables:
| variable | default | what it sets |
|---|---|---|
INTERBOX_DONE_ITEMS_RETENTION_DAYS | 7 | how many days the record of finished sender items is kept, see Unit. Unset or blank uses the default. A plain decimal number of days from one hour, 0.042, to 36500 is taken as given, a fraction included. 0 keeps the record forever and logs a warning saying so. Any other value, such as one under an hour or over 36500, a negative one, an exponent like 1e3, a hex number or text, uses the default and logs a warning |
INTERBOX_API_READ_TIMEOUT_MS | 10000 | the time budget, in milliseconds, of one request behind the Dashboard's totals and error kinds and the Pipelines, Units and Unit pages, and of one action. Past it the request answers 504. A value that is not a whole number above zero uses the default. The dashboard waits 20 seconds for most of these reads, so above that a slow read shows there as failed rather than as 504 |
INTERBOX_INSTANCE_ID | the host name | the instance's id on the Instances page. Give each engine its own |
INTERBOX_STOP_GRACE_MS | see Updating the Engine | how long a stop lets started work finish; The Workspace covers it during a hot reload |