-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patharchitecture.html
More file actions
469 lines (430 loc) · 33.2 KB
/
Copy patharchitecture.html
File metadata and controls
469 lines (430 loc) · 33.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Architecture — MessageFoundry</title>
<meta name="description" content="How MessageFoundry fits together — the system topology, the durable staged-queue message flow with ACK-on-receipt, and the Connection/Router/Handler wiring graph.">
<link rel="canonical" href="https://messagefoundry.org/architecture.html">
<link rel="icon" href="/assets/img/favicon.svg" type="image/svg+xml">
<meta name="theme-color" content="#0b1220">
<meta property="og:type" content="website">
<meta property="og:site_name" content="MessageFoundry">
<meta property="og:title" content="Architecture — MessageFoundry">
<meta property="og:description" content="System topology, runtime message flow through the durable staged queue, and the config wiring graph — how the open-source healthcare interface engine fits together.">
<meta property="og:url" content="https://messagefoundry.org/architecture.html">
<meta property="og:image" content="https://messagefoundry.org/assets/img/og.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:type" content="image/png">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://messagefoundry.org/assets/img/og.png">
<link rel="stylesheet" href="/assets/css/styles.css">
<style>
/* Architecture diagrams — static SVG styled with the design tokens (no JS). */
svg.dg { width: 100%; height: auto; display: block; margin: 0 auto; max-width: 940px; }
svg.dg text { font-family: var(--font); }
.dg-box { fill: #fff; stroke: #cdd6e3; stroke-width: 1.2; }
.dg-api { fill: #fff8ee; stroke: var(--accent); stroke-width: 1.7; }
.dg-store { fill: #fff4e3; stroke: #fde6c2; stroke-width: 1.3; }
.dg-bound { fill: none; stroke: var(--border-dk); stroke-width: 1.2; }
.dg-ttl { fill: var(--text); font-weight: 700; font-size: 14px; }
.dg-sub { fill: var(--muted); font-size: 11px; }
.dg-bnd { fill: var(--muted); font-weight: 600; font-size: 10.5px; letter-spacing: .05em; }
.dg-lg { fill: var(--muted); font-size: 11px; }
.dg-solid { fill: none; stroke: var(--muted); stroke-width: 1.7; }
.dg-dash { fill: none; stroke: #93a1b8; stroke-width: 1.7; stroke-dasharray: 4 4; }
</style>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"@id": "https://messagefoundry.org/#software",
"name": "MessageFoundry",
"applicationCategory": "Healthcare interface engine",
"applicationSubCategory": "HL7 interface engine",
"operatingSystem": "Windows, Linux, macOS",
"url": "https://messagefoundry.org/",
"downloadUrl": "https://pypi.org/project/messagefoundry/",
"license": "https://www.gnu.org/licenses/agpl-3.0.html",
"isAccessibleForFree": true,
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" },
"publisher": { "@id": "https://messagefoundry.org/#org" }
}
</script>
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<div class="eabar" role="region" aria-label="Project status">
<div class="container">
<p><strong>Early Access · beta.</strong> We're building toward 1.0 — and we want people involved now, while it can still be shaped. <a href="/status.html">Where the project stands →</a></p>
</div>
</div>
<header class="site-header">
<div class="container header-inner">
<a class="brand" href="/" aria-label="MessageFoundry home">
<img class="brand-mark" src="/assets/img/logo.svg" alt="" width="28" height="28">
<span class="brand-name">Message<span class="brand-accent">Foundry</span><span class="brand-tm">™</span></span>
</a>
<button class="nav-toggle" aria-expanded="false" aria-controls="site-nav" aria-label="Toggle navigation"><span></span><span></span><span></span></button>
<nav class="site-nav" id="site-nav">
<div class="nav-group">
<button type="button" class="nav-group__btn" aria-expanded="false" aria-haspopup="true" aria-controls="nav-product">Product</button>
<div class="nav-group__menu" id="nav-product">
<a href="/overview.html">Overview</a>
<a href="/features.html">Features</a>
<a href="/features-table.html">Feature list</a>
<a href="/architecture.html" class="active" aria-current="page">Architecture</a>
<a href="/reliability.html">Reliability (CI)</a>
<a href="/tools.html">Tools</a>
<a href="/editor.html">Editor</a>
<a href="/console.html">Console</a>
<a href="/ai.html">AI assist</a>
</div>
</div>
<a href="/security.html">Security</a>
<a href="/comparison.html">Comparison</a>
<div class="nav-group">
<button type="button" class="nav-group__btn" aria-expanded="false" aria-haspopup="true" aria-controls="nav-getstarted">Get started</button>
<div class="nav-group__menu" id="nav-getstarted">
<a href="/getting-started.html">Quickstart</a>
<a href="/guides/">Guides</a>
<a href="/documents.html">Documents</a>
</div>
</div>
<div class="nav-group">
<button type="button" class="nav-group__btn" aria-expanded="false" aria-haspopup="true" aria-controls="nav-company">About Us</button>
<div class="nav-group__menu" id="nav-company">
<a href="/about.html">About</a>
<a href="/status.html">Project status</a>
<a href="/licensing.html">Licensing</a>
<a href="/contact.html">Contact</a>
<a href="/sponsor.html">Sponsor</a>
</div>
</div>
<a class="btn btn-ghost" href="/pypi.html">PyPI<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3l8 4.5l0 9l-8 4.5l-8 -4.5l0 -9l8 -4.5"/><path d="M12 12l8 -4.5"/><path d="M12 12l0 9"/><path d="M12 12l-8 -4.5"/><path d="M16 5.25l-8 4.5"/></svg></a>
<a class="btn btn-ghost" href="https://github.com/MEFORORG/MessageFoundry" target="_blank" rel="noopener">GitHub ↗</a>
</nav>
</div>
</header>
<main id="main">
<!-- shared arrow markers for the diagrams -->
<svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false">
<defs>
<marker id="dg-ah" markerWidth="10" markerHeight="10" refX="7.5" refY="4.5" orient="auto"><path d="M0,0 L9,4.5 L0,9 Z" style="fill:var(--muted)"/></marker>
<marker id="dg-ahd" markerWidth="10" markerHeight="10" refX="7.5" refY="4.5" orient="auto"><path d="M0,0 L9,4.5 L0,9 Z" style="fill:var(--muted-dk)"/></marker>
</defs>
</svg>
<!-- hero -->
<section class="section section--dark section--tight">
<div class="container center">
<span class="eyebrow">Architecture</span>
<h1 style="max-width:22ch;margin-inline:auto">How Message<span class="wordmark-accent">Foundry</span> fits together</h1>
<p class="lead">Four views, each answering a different question: every shipped component and how it relates to the engine; the engine's internal packages and where the process boundaries sit; how a received message moves through the durable staged queue and earns a disposition; and how Connections, Routers, and Handlers wire together by name. The prose source of truth is the engine's <a href="https://github.com/MEFORORG/MessageFoundry/blob/main/docs/ARCHITECTURE.md" target="_blank" rel="noopener" style="color:var(--accent)">ARCHITECTURE.md</a>.</p>
<div class="btn-row center" style="margin-top:24px">
<a class="btn btn-primary" href="/assets/docs/MessageFoundry-Mental-Model.pdf" target="_blank" rel="noopener">Read the mental model (PDF) ↓</a>
</div>
<p style="margin:26px auto 0;max-width:760px;color:var(--muted-dk);font-size:.95rem">This architecture is real and running — but the project is young. MessageFoundry started in <b>May 2026</b> and is in Early Access. <a href="/status.html" style="color:var(--accent)">See where it stands →</a></p>
</div>
</section>
<!-- 1. top-level components -->
<section class="section section--alt">
<div class="container">
<div class="center" style="margin-bottom:30px;max-width:760px">
<span class="eyebrow">1 · The whole system</span>
<h2>Top-level components</h2>
<p class="lead center">Beyond the engine itself, MessageFoundry ships as a set of <b>independent, separately-built components</b> — operator tools, the version-controlled config, the CLI and Windows service that run it, dev/test tooling, a standalone migration tee relay, and the CI-to-PyPI release path. Operator tools reach the engine <b>only</b> through the localhost API; the engine is the core, and everything else sits around it.</p>
<p style="margin-top:14px"><a href="/tools.html" style="color:var(--accent-ink);font-weight:650">See the whole toolkit, up close →</a></p>
</div>
<svg class="dg" viewBox="0 0 940 460" role="img" aria-labelledby="t0 d0">
<title id="t0">Top-level components — the whole system</title>
<desc id="d0">A central headless engine, reached only through a localhost API, surrounded by independent components: operator tools (admin console, VS Code extension), version-controlled author-time config, the CLI and Windows service that run it, dev and test tooling, live HL7 traffic, a standalone migration tee relay, and the CI-to-PyPI release path.</desc>
<path class="dg-solid" d="M490,76 L490,106" marker-end="url(#dg-ah)"/>
<path class="dg-solid" d="M490,158 L490,186" marker-end="url(#dg-ah)"/>
<path class="dg-solid" d="M490,260 L490,291" marker-end="url(#dg-ah)"/>
<path class="dg-solid" d="M252,176 L394,206" marker-end="url(#dg-ah)"/>
<path class="dg-solid" d="M252,262 L394,240" marker-end="url(#dg-ah)"/>
<path class="dg-dash" d="M584,212 L688,180" marker-end="url(#dg-ahd)"/>
<path class="dg-dash" d="M338,378 L412,262" marker-end="url(#dg-ahd)"/>
<text class="dg-sub" x="502" y="96">HTTP / WS</text>
<text class="dg-sub" x="298" y="180">loads config</text>
<text class="dg-sub" x="298" y="250">runs · MLLP</text>
<text class="dg-sub" x="598" y="190">MLLP / file</text>
<text class="dg-sub" x="356" y="330" text-anchor="end">shadow</text>
<rect class="dg-box" x="396" y="30" width="188" height="46" rx="10"/>
<text class="dg-ttl" x="490" y="50" text-anchor="middle">Operator tools</text>
<text class="dg-sub" x="490" y="66" text-anchor="middle">Admin console · VS Code extension</text>
<rect class="dg-api" x="396" y="108" width="188" height="50" rx="10"/>
<text class="dg-ttl" x="490" y="128" text-anchor="middle">API — 127.0.0.1</text>
<text class="dg-sub" x="490" y="145" text-anchor="middle">auth · RBAC · only external surface</text>
<rect class="dg-box" x="396" y="186" width="188" height="74" rx="10"/>
<text class="dg-ttl" x="490" y="208" text-anchor="middle">Engine — headless asyncio</text>
<text class="dg-sub" x="490" y="225" text-anchor="middle">pipeline · transports · parsing</text>
<text class="dg-sub" x="490" y="240" text-anchor="middle">store · config · auth</text>
<rect class="dg-store" x="396" y="291" width="188" height="48" rx="10"/>
<text class="dg-ttl" x="490" y="312" text-anchor="middle">Store</text>
<text class="dg-sub" x="490" y="328" text-anchor="middle">SQLite · PostgreSQL · SQL Server</text>
<rect class="dg-box" x="40" y="148" width="212" height="60" rx="10"/>
<text class="dg-ttl" x="146" y="168" text-anchor="middle">Author-time config</text>
<text class="dg-sub" x="146" y="184" text-anchor="middle">Connections · Routers · Handlers</text>
<text class="dg-sub" x="146" y="199" text-anchor="middle">+ environments · version-controlled</text>
<rect class="dg-box" x="40" y="236" width="212" height="60" rx="10"/>
<text class="dg-ttl" x="146" y="256" text-anchor="middle">Run it · dev / test</text>
<text class="dg-sub" x="146" y="272" text-anchor="middle">Windows service · CLI</text>
<text class="dg-sub" x="146" y="287" text-anchor="middle">generators · test harness</text>
<rect class="dg-box" x="688" y="148" width="212" height="60" rx="10"/>
<text class="dg-ttl" x="794" y="168" text-anchor="middle">Live HL7 traffic</text>
<text class="dg-sub" x="794" y="184" text-anchor="middle">Upstream senders</text>
<text class="dg-sub" x="794" y="199" text-anchor="middle">↕ downstream receivers</text>
<rect class="dg-box" x="688" y="236" width="212" height="60" rx="10"/>
<text class="dg-ttl" x="794" y="256" text-anchor="middle">Build / release</text>
<text class="dg-sub" x="794" y="272" text-anchor="middle">CI: tests · SAST · SBOM · sign</text>
<text class="dg-sub" x="794" y="287" text-anchor="middle">→ PyPI · Trusted Publishing</text>
<rect class="dg-box" x="300" y="378" width="340" height="60" rx="10"/>
<text class="dg-ttl" x="470" y="398" text-anchor="middle">Migration — standalone tee relay</text>
<text class="dg-sub" x="470" y="414" text-anchor="middle">parallel-run parity vs a legacy engine · own SQLite</text>
<text class="dg-sub" x="470" y="429" text-anchor="middle">egress-suppressed shadow — imports no engine code</text>
</svg>
</div>
</section>
<!-- 2. system topology -->
<section class="section">
<div class="container">
<div class="center" style="margin-bottom:30px;max-width:760px">
<span class="eyebrow">2 · Components & boundaries</span>
<h2>System topology</h2>
<p class="lead center">The engine is a headless service; clients are separate processes that reach it <b>only</b> through the localhost API. Dependencies point one way — the API depends on the engine, the engine never imports the API or a client.</p>
</div>
<svg class="dg" viewBox="0 16 800 510" role="img" aria-labelledby="t1 d1">
<title id="t1">System topology</title>
<desc id="d1">The admin console, VS Code extension, and test harness reach the engine only through a localhost API with auth and RBAC. The headless engine's pipeline depends on transports, parsing, the store (SQLite, PostgreSQL, SQL Server), and config.</desc>
<line class="dg-solid" x1="566" y1="34" x2="592" y2="34" marker-end="url(#dg-ah)"/>
<text class="dg-lg" x="600" y="38">depends on</text>
<line class="dg-dash" x1="566" y1="52" x2="592" y2="52" marker-end="url(#dg-ahd)"/>
<text class="dg-lg" x="600" y="56">over the API / wire</text>
<path class="dg-dash" d="M178,130 L330,176" marker-end="url(#dg-ahd)"/>
<path class="dg-dash" d="M400,130 L400,176" marker-end="url(#dg-ahd)"/>
<path class="dg-dash" d="M622,130 L470,176" marker-end="url(#dg-ahd)"/>
<path class="dg-solid" d="M400,230 L400,286" marker-end="url(#dg-ah)"/>
<path class="dg-solid" d="M360,338 L162,390" marker-end="url(#dg-ah)"/>
<path class="dg-solid" d="M388,340 L322,390" marker-end="url(#dg-ah)"/>
<path class="dg-solid" d="M412,340 L480,390" marker-end="url(#dg-ah)"/>
<path class="dg-solid" d="M440,338 L642,390" marker-end="url(#dg-ah)"/>
<rect class="dg-box" x="78" y="84" width="200" height="46" rx="10"/>
<text class="dg-ttl" x="178" y="105" text-anchor="middle">Admin console</text>
<text class="dg-sub" x="178" y="121" text-anchor="middle">separate process</text>
<rect class="dg-box" x="300" y="84" width="200" height="46" rx="10"/>
<text class="dg-ttl" x="400" y="105" text-anchor="middle">VS Code extension</text>
<text class="dg-sub" x="400" y="121" text-anchor="middle">authoring + ops</text>
<rect class="dg-box" x="522" y="84" width="200" height="46" rx="10"/>
<text class="dg-ttl" x="622" y="105" text-anchor="middle">Test harness</text>
<text class="dg-sub" x="622" y="121" text-anchor="middle">synthetic traffic</text>
<text class="dg-bnd" x="400" y="150" text-anchor="middle">LOCALHOST · THE ONLY EXTERNAL SURFACE</text>
<rect class="dg-api" x="220" y="178" width="360" height="52" rx="10"/>
<text class="dg-ttl" x="400" y="200" text-anchor="middle">API — HTTP + WebSocket</text>
<text class="dg-sub" x="400" y="217" text-anchor="middle">auth · RBAC · hash-chained audit</text>
<rect class="dg-bound" x="40" y="252" width="720" height="266" rx="14"/>
<text class="dg-bnd" x="58" y="273">ENGINE — HEADLESS ASYNCIO SERVICE</text>
<rect class="dg-box" x="260" y="286" width="280" height="52" rx="10"/>
<text class="dg-ttl" x="400" y="308" text-anchor="middle">Pipeline</text>
<text class="dg-sub" x="400" y="324" text-anchor="middle">listener · router · transform · delivery</text>
<rect class="dg-box" x="60" y="390" width="158" height="92" rx="10"/>
<text class="dg-ttl" x="139" y="414" text-anchor="middle">Transports</text>
<text class="dg-sub" x="139" y="433" text-anchor="middle">MLLP · TCP · HTTP</text>
<text class="dg-sub" x="139" y="448" text-anchor="middle">SOAP · DB · files</text>
<text class="dg-sub" x="139" y="463" text-anchor="middle">(SFTP/FTP/FTPS)</text>
<rect class="dg-box" x="234" y="390" width="158" height="92" rx="10"/>
<text class="dg-ttl" x="313" y="414" text-anchor="middle">Parsing</text>
<text class="dg-sub" x="313" y="433" text-anchor="middle">HL7 v2 deep parse;</text>
<text class="dg-sub" x="313" y="448" text-anchor="middle">JSON / XML / X12</text>
<text class="dg-sub" x="313" y="463" text-anchor="middle">routed</text>
<rect class="dg-store" x="408" y="390" width="158" height="92" rx="10"/>
<text class="dg-ttl" x="487" y="414" text-anchor="middle">Store</text>
<text class="dg-sub" x="487" y="433" text-anchor="middle">SQLite · PostgreSQL</text>
<text class="dg-sub" x="487" y="448" text-anchor="middle">· SQL Server</text>
<text class="dg-sub" x="487" y="463" text-anchor="middle">AES-256-GCM</text>
<rect class="dg-box" x="582" y="390" width="158" height="92" rx="10"/>
<text class="dg-ttl" x="661" y="414" text-anchor="middle">Config</text>
<text class="dg-sub" x="661" y="433" text-anchor="middle">Connections ·</text>
<text class="dg-sub" x="661" y="448" text-anchor="middle">Routers · Handlers</text>
</svg>
</div>
</section>
<!-- 2. runtime message flow -->
<section class="section section--alt">
<div class="container">
<div class="center" style="margin-bottom:30px;max-width:760px">
<span class="eyebrow">3 · Runtime message flow</span>
<h2>The durable staged queue</h2>
<p class="lead center">A received message is <b>ACKed on receipt</b> — once it's durably committed to the ingress stage, before routing, transform, or delivery. Each handoff is a single committed transaction, giving reliable delivery, retries, and replay with no separate broker.</p>
</div>
<svg class="dg" viewBox="0 0 940 332" role="img" aria-labelledby="t2 d2">
<title id="t2">Runtime message flow through the staged queue</title>
<desc id="d2">An inbound message is decoded and parsed by the listener, committed to the ingress stage, and ACKed on receipt. It then moves through routed and outbound stages via committed transactions — router, transform, and delivery workers — before reaching the outbound connection. Each message earns a disposition finalized by the store.</desc>
<path class="dg-solid" d="M142,148 L196,148" marker-end="url(#dg-ah)"/>
<text class="dg-sub" x="169" y="139" text-anchor="middle">listener</text>
<path class="dg-solid" d="M332,148 L406,148" marker-end="url(#dg-ah)"/>
<text class="dg-sub" x="369" y="139" text-anchor="middle">router</text>
<path class="dg-solid" d="M542,148 L616,148" marker-end="url(#dg-ah)"/>
<text class="dg-sub" x="579" y="139" text-anchor="middle">transform</text>
<path class="dg-solid" d="M752,148 L796,148" marker-end="url(#dg-ah)"/>
<text class="dg-sub" x="774" y="139" text-anchor="middle">deliver</text>
<path class="dg-solid" d="M265,112 L265,86" marker-end="url(#dg-ah)"/>
<rect class="dg-api" x="200" y="56" width="130" height="30" rx="8"/>
<text class="dg-sub" x="265" y="75" text-anchor="middle" style="font-weight:700;fill:var(--accent-ink)">ACK (AA) — on receipt</text>
<rect class="dg-box" x="22" y="120" width="120" height="56" rx="10"/>
<text class="dg-ttl" x="82" y="145" text-anchor="middle">Inbound</text>
<text class="dg-sub" x="82" y="161" text-anchor="middle">connection</text>
<rect class="dg-store" x="200" y="112" width="132" height="72" rx="10"/>
<text class="dg-ttl" x="266" y="142" text-anchor="middle">ingress</text>
<text class="dg-sub" x="266" y="159" text-anchor="middle">raw committed</text>
<rect class="dg-store" x="410" y="112" width="132" height="72" rx="10"/>
<text class="dg-ttl" x="476" y="142" text-anchor="middle">routed</text>
<text class="dg-sub" x="476" y="159" text-anchor="middle">one row / handler</text>
<rect class="dg-store" x="620" y="112" width="132" height="72" rx="10"/>
<text class="dg-ttl" x="686" y="142" text-anchor="middle">outbound</text>
<text class="dg-sub" x="686" y="159" text-anchor="middle">one row / dest.</text>
<rect class="dg-box" x="800" y="120" width="120" height="56" rx="10"/>
<text class="dg-ttl" x="860" y="145" text-anchor="middle">Outbound</text>
<text class="dg-sub" x="860" y="161" text-anchor="middle">idempotent send</text>
<rect class="dg-box" x="22" y="244" width="898" height="58" rx="10"/>
<text class="dg-bnd" x="44" y="266">DISPOSITION · count-and-log, finalized by the store</text>
<text class="dg-sub" x="898" y="266" text-anchor="end">decode/validate failures NAK synchronously, pre-ingress</text>
<text class="dg-ttl" x="44" y="289" style="font-size:13px">RECEIVED → ROUTED / UNROUTED → PROCESSED / FILTERED / ERROR</text>
</svg>
<div class="callout" style="margin-top:30px;max-width:900px;margin-inline:auto">
<p><strong>Why nothing is lost.</strong> Every message is committed to the store before it's ACKed, and each stage handoff is a single transaction — so a crash can only ever repeat the last step, never skip it. In normal operation every message is delivered exactly once. The one edge case: if the engine crashes after a downstream send but before it records that success, on restart it re-sends that single in-flight message rather than risk dropping it. (Engineers call this "deliver again rather than lose" guarantee <b>at-least-once</b> — the deliberate opposite of <em>at-most-once</em>, which can silently lose clinical data.) That rare re-send is detectable, not noise: a re-delivered HL7 message carries the same <code>MSH-10</code> control ID, so a downstream sees a retry of a known message — exactly where deduplication belongs.</p>
</div>
<div class="callout" style="margin-top:22px;max-width:900px;margin-inline:auto">
<p><strong>How much one interface carries.</strong> Capacity is a measured property of this staged queue, not a separate claim. On anonymized, representative message traffic, a single inbound interface sustains a measured <b>~50 messages/second</b> on commodity hardware — about <b>180,000 in a peak hour</b> and on the order of <b>1.6 million a day</b> — with every message committed to the durable store before it's ACKed and delivered strict per-interface FIFO. Whatever the rate, nothing is counted that isn't stored, and nothing is silently dropped.</p>
<p style="margin-bottom:0">Those day-level figures are sized to the <em>busy hour</em>, not a flat 24 hours: clinical ADT traffic is business-hours-bursty. In <b>de-identified ADT traffic profiled from a live hospital interface feed</b>, the busiest hour runs roughly <b>2.7×</b> the daily average, at around <b>11.5 KB</b> per message — so one interface moves on the order of <b>18 GB/day</b>. Treat the message size as <em>directionally</em> right rather than a constant: it varies a great deal by feed, and messages carrying <b>base64-encoded documents or images</b> are far larger, which changes the storage and bandwidth arithmetic well before it changes the message rate. A single hospital's full ADT feed is only a few percent of one interface, so capacity is rarely the question for one site — and when it is, you scale horizontally: feeds split at the source across more interfaces, plus an optional multi-node tier. At that grain, <b>16 million messages a day is roughly 10 interfaces</b> — distributed, not forced through a single pipe. Your real numbers track your transforms, storage, and hardware. <a href="/throughput/">How to read these throughput numbers →</a> · <a href="/assets/docs/MessageFoundry-Throughput-and-Capacity.pdf" target="_blank" rel="noopener">Throughput & capacity (PDF) ↓</a></p>
</div>
</div>
</section>
<!-- 3. config wiring -->
<section class="section">
<div class="container">
<div class="center" style="margin-bottom:30px;max-width:760px">
<span class="eyebrow">4 · Config wiring</span>
<h2>Connections, Routers, and Handlers — wired by name</h2>
<p class="lead center">Your configuration is a graph wired by name — a set of nodes connected by edges, like boxes joined by arrows in a flowchart. An inbound Connection names a Router; the Router forwards to Handler(s) by name; each Handler sends to outbound Connection(s). There's no enclosing "channel" object — set it up with guided wizards, or write it in Python.</p>
</div>
<svg class="dg" viewBox="0 0 900 272" role="img" aria-labelledby="t3 d3">
<title id="t3">Config wiring graph</title>
<desc id="d3">An inbound connection IB_ACME_ADT (MLLP) names a router. The router forwards by name to two handlers — to_EHR and to_archive — which Send to outbound connections OB_EHR_ADT (MLLP) and OB_ARCHIVE (File).</desc>
<path class="dg-solid" d="M190,150 L246,150" marker-end="url(#dg-ah)"/>
<text class="dg-sub" x="218" y="141" text-anchor="middle">names</text>
<path class="dg-solid" d="M430,138 L486,94" marker-end="url(#dg-ah)"/>
<path class="dg-solid" d="M430,162 L486,206" marker-end="url(#dg-ah)"/>
<text class="dg-sub" x="455" y="118" text-anchor="middle">by name</text>
<path class="dg-solid" d="M650,94 L706,94" marker-end="url(#dg-ah)"/>
<text class="dg-sub" x="678" y="85" text-anchor="middle">Send</text>
<path class="dg-solid" d="M650,206 L706,206" marker-end="url(#dg-ah)"/>
<text class="dg-sub" x="678" y="197" text-anchor="middle">Send</text>
<rect class="dg-box" x="20" y="118" width="170" height="64" rx="10"/>
<text class="dg-ttl" x="105" y="145" text-anchor="middle">IB_ACME_ADT</text>
<text class="dg-sub" x="105" y="161" text-anchor="middle">inbound · MLLP</text>
<rect class="dg-api" x="246" y="112" width="184" height="76" rx="10"/>
<text class="dg-ttl" x="338" y="143" text-anchor="middle">@router</text>
<text class="dg-sub" x="338" y="160" text-anchor="middle">filters · forwards by name</text>
<rect class="dg-box" x="486" y="66" width="164" height="56" rx="10"/>
<text class="dg-ttl" x="568" y="91" text-anchor="middle">@handler: to_EHR</text>
<text class="dg-sub" x="568" y="107" text-anchor="middle">filter → transform</text>
<rect class="dg-box" x="486" y="178" width="164" height="56" rx="10"/>
<text class="dg-ttl" x="568" y="203" text-anchor="middle">@handler: to_archive</text>
<text class="dg-sub" x="568" y="219" text-anchor="middle">filter → transform</text>
<rect class="dg-box" x="706" y="66" width="174" height="56" rx="10"/>
<text class="dg-ttl" x="793" y="91" text-anchor="middle">OB_EHR_ADT</text>
<text class="dg-sub" x="793" y="107" text-anchor="middle">outbound · MLLP</text>
<rect class="dg-box" x="706" y="178" width="174" height="56" rx="10"/>
<text class="dg-ttl" x="793" y="203" text-anchor="middle">OB_ARCHIVE</text>
<text class="dg-sub" x="793" y="219" text-anchor="middle">outbound · File</text>
</svg>
<div class="grid grid-2" style="margin-top:42px;align-items:start">
<div>
<span class="eyebrow">Why wire it this way</span>
<h3 style="margin-top:8px">A wiring diagram, not a stack of channels</h3>
<p class="lead">Because the links are just names, each box is defined once and the arrows are cheap — what a bundled "channel" can't give you.</p>
</div>
<ul class="feature-list">
<li><b>Define once, reuse anywhere</b> — name a destination, a shared transform, or a router and reference it from anywhere, instead of copying it into every channel that needs it.</li>
<li><b>Rewiring is a one-line diff</b> — add a destination with one more Send, or reroute a feed by changing a name. No "channel" surgery, and every change is a small version-controlled diff.</li>
<li><b>Mistakes caught at load</b> — an unknown router, a dangling handler, a duplicate name or port are errors at check time, not silent surprises in production.</li>
<li><b>Each piece is small and testable</b> — a Router or Handler is a tiny function with a clear contract, so your team (or an AI agent) can build and test parts in parallel.</li>
</ul>
</div>
</div>
</section>
<!-- cta -->
<section class="section section--tight">
<div class="container">
<div class="cta-band">
<h2>See it run for yourself</h2>
<p class="lead center" style="color:var(--muted-dk)">From install to your first routed message — with the full feature set and the engine docs a click away.</p>
<div class="btn-row center">
<a class="btn btn-primary" href="/getting-started.html">Get started</a>
<a class="btn btn-ghost" href="/features.html">Browse features →</a>
</div>
</div>
</div>
</section>
</main>
<footer class="site-footer">
<div class="container">
<div class="footer-grid">
<div class="footer-brand">
<a class="brand" href="/" aria-label="MessageFoundry home">
<img class="brand-mark" src="/assets/img/logo.svg" alt="" width="28" height="28">
<span class="brand-name">Message<span class="brand-accent">Foundry</span><span class="brand-tm">™</span></span>
</a>
<p class="footer-tagline"><b>More</b> Than an HL7 Interface Engine</p>
<p>Open-source healthcare interface engine — secure by default, built to recognized standards, connecting a wide range of protocols and message types.</p>
<div class="badge-row" style="margin-top:16px">
<span class="badge">Open source</span>
<span class="badge">No lock-in</span>
<span class="badge">Self-hosted</span>
</div>
</div>
<div class="footer-col">
<h4>Product</h4>
<a href="/overview.html">Overview</a>
<a href="/features.html">Features</a>
<a href="/architecture.html">Architecture</a>
<a href="/reliability.html">Reliability</a>
<a href="/dicom.html">DICOM</a>
<a href="/security.html">Security & PHI</a>
<a href="/documents.html">Documents</a>
</div>
<div class="footer-col">
<h4>Tools</h4>
<a href="/tools.html">Tools</a>
<a href="/editor.html">Editor</a>
<a href="/console.html">Console</a>
<a href="/ai.html">AI assist</a>
</div>
<div class="footer-col">
<h4>Get started</h4>
<a href="/getting-started.html">Quickstart</a>
<a href="/guides/">Guides</a>
<a href="/comparison.html">Comparison</a>
</div>
<div class="footer-col">
<h4>Project</h4>
<a href="/status.html">Project status</a>
<a href="https://github.com/MEFORORG/MessageFoundry" target="_blank" rel="noopener">GitHub</a>
<a href="https://github.com/MEFORORG/MessageFoundry/issues" target="_blank" rel="noopener">Issues</a>
<a href="/contact.html">Contact</a>
<a href="/licensing.html">License</a>
<a href="/about.html">About</a>
<a href="/sponsor.html">Sponsor</a>
</div>
</div>
<div class="footer-bottom">
<span>© 2026 MEFOR-ORG, a non-profit — this website and its content, all rights reserved. The MessageFoundry engine is separately open source, licensed under AGPL-3.0-or-later.</span>
<span>MessageFoundry and the MessageFoundry word mark are trademarks of MEFOR-ORG. Mirth Connect, Cloverleaf, Rhapsody, Corepoint, and Ensemble are trademarks of their respective owners; MessageFoundry is independent and unaffiliated.</span>
</div>
</div>
</footer>
<script src="/assets/js/nav.js" defer></script>
</body>
</html>