Line assurance builds on the coding printers: every job is recorded with what it was printed for, the printed code can be verified, and the result is shown live and reported per shift.
Batch traceability and lot recall
A printer node carries an optional traceability context (templates over the record): order, batch,
lot, product and code (the code the job prints), plus mask, a list of logical field names stored as
***. A jobs_from entry may carry the same keys itself.
{ "id": "p", "type": "printer", "line": "${line}", "message": "${fmt}",
"fields": { "BATCH": "${batch}", "SERIAL": "${serial}" },
"order": "${order}", "batch": "${batch}", "lot": "${lot}", "product": "${sku}",
"code": "${serial}", "mask": ["CUSTOMER"] }Every row of the job log (printer_jobs.db) then holds order, batch, lot, product, line, printer, message,
field values (masked by the privacy rules and by mask), the code, timestamps, the outcome, what the driver
confirmed and the verification result. Old databases are migrated in place.
GET /api/traceability/jobs?batch=&lot=&order=&product=&line=&printer=&code=&status=&verify=&from=&to=&format=json|csvGET /api/traceability/lotsthe lots (else batches) seen.GET /api/traceability/recall?lot=LOT-9[&line=&from=&to=]&format=json|csv|html: every code printed under the lot, on every printer, where and when, with the failed and unverified jobs.htmlis a printable page (print it to PDF from the browser). Dates areYYYY-MM-DDinassurance.timezoneor RFC 3339.
MCP tools: traceability_jobs, list_lots, lot_recall. Editor: Operations, Traceability.
Verification
Give a printer a verify: option:
printers:
- id: z1
line: L1
protocol: zebra
confirm: true # ~HS label-count confirmation
verify: { method: printer }
- id: mi1
line: L1
protocol: markem_imaje
verify: { method: scan, window_ms: 8000 }method: scan needs the node’s code. After the job a scanner or camera must report that code within
window_ms. method: printer accepts only the driver’s own proof of print.
What each driver can confirm (confirmation in the job log and printer list):
| Driver | Confirmation |
|---|---|
| Zebra | printed with confirm: true (~HS shows no label left on two polls), else sent |
| Markem-Imaje, Domino, Hitachi, Zipher, EOTSUM | acked: every command acknowledged (data accepted, not proof of paper) |
| ASCII | acked with an ack regex, else sent |
| TSC, SATO | sent: the status query is only a precheck |
Scans reach the engine three ways: POST /api/assurance/scan {code, printer?, line?, scanner?}, a verify
flow node at the end of a flow that receives readings (webhook, MQTT, ingest), or a configured scanner that
streams one code per line over TCP or serial:
assurance:
scanners:
- { id: cam1, line: L1, transport: { kind: tcp, host: 10.0.0.9, port: 23 }, strip_prefix: "]d2" }A scan equal to a waiting job’s code verifies it. A different code marks the oldest waiting job on that
printer or line unverified: mismatch. A job with no scan in time becomes unverified: missed. A verdict is
final and alerts once. A line alerts on the first unverified job of a streak and then every
alert_repeat_every (default 10) until a job verifies.
Reject output. Each unverified job sends a record {event: "print_unverified", reason, line, printer_id, job_id, code, scanned_code, order, batch, lot, product, consecutive} into the flow source verify:{line}
(or verify:*). Put an http node or a PLC write behind it to stop the line or divert the product. Without
such a source nothing is sent.
Line board
GET /api/assurance/board[?line=] aggregates per line: each printer’s state (up / fault / down, from jobs and
a status poll every status_poll_secs), fault text, current message / order / batch / lot / product, last
code and its verification, counts of the running shift (else the day), and per line the verification rate,
jobs per hour, waiting read-backs and the unverified streak.
- Editor: Operations, Line board.
- TV on the line: open
/board(all lines) or/board/L1, sign in once. Dark, large type, refreshes every 3 s (?refresh=seconds), keeps the last data on screen and says so when the connection drops. - Apps: an
assurancedata source (viewsboard,jobs,recall,lots,report) and theline_boardcomponent.
Shift and day report
assurance:
timezone: Asia/Jakarta
shifts:
- { name: A, start: "06:00", end: "14:00" }
- { name: N, start: "22:00", end: "06:00" } # crosses midnightGET /api/assurance/report?day=2026-10-30[&shift=A][&line=L1]&format=json|csv|html gives per line and
printer: jobs, failed, verified, unverified, verification rate, downtime and availability. Downtime is the
time a printer was unreachable (down) or reporting a fault (fault); time while the connector was stopped
is not counted. MCP tool line_report; editor: Operations, Line shift report.
Configuration
assurance: keys: enabled, timezone, shifts, status_poll_secs (30, 0 = off), sweep_ms,
alert_repeat_every, scan_grace_ms, scanners. Routes need viewer, except POST /api/assurance/scan
(operator, audited assurance.scan).