Brio-IO Communication Server

Concepts

How Brio-IO is built: channels and connectors, the pipeline, canonical HL7v2 XML, Git as the configuration source — and branches with shadowing and comparison, including today's limit.

Last updated: Examples verified against brio-server fa237ec7ad

This chapter explains the building blocks every other chapter refers to. It describes what runs today — not the plan.

Channel and connector

A channel is a processing path. It has a source connector, a chain of processing steps and one or more destinations:

Source connector

Filter        (per message: process or drop)

Transformer   (rewrite the message)

Destination connector(s)

Connectors are first-class objects, not sub-fields of a channel. They are created separately and referenced by the channel — so the same http-listener can serve several channels, and a connector can be tested and changed independently of the channel.

Every connector has a protocol (mllp-listener, file-reader, http-sender, …) and a set of typed properties. Which protocols exist and which properties they take is in the Connector reference.

The message passes through stages — and all of them stay visible

Brio-IO does not store a message only in its final state, but in stages: RAW (as it arrived), TRANSFORMED (after filter and transformer), SENT (what went out). On top of that come states that usually stay invisible:

  • FILTERED — a message a filter dropped. It shows up as its own row instead of disappearing without trace.
  • ERROR — processing failed; the reason is in the message notes.
  • SHADOW_SENT — the message ran in a shadow branch (see below).

Every message also carries an origin, saying which branch it came from. That is why shadow traffic does not distort the live channel’s counters.

Canonical XML: one message, one shape

As soon as an HL7v2 message arrives, Brio-IO converts it into a canonical XML representation. Scripts work on that shape, not on the raw ER7 string with its pipes and carets.

Two properties of that shape matter, because both correct historical mistakes:

Underscores instead of dots. In the classic Mirth world the field is called PID.5 — and the dot is the property access operator in every programming language, which is why you have to use string brackets there. Brio-IO separates levels with an underscore (PID_5), so element names are ordinary identifiers.

Components numbered positionally. A component is named after its position, not after its HL7 data type:

<PID>
  <PID_3>
    <PID_3_1>12345</PID_3_1>
    <PID_3_4>MRN</PID_3_4>
  </PID_3>
  <PID_5>
    <PID_5_1>Schmidt</PID_5_1>
    <PID_5_2>Hans</PID_5_2>
  </PID_5>
</PID>

PID_5_1 is the family name — field PID-5, component 1. Exactly the numbering HL7 describes and a Mirth practitioner has in their head.

Important for existing scripts: an interim version of Brio-IO named components after their data type (PID_5XPN_1FN_1). Those paths no longer work — they silently return an empty string. When a channel is deployed, Brio-IO warns by name if a script still contains such data-type paths. The reason for a loud warning instead of a silent alias: two simultaneously valid spellings were exactly the problem this rework ended.

Escape sequences (\S\, \T\, …) stay verbatim in the XML. To resolve or produce them in a script, use escapeHl7/unescapeHl7 — see the Transformer handbook.

Three script engines, one set of built-ins

Filters and transformers are scripts. Brio-IO runs three engines in parallel; you choose the engine per script, not per channel:

EngineIdentifierLanguageRole
GroovygroovyApache Groovydefault for new scripts
GraalJSjs-modernmodern JavaScriptfor those who prefer JS
Rhinojs-legacyJavaScript with E4Xonly for imported Mirth scripts

The built-ins are the same in all engines: msg, xml, channelMap, sourceMap, responseMap, globalMap, globalChannelMap, tmp, logger, xslt. Scripts are compiled at deployment, not per message.

Details and examples: Transformer handbook.

Git is the configuration source

A Brio-IO server’s configuration — channels, connectors, scripts, code templates — does not live in a database table but in a Git repository the server maintains itself (embedded JGit; an optional remote is possible).

That is not cosmetic. It is the reason for several properties you would otherwise have to build:

  • Every change has a commit. Who, when, what — without a separate audit feature.
  • Rolling back means rolling back, not “restoring a backup”.
  • Branches are real Git branches — and therefore the basis of the next section.

Where the repository lives is controlled by brio.config.git.local-path; see Operations.

Branches: shadowing and comparison

A configuration change is rarely harmless in integration work: a modified transformer meets real traffic, and classically you only find out afterwards whether it did the right thing. Brio-IO addresses this with branches.

Shadowing. A branch can observe the running channel’s real traffic and execute the pipeline on a live copy — without calling a real destination. The results land as SHADOW_SENT in the message browser, cleanly separated from live traffic by origin.

Comparison. The comparison puts what main and the branch produced side by side, message by message, and reaches a verdict:

VerdictMeaning
IDENTICALthe branch produces the same result as main
DIFFERENTthe results diverge — viewable side by side
NO_PAYLOAD_STOREDthe channel stores no payloads (storage policy MINIMAL), so a content comparison is not possible

The last verdict is deliberate: a channel that stores no payloads for data protection reasons does not get an invented “identical” but the honest statement that there is nothing to compare here.

On merge, Brio-IO writes a summary to audit/branch-summaries/ in the configuration on main — including a bounded number of concrete divergence examples (default: 20). When a branch is deleted, Brio-IO clears both its runtime routes and its data rows.

Today’s limit: no branch deploy

You cannot deploy a branch-scoped channel today. The server rejects it:

HTTP 409 Conflict
Branch deployment is not available while branches share the runtime context.
Validate branches via Shadowing; branch deploy becomes available with isolated
contexts.

In the web UI the deploy switch is disabled in a branch context and explains itself via tooltip. This is not a defect. The reason is concrete: all branches share one runtime context today. A branch-scoped deploy would therefore overwrite the shared route — that is, change the live channel while you believe you are only testing a branch. An honest block beats a function that does something other than what its name promises.

So the validation path is: create a branch → observe shadowing → compare → merge. The isolated per-branch context that would unlock branch deploy is being worked on.

What Brio-IO does not do today

For the same reason branch deploy is blocked, here is a short, honest list:

  • No channel-to-channel dispatch in scripts. There is no router.routeMessage() as in Mirth; the way is a destination on the channel. The Mirth importer warns about exactly this.
  • No LIVE mode for branches (two branches in parallel production).
  • No public download. Brio-IO is in Early Access; access is set up personally.

This page is currently available in German and English. A Spanish version will follow.