@cosyte/pathways
v0.0.4
Published
A code-first healthcare integration engine for Node.js: channels in YAML, transforms in TypeScript.
Maintainers
Readme
Pathways
A code-first healthcare integration engine for Node.js: channels in YAML, transforms in TypeScript.
A code-first healthcare integration engine for Node.js. Channels are YAML files in git, transforms are TypeScript functions, and both are tested like any other code.
Pathways is published on npm as @cosyte/pathways,
and a signed container image of each release is at ghcr.io/cosyte/pathways. It is early: the
version is 0.0.x, and a release may still change a key or an API, which its changelog says.
Contents
- Install
- Quickstart
- Why this exists
- What the latest release ships
- Status
- Build from source
- Layout
- Documentation
- Support
- Security
- Contributing
- License
Install
Pathways needs Node.js 24 or later. Start a new project with the starter, which lists the package in
its own package.json:
npx @cosyte/pathways init my-project
cd my-project
npm installOr add it to a project you have, as a development dependency so the project pins its version:
npm install --save-dev @cosyte/pathways
npx pathways --versionSQLite works with no setup. For PostgreSQL, MySQL or MariaDB, or SQL Server, install that database's driver in the project: Install Pathways lists them.
Quickstart
The starter holds one channel, adt-to-lab: it reads HL7 v2 files from data/adt-to-lab/in, keeps
only admissions (ADT^A01), sets the receiving application to LAB, and writes them to
data/adt-to-lab/out. Its two test messages are synthetic.
In the project init wrote, run the channel's tests, then start the engine:
npm test
npm startnpm test runs pathways test, which sends each test message through the transform and compares
the output with the .expected file beside it. npm start runs the channel until you stop it. In a
second terminal, in the same folder, drop the test messages into the channel's inbox:
mkdir -p data/adt-to-lab/in
cp channels/adt-to-lab/tests/a01.hl7 channels/adt-to-lab/tests/a08.hl7 data/adt-to-lab/in/Within a few seconds the admission is delivered to data/adt-to-lab/out/1-1.hl7 and the update is
filtered out. Stop the engine with Ctrl+C, and npx pathways status counts one delivered. The
quickstart walks through
each step and what the engine logs.
For the common shapes, start from an example in
examples/: a small project that runs on
synthetic messages, with its tests, which CI runs on every change. Each has a page:
- HL7 v2 over MLLP in, HTTP out
- A file drop to SFTP
- HL7 v2 to FHIR
- Filter, then send to two destinations
- Replay after a failure
- A veterinary lab result to FHIR
- A veterinary lab result as v2 XML over HTTPS
Why this exists
Pathways is for a developer who writes and runs integrations: someone who would rather review a channel in a pull request and test a transform with sample messages than configure both in a product. So a channel is a file in git, a transform is TypeScript, and both go through review and tests like the rest of a project's code. Pathways runs from the command line or in Docker, and more than one engine can share a database through the engine lease: one is active and the rest wait as standbys.
It is not a web UI, not a drop-in replacement for another engine (it does not import channels from one), and not a sandbox: a transform is your own trusted code.
What the latest release ships
What follows is in 0.0.4, the version on npm:
- Channels in git. A channel is a YAML file that wires a source to its destinations. Its transform is a TypeScript file that receives the parsed message and returns what to send.
- Formats. HL7 v2, FHIR R4 JSON, X12, and C-CDA arrive parsed, through Cosyte's open-source
@cosyte/*libraries. CSV and fixed-width text arrive as a table. JSON arrives as a value and anything else as text. - Connectors. MLLP, HTTP, TCP with your own framing, and files in a folder, on an SFTP server, or on Amazon S3 and Azure Blob Storage, in and out, and an SMTP destination. Over TLS by default, with a bearer token or mutual TLS on every endpoint that listens, and limits on MLLP connections and message sizes.
- Nothing lost. Every message is stored before it is acknowledged. When a destination is down, Pathways retries and keeps that destination's messages in order. A failed message can be replayed once the transform is fixed, and every failed message in a scope at once, counted first and resumable.
- Storage. SQLite with no setup, or PostgreSQL, MySQL or MariaDB, and SQL Server, with every stored message encrypted at rest, separate retention clocks, and sealed archives.
- Tests.
pathways testruns synthetic messages through a channel and compares the output with saved files, andpathways validatechecks a project's configuration without its secrets. - Operations. An audit log of every operator action; commands to inspect, replay and clear messages, and to pause, stop and reload one channel of a running engine; a bounded drain when the engine is told to stop; a health endpoint with liveness and readiness probes; and a signed Docker image.
- Yours to extend. Your own sources, destinations, hooks, storage and key providers, each a TypeScript module named in the YAML.
- Security controls. TLS in transit, encryption at rest, an audit log of operator actions, and no message contents in logs or errors. Compliance is a property of how an organization runs software, so we describe the controls Pathways provides rather than claim compliance for it.
- Batch files and bodies. A file or HTTP source with
batch:cuts one file or request body into many messages (split: hl7,linesordelimiter), each stored on its own as exactly the bytes it held. An HTTP batch takes anIdempotency-Keyheader, so a retry stores no piece twice. - Channel to channel. A
channel:destination, orctx.routein a transform, hands a message to another channel of the same project through the store, and the message names its parent. Start refuses a loop of channel destinations, and a hop limit catches one made throughctx.route. - A transform and a filter per destination. A destination takes its own
transform:,name:andformat:, and one whose transform returns nothing for a message isfiltered. Each delivery now records the destination it was made for, so a channel with two unnamed destinations of one kind and target is refused until each has aname:, and a delivery queued before this release is marked failed withdelivery: destination-unknown, its message ready to replay. - Start and stop scripts, and a settled event. A transform module may export
onStartandonStop, a hook gets onesettledevent per message once it is delivered, filtered or failed for good, and the audit log records each engine start and stop. A hook that switches on every lifecycle event or audit action adds cases for the new ones. - Metrics. The health endpoint serves
GET /metricsin the OpenMetrics text format, behind its bearer token or mutual TLS, with labels that never carry a message's content./metricsused to answer with the health body, so a monitor that read the body there now reads it from another path, such as/. - HTTP destination options. An HTTP destination takes a
method:, aquery:and URL placeholders that name a message metadata key, and can send anidempotencyHeader:. Message metadata is still empty in this release, so a URL with a placeholder fails each delivery until it can be set. It checks every address it connects to, so one whose host is or resolves to a loopback, link-local or private address now needsallowNetworks:naming the network; until then its deliveries wait withADDRESS_REFUSED. - An XML format. A channel with
format: xmlhands its transform any UTF-8 XML body as a DOM document, read by@cosyte/ccda's hardened reader under size and shape caps, and a document the transform leaves untouched goes out byte for byte. - HL7 v2 XML acknowledgements. With
ack: xml, an HTTP destination settles each delivery by the HL7 v2 XML acknowledgement in the answer:AAorCAis delivered,AEorCEretried, andARorCRfailed. A new example and how-to show a veterinary lab result sent as v2 XML over HTTPS with a client certificate. - One HL7 v2 message per frame or body. On an
hl7v2channel, a frame or body holding a secondMSH,FHSorBHSheader, in any letter case, behind bytes that are not printable ASCII, or joined to the first with no line break, is now refused withMULTIPLE_MESSAGESon every source, and so is a field holding a raw header (MSH|^~\&|) that is not escaped. A message with more than one PID, where the structure HL7 publishes for its type allows one, now fails withMULTIPLE_PATIENTS, and an HTTP source withoutresponse: ackanswers a refused body with 422 where it answered 202. - Declared character sets in HL7 v2. An
hl7v2message a transform returns is written in the character set its MSH-18 declares, where it was always written as UTF-8, so a Latin-1 message nothing changed leaves byte for byte. When a sender leaves MSH-18 blank and sends bytes that are not ASCII, a transform that edits a segment holding one now fails that output withUNENCODABLE_CHARACTER: have the sender declare MSH-18, or set the channel'shl7v2: { assumeCharset }. - One message per idempotency key. When two hand-ins of one message under one idempotency key arrive at the same moment, on two connections or two nodes, a resumed one finds the first and nothing new is stored on SQLite, PostgreSQL, MySQL, MariaDB and SQL Server, through a claim that a new schema step adds when the engine starts.
- Docker Compose.
pathways initnow also writescompose.yaml, which runs the project's channels on the published image beside PostgreSQL, and a.env.examplethat lists the variables Pathways reads by a fixed name. A new page walks through the same files inexamples/docker-compose/. - Examples. Seven example projects in
examples/, each running on synthetic messages with its tests and a docs page, and CI runs each on every change. The package description on npm now says only what Pathways is, and the README that more than one engine can share a database.
Status
Early, and released: 0.0.4 is on npm. Each release is published with provenance from this repository's release workflow, with its signed image. Watch or star the repository to follow along.
Pathways is an open engine, MIT licensed and free to self-host. We plan to offer hosted, managed Pathways as well. Need an integration built, or hosted and run for you? See our integration services.
Build from source
To work on Pathways itself, or to run what is on main before it is released, build it from a
clone. It needs Node.js 24 or later and pnpm at the version packageManager in package.json
names:
git clone https://github.com/cosyte/pathways.git
cd pathways
pnpm install --frozen-lockfile
pnpm build
node dist/bin/pathways.mjs --versionnode dist/bin/pathways.mjs then stands in for pathways in every command the documentation shows.
AGENTS.md lists the checks to run before a pull request.
Layout
src/: the engine, its connectors, storage, encryption and thepathwayscommand.test/: the test suites.templates/starter/: the starter projectpathways initwrites.examples/: example projects, each with its tests and a page indocs-content/.examples/docker-compose/: the engine under Docker Compose, beside PostgreSQL.docs-content/: the documentation pages.bench/: the benchmark suites.scripts/: the repository's own scripts.Dockerfile: the container image.
Documentation
The pages live in this repository's
docs-content/. Start with these:
- Overview: what Pathways does, who it is for and what it is not.
- Write a channel: a channel from nothing, with its tests.
- How delivery and errors work, before you run Pathways for real.
- The security model: what Pathways does to protect the data it moves and stores, and what stays with you.
- Run Pathways with Docker Compose:
the engine beside PostgreSQL, from
examples/docker-compose/, with a synthetic message sent through. - Testing and quality and Performance.
Support
Questions, bug reports and feature requests go on the issue tracker.
Security
Report a vulnerability privately, as the security policy describes, never in a public issue.
PHI and safety
Pathways moves messages that carry patient data between clinical systems. Logs, errors and the health body carry ids, counts and codes, never a message's content. A transform is your own trusted code: its worker thread protects the engine from a transform that hangs, throws or runs out of memory, not from one written to do harm. The security model says what Pathways does and what stays with the organization that runs it.
Never attach a real patient's message, a log from a live system or anything else that holds patient
data to an issue: a synthetic message that shows the problem is enough, and
@cosyte/synth makes one.
Contributing
Pull requests are welcome. For anything that changes how a message is stored, routed or delivered, or
moves the public API, open an issue first so the behavior can be agreed before it is written. Every
fixture is synthetic, a change in behavior comes with a test that fails without it, and a change to
what a reader sees updates the pages in docs-content/ in the same pull request. AGENTS.md lists
the checks a pull request must pass, and
Testing and quality
says the floor they hold a change to. The organization's
contributing guide and
code of conduct apply here.
License
MIT, Copyright (c) 2026 Cosyte LLC. See LICENSE.
