Early Access · beta. Help test and shape the 1.0 release. Where the project stands →

Reliability

Review, test, and roll back interface changes

Keep interface logic in your own version-controlled repository. Review each diff and test against the engine before deploying a change. Restore earlier configuration through Git.

MessageFoundry started in May 2026 and is in Early Access. Read the project status →

The usual problem

Where interface logic usually lives

When configuration is edited directly in a running engine, teams need a way to track, review, and test those changes.

Hard to review

Store configuration in version control so reviewers can compare changes and trace their history.

Drifts between environments

Direct edits in production can leave staging and production with different configurations. Keep one reviewed source for both.

Risky to change

Test routes and transforms before deployment. Keep the previous configuration available for rollback.

Config as code

Your interfaces, in a repository you own

Connections, Routers, and Handlers are plain Python in your private Git repository. messagefoundry init creates its initial files. Your team installs the engine as a pinned, signed dependency.

Deploy the same reviewed commit to each environment, with a small values file for environment-specific settings. Keep secrets and PHI out of Git; use environment variables for those values.

  • Every change is a diff — reviewed, attributed, and kept in full history like any other code.
  • Revert the configuration commit or restore the previous pinned engine version to roll back a change.
  • One source of truth — the same modules run unchanged in dev, staging, and prod.
  • Routing and transform code reaches production through a reviewed pipeline. The database does not store that code.
The CI gate

Require checks before merging

messagefoundry init creates a continuous integration (CI) workflow for automated checks. It runs on every pull request and every push to main. The same checks run in the VS Code editor and an optional pre-commit hook.

Validate

Every config module imports and every inbound → router → handler reference resolves. A router named but never defined, a dangling binding, or an import error fails the build.

Dry-run routing

Synthetic messages run through the same routing core used in production, with no store, network, or connectors. The test checks routing logic; it does not exercise the full deployment.

Posture

Checks environment and security settings that could prevent the engine from starting.

Supply-chain verify

Before installation, CI verifies the pinned engine wheel against its SLSA / Sigstore build records. A substituted or relabelled wheel fails the build.

Branch protection

Make the gate a merge requirement on main: no interface change lands unless validate and dry-run are green and reviewed.

One gate, three places

The exact same check runs in the editor as you work, in your pre-commit hook, and in CI on the PR — so problems surface early and consistently.

What it buys you

Check delivery behavior and change history

Reviewed before it ships

Use branches, pull requests, and review for interface changes. CI checks routes with representative traffic before merge.

Reversible by design

Git records each change and its author. Revert a configuration commit or restore the prior pinned engine version when needed.

Deploy the same reviewed code

Promote the same reviewed commit to each environment. Keep environment-specific values separate from interface code.

A single interface measured about 50 messages per second on anonymized, representative traffic, with durable, ordered delivery. Test capacity with your own transforms, storage, hardware, and partner response times. Read the measurements and sizing guidance →

Durable delivery

Stored first, then acknowledged

The sender gets its ACK only after the engine commits the raw message to the message store. Routing, transform, and delivery run after the ACK, each as its own committed step.

A failure after the ACK does not reach the sender. The engine retries a failed send, and reruns a step that a restart interrupted. It dead-letters a row when a Router or a Handler raises, or when the delivery attempts run out. The message then shows as ERROR. In the diagram, a solid arrow is a call and a dashed arrow is a reply. See the staged queue →

Receive to deliver: the ACK comes after the commit A sender sends an HL7 message to the inbound listener. The listener decodes, parses, and validates it. If that fails, the sender gets a NAK and the message is recorded as ERROR. Otherwise the listener commits the message to the ingress stage of the message store, and only then sends the ACK. After the ACK, no later step sends the sender a NAK. The router worker, the transform worker, and the delivery worker each run as their own committed step. If a Router or a Handler raises, the row is dead-lettered and the message ends as ERROR. If a send fails, the delivery worker retries with a backoff, then dead-letters the row. The store finalizer sets PROCESSED once no row is pending or in flight. If the engine stops in the middle of a step, the row returns to pending on restart and the step runs again. Sender Inbound listener Message store HL7 message decode, parse, validate If that fails: NAK (AR or AE), recorded as ERROR commit to ingress stage stored: RECEIVED ACK (AA) The sender has its answer. No later step sends a NAK. Router worker one transaction: ingress row in, a routed row per Handler out If the Router raises: row dead-lettered, message ERROR Transform worker one transaction: routed row in, an outbound row per Send out If the Handler raises: row dead-lettered, message ERROR Delivery worker sends through the outbound Connection, marks the row done If the send fails: retry with backoff, then dead-letter, ERROR Store finalizer PROCESSED once no row is pending or in flight If the engine stops mid-step: the row returns to pending on restart, and the step runs again.
High availability

One primary, one warm standby

Senders connect to a floating virtual IP address or a load balancer, never to a fixed node. Only the primary binds the listener ports, so the health check passes only there.

Both nodes use one shared server database, which holds the leadership lease and the message store. Solid arrows carry messages. Dashed arrows are the health check that fails on the standby, and the lease traffic. Clustering guide (PDF)

High availability: active-passive deployment Senders connect to a floating virtual IP address or a load balancer. Its health check is a TCP connect to the listener port. Node A, the primary, holds the leadership lease, binds every listener, and runs the workers, so the check passes there. Node B, the standby, binds no listeners and runs no workers, so the check fails there and it gets no sender traffic. The primary reads and writes the message store and delivers to downstream receivers. Both nodes use one shared PostgreSQL or SQL Server database. The primary renews the leadership lease, and the standby takes the lease only after it expires. delivers port bound, check passes port not bound, check fails reads, writes the queue renews the lease takes the lease only after it expires Downstream receivers through outbound Connections Senders partner systems, MLLP and TCP Floating VIP or load balancer health check: TCP connect to the listener port ENGINE NODES Node A: primary holds the leadership lease binds every listener runs the workers Node B: standby binds no listeners runs no workers waits for the lease Message store durable staged queue Leadership lease one owner at a time SHARED DATABASE · POSTGRESQL OR SQL SERVER
Test scope

What the checks cover and what you must test

The CI checks confirm that configuration loads, references resolve, and representative traffic routes without errors using production routing logic. A transform can still produce valid but incorrect HL7. The checks alone do not prove byte-for-byte output parity with a downstream system.

Check output parity separately with synthetic messages, the test harness, and known input/output pairs. Reconcile sent and delivered messages before and after cutover.

Adopt interfaces you can review, test, and roll back

Create a configuration repository with automated checks, then send a test message.

FAQ

Frequently asked questions

Is message delivery reliable?

MessageFoundry stores each message durably before acknowledging it, then routes from that store. Stored messages can resume processing after engine restarts and failures.

How does MessageFoundry handle failover?

A standby instance takes over if the primary fails. An application-level leader lease keeps only one instance active at a time. Failover typically completes in about 30 seconds.

Are my interfaces version-controlled and tested?

Interfaces are plain Python in your Git repository. Automated checks test configuration and routing against the engine. Your team reviews changes and checks transform output separately.

Is a Python engine fast enough for production HL7?

A single interface measured about 50 messages per second on anonymized, representative traffic. Actual throughput depends on your transforms, storage, hardware, and partner response times. Measure your combined workload before sizing a deployment.