The hardened, industry-standard VistA/M toolchain
Work on VistA like it's 2026, not 1985. VistA Forge gives M the
developer experience every other language takes for granted: format and lint in your
editor, navigate over a real language server, run unit tests with coverage
against a live engine on every push, call a 40-module standard library
instead of hand-rolling utilities, and ship VistA patches that provably install —
and provably back out. Your code lives in git; the M engine is just the
runtime. One docker pull gets you a running VistA; one static binary gets
you the whole toolchain — identical on YottaDB and InterSystems IRIS.
The tagline makes two claims, and both are load-bearing — they are the standout features of VistA Forge.
Hardened — the VistA layer. VistA Forge treats VistA the way regulated industries treat production software:
- Contract/registry-based seams. Every cross-layer boundary — the m/v waterline, the client-server dispatch seam, the engine seam — is a generated registry held by a red gate, never a convention or a review note.
- TDD for every VSL module. Each
VSL*module is built test-first against live VistA, with per-module coverage gates. - TDD for every v-app. Each Go client is built the same way — unit tests, golden tests, and drift gates on its generated bindings.
- Full-corpus, live-system testing. VSL suites and executable examples run in CI against two complete live VistA systems — YottaDB and IRIS — each carrying VistA's full ~40k-routine base; the KIDS lifecycle is proven against a 2,404-distribution corpus with byte-identical round-trips.
- Provable reversibility. A release tag can only exist after a full build → install → verify → test-on-installed → uninstall cycle diffs byte-clean against baseline on both engines, and every engine mutation lands in a hash-chained attestation ledger.
- Declared data ownership. Modules declare their file footprints
(
@file <n> <mode>), exactly one module may write a given file, and every mutating verb carries fail-closed, host-attributed audit.
Industry-standard — the M layer. The same tooling every mainstream language takes for granted, built on the M standard itself:
- One grammar, from the M standard.
tree-sitter-mis built against the M standard as implemented across today's M systems — and drives the first real formatter, linter, and LSP language server M has ever had, all on one shared parser. - The M Standard Library (MSL). 40
STD*modules make M a peer of every modern mainstream language for 2026-era development — JSON, HTTP(S), JWT, crypto digests, UUIDs, base64, regex, S3, testing, and more — conformance-tested against vendored RFC/NIST corpuses, identical on both engines. - Mainstream delivery. Git-canonical
.msource, static Go binaries, Docker engines, and CI on every push.
By the numbers (measured from the repos, July 2026):
| Hardening surface | Measure |
|---|---|
| M unit tests — MSL + VSL (incl. VWEB) | 64 suites · 1,400+ test cases · 3,200+ assertions, every suite run on both engines |
| Go unit tests — toolchain + v-apps | 1,300+ test functions (665 m-layer, 667 v-apps), table-driven |
| Executable doc examples — MSL + VSL | 51 generated example programs from 500+ @example tags, run against the live VistA engines |
| CI coverage | 21 repos gated on every push; 15 layer-bearing repos each run the waterline gates |
| m/v waterline checks | G1–G4 (m arch check: dependency direction, no VistA symbols below the line, transport monopoly, seam pin) in every layer-bearing repo's CI + an engine-access scan in every M CI caller + a scheduled org-wide meta-gate backstop |
| m-engine waterline checks | an executable conformance gate per driver (m-ydb, m-iris) — a driver binary that can't pass it can't ship |
| VSL drift gates | 24 red gates in one make gates run — ICR, namespace, engine-access, manifest/dispatcher/envelope drift, data ownership, additive-only compat, … |
| Hard stops | lint --error-on=error zero findings · ≥85 % per-module coverage · release only on a byte-clean lifecycle, both engines |
And the gating is rigid top to bottom: a red anywhere is a hard stop — it blocks the commit, the merge, or the release tag. Nothing in the stack ships around a failing gate.
- Hardened, and industry-standard
- The stack at a glance
- Why VistA Forge
- Start here
- What's here, by developer task
- Write M like any modern language
- Test against a real engine, with coverage
- Stop hand-rolling utilities — the M standard library
- Reach an engine exactly one way
- Treat VistA patches as code
- Build VistA-side libraries on tested seams
- Administer VistA with plain-noun commands
- Observe the RPC Broker
- Run VistA on your own machine
- Drive it all with an AI agent
- Architecture: enforced waterlines + registries
- The repos
- Design principles
- Contributing
- Shared CI
The VistA layer (v-*, top table) is built from one repeating shape — a
v-app, where app is a VistA functional domain (package management,
database management, task management, …). Each domain ships as a client and
a server component joined by a client-server API, consolidating an
entire category of VistA-specific functionality into one coherent command-line
(or web) interface the end user actually drives. Beneath it, the engine-neutral
M layer (m-*, bottom table) is the toolchain, seam, and runtime every
domain is built on.
The two are split by the v/m waterline — dependency is one-way, v → m, never back — and each italic (gray) row is itself an enforced seam, held by a generated registry and a red gate rather than convention.
VistA-specific (v-*)
| Layer | What it is & does |
|---|---|
| v-app | end-user client application (written in Go) for a v-stdlib domain (database, tasks, monitoring, …), using a modern, mainstream user interface and naming conventions, with comprehensive quality, safety, and unit tests — VistA-specific terminology and syntax modernized to mainstream terms |
| v-api | client-server API — carries every client-to-server verb call, audited |
| v-stdlib | VistA Standard Library (VSL). A library comprised of wrappers of VistA infrastructure applications (FileMan, Kernel, …) using modern, mainstream formats, naming conventions, and APIs |
M-general — engine-neutral (m-*)
| Layer | What it is & does |
|---|---|
| m-toolchain | m dev tools — fmt · lint · lsp · test · coverage, one shared parser |
| m-stdlib | M Standard Library (MSL). A portable, industry-standard M library of 40 STD* runtime modules (JSON, HTTP, crypto, testing, …) |
| m-driver-sdk | M engine seam — the only path to a live engine |
| m engines | YottaDB / IRIS — run VistA; vendor specifics contained in the drivers (m-ydb, m-iris) |
The two standard libraries this stack is built on:
- VSL — the VistA Standard Library. A thin wrapper around VistA's bespoke technology (Kernel, FileMan, KIDS), reframed in modern mainstream terms and methods — so working against VistA feels like working against an ordinary, well-typed API.
- MSL — the M Standard Library. A suite of industry-standard libraries (JSON, HTTP, crypto, testing, dates, and more) that make M a peer of other mainstream languages instead of a special case.
Each layer is a repo family covered below; how the three seams are generated and red-gated is in Architecture.
VistA — the VA's public-domain hospital information system — has run real hospitals for over four decades and is one of the largest working bodies of M (MUMPS) code in existence. Yet developing on it today still means working the way its authors did in the 1980s: code lives in the database rather than in git; there is no formatter, no linter, no language server, no unit-test runner, no coverage, no CI; and a patch installs into a live system with no reliable way to back it out. The everyday developer loop every modern language takes for granted has simply never existed for M.
VistA Forge closes that gap — not by rewriting VistA, but by building the modern loop around it:
- Git-canonical source —
.mfiles under version control; the database is the runtime, not the repository. - Real editor tooling — formatter, linter, and language server, all on one shared parser.
- Test-first against live engines — unit tests and coverage gates run on real YottaDB and IRIS instances, in CI, on every push.
- Patches as code — KIDS distributions decomposed into git-diffable trees, built from drift-gated specs, provably reversible.
- Agent-ready by construction — one command grammar and reflected schemas expose the whole toolchain to AI agents over MCP.
And because the m-* half is engine-neutral, none of it is VistA-bound: the
same toolchain works on any M system, in healthcare and beyond.
- Want the tool? m-cli is
the
mcommand; its README has install + first-run instructions. - Want a running VistA?
docker pull ghcr.io/vista-forge/vista-iris:latestboots a fully installed VistA-on-IRIS with fictitious test data. - Setting up a machine?
workspace —
./bootstrap.shclones every repo and generates the Go workspace. - Want the design record? The docs repo holds the ADRs and design corpus behind everything below.
m fmt (AST-preserving formatter), m lint (query-driven rule engine, with
XINDEX/VistA-Kernel compatibility profiles), and m lsp (an LSP 3.x language
server) all sit on one parser —
m-parse, which embeds the
tree-sitter-m grammar as WASM and runs it through wazero. The whole
toolchain ships as static Go binaries with no CGO and no C toolchain at
runtime.
m test runs *TST.m suites on a live YottaDB or IRIS engine via
^STDASSERT, with machine-readable results; m coverage produces LCOV with
per-module coverage gates. Every library in this org is built test-first and
gated on both engines.
m-stdlib (tagged through
v0.13.0) is a pure-M library of 40 STD* modules: data formats
(STDJSON, STDXML, STDTOML, STDCSV, STDREGEX, STDURL), encoding
(STDB64, STDHEX, STDUUID), testing (STDASSERT, STDMOCK, STDFIX,
STDSEED, STDSNAP, STDPROF), crypto and auth (STDCRYPTO, STDCSPRNG,
STDJWT, STDSIGV4), networking (STDHTTP, STDHTTPD, STDNET, STDS3),
plus dates, logging, collections, strings, math, filesystem, and more.
Conformance-tested against vendored RFC/NIST corpuses; identical on YottaDB
and IRIS.
m-driver-sdk defines the
vendor-neutral driver contract — typed verbs (Health · Load · Exec · ReadGlobal · SetGlobal), a structured engine-error model, honest capability
documents, and an executable conformance gate that any driver binary must
pass. Two drivers implement it today:
- m-ydb — YottaDB, over
local, Docker, and SSH transports, plus native passthrough to the full
YottaDB utility surface (
mupip,dse,gde, …). - m-iris — IRIS, over Atelier REST (remote), Docker session, and local transports, including routine sync/deploy into an IRIS namespace.
Everything above the seam — m test, m vista exec, the v tools — reaches
an engine only through this contract. Both drivers are conformance-green and
proven against live VistA systems on their respective engines.
v-pkg (tagged through v0.6.1)
gives KIDS — VistA's native package/patch format — a real lifecycle:
parse · classify · lint · decompose · assemble · roundtrip · diff · build · install · verify · snapshot · restore · uninstall · attest. Decompose a
distribution into a git-diffable component tree and reassemble it
byte-identically; build from a drift-gated spec; install, verify, and cleanly
back out against a live engine. Mounted as v pkg on the
v-cli umbrella.
v-stdlib (released as
VSL*1.0*24 / tag v1.0.24) is the VistA Standard Library — VSL* modules
wrapping Kernel/FileMan seams behind tested M APIs: VSLCFG (XPAR
configuration), VSLSEC (Kernel security), VSLFS (FileMan file storage),
VSLIO (file I/O), VSLLOG (audit logging), VSLTASK (TaskMan) — plus the
VSLAPI admin-verb dispatcher and its VSLPING diagnostic prover (next
section). Every label carries machine-read source tags (@publish,
@mutates, @since, @file <n> <mode>, @call) that generate the
VSL manifest — the registry every downstream surface is built from.
Ships as a single deterministic KIDS build, exercised against live VistA on
both engines; a release tag can only be cut when the
lifecycle-healthcheck gate proves the build byte-clean reversible
(build → install → verify → suites-on-installed → uninstall → baseline diff)
on both.
The sysadmin platform pairs each engine-side VSL* module with a host-side
v <domain> command, generated — not hand-wired — from the VSL manifest:
- Engine half: a module opts its labels into dispatch (
@dispatch); the gen cascade projects them into a generated verb table (VSLAPIT) that theVSLAPIdispatcher serves over a versioned JSON envelope (b64 request in, sentinel-framed reply out, typed error classes, pagination tokens, and fail-closed host-attributed audit for every mutating verb).VSLPING(echo/sum/page/fail/touch) is the platform prover. - Host half: v-base is the
shared library every domain pins (like clikit and the driver SDK): the
dispatch client, engine profiles (
--engine vehu|foia), the pagination runtime, the--yes/exit-4 confirm conventions — andvslgen, which turns the pinned manifest into typed Go bindings plus a freeexplaincommand per verb (APIs wrapped, ICRs, files touched, safety class). A domain that drifts from its manifest fails CI, not code review. - The commands: plain nouns a developer can guess, mounted on the
v-cli umbrella —
v pkg(KIDS),v rpc-debug/v rpc-tap(Broker) today; the VSL-backed sysadmin set (v alertfirst, thenv ping,v config/VSLCFG,v task/VSLTASK,v sec/VSLSEC, audit viaVSLLOG, …) lands vertical-by-vertical as the platform build-out (B1–B2) completes. VA product names never appear in a command — a lint gate enforces the plain language.
v-rpc-debug (tagged through
v0.3.0, mounted as v rpc-debug) is a read-only RPC observation tool over
the broker's native XWBDEBUG logging: doctor, arm/disarm, tail,
capture, status, with LDJSON output for offline analysis — no
modifications to the running system.
vista-iris publishes a multi-arch container of WorldVistA on IRIS for Health Community — the full site build (routine/global import, FileMan/Kernel install) is baked into the image, so it boots already operational: Management Portal + REST (52773), RPC Broker for CPRS (9430), HL7 MLLP (5026), IRIS superserver (1972). Fictitious test data only.
Every binary shares one command grammar
(clikit, tagged through v0.8.0):
--output text|json|auto, a versioned JSON envelope, a deterministic
exit-code ladder, and a reflected schema command — so an AI agent invokes
the same gated surface a human uses and gets machine-parseable results.
(An MCP shim over that schema was built and retired unused —
m-dev-tools-mcp, archived
2026-07-05, kept read-only as the reference for the pattern.)
The org is partitioned by three hard boundaries, and every boundary is held by a generated registry with a red gate — never by convention or code review.
The m/v waterline splits everything on one question — does this code work on a bare M engine with no VistA installed?
m-*— the M standard toolchain: works on any M implementation, with or without VistA — VistA today, and equally the other M-based systems across healthcare and finance (Epic, MEDITECH, LabCorp, and others). Includes MSL, theSTD*M standard library.v-*— VistA-specific: needs Kernel/FileMan/KIDS. Includes VSL, theVSL*VistA standard library, and thev <domain>sysadmin apps.
Dependency is one-way — v → m, never the reverse.
The dispatch seam splits the v layer itself into an engine half
(VSL* M modules + the VSLAPI dispatcher) and a host half (v-base +
the v <domain> Go apps). The two halves share exactly one contract: the
versioned VSLAPI envelope, whose verbs, parameter shapes, safety classes,
and data footprints are all projections of one generated registry — the
VSL manifest. Host bindings are generated from it (vslgen); the
engine dispatch table is generated from it; a drift on either side is a CI
failure.
The m-engine waterline separates everything above from the
vendor-specific engines: the only path to a live engine is the
m-driver-sdk contract (one Go client, one envelope), with all vendor
specifics below it in the drivers (m-ydb, m-iris). Raw docker exec /
direct-mode access is denied by tooling, not discouraged by docs.
Classic VistA integrates by reaching into other packages' globals: every application owns its own data and freely reads and writes everyone else's, with the agreements (ICRs/DBIAs) recorded in prose nobody executes, naming policed by human SAC review, and patches installed with no reliable back-out. The result is the coupling archaeology every VistA developer knows.
Here the same concerns are declared, generated, and red-gated — one
discipline, source-tag → generate → registry → red-gate, at every layer:
| Surface | Registry (generated) | Red gate |
|---|---|---|
| Repo layering | ecosystem.json (layer per repo) |
arch-waterline in every repo's CI (G1 dependency direction, G2 no VistA symbols below the line, G3 transport monopoly, G4 seam pin) + a scheduled bidirectional meta-gate |
| Engine access | driver capability document + seam contract | executable conformance gate per driver; engine-access scan red-gates any hand-rolled transport |
| Admin verbs | vsl-manifest.json (from @publish/@since/@mutates tags) → generated VSLAPIT dispatch table + vslgen host bindings |
dispatch/bindings drift gates; G-compat — the published surface is additive-only against the last release tag |
| Data ownership | per-module @file <n> <mode> footprints in the manifest |
G-data — source scan vs declared footprint, single-writer-per-file, classified-direct + sprawl counters |
| Module granularity | manifest calls DAG + seam projections |
G-lateral (label granularity, no lateral tangles) |
| Releases | deterministic KIDS build spec → dist/VSL.kids |
lifecycle-healthcheck: install → verify → suites-on-installed → uninstall → byte-clean baseline diff, both engines — a release tag may exist only on green; every engine mutation lands in a hash-chained attestation ledger |
| Command surface | v-registry.json (the v umbrella mounts) |
registry golden test + plain-language lint (no VA product names in commands) |
| Host frameworks | pinned clikit / m-driver-sdk / v-base versions | G-framework meta-gate — every domain pins the same versions |
So no application "owns its own data" in the VistA sense: a VSL module
declares which files it touches and how, exactly one module may write a
given file, every mutating verb is audited with host attribution, and a
patch that cannot prove byte-clean reversibility cannot become a release.
The layer stack itself is the table in The stack at a glance; here is what's inside each band.
The two halves share exactly one contract — the versioned VSLAPI envelope — and both sides of it are generated from the VSL manifest:
flowchart TB
subgraph host["host half — Go, on your machine"]
vcli["v CLI umbrella — v pkg · v rpc-debug · v rpc-tap · v alert …"]
vbase["v-base — dispatch client · engine profiles · pagination · vslgen"]
vcli --> vbase
end
subgraph eng["engine half — M, inside VistA"]
vslapi["VSLAPI dispatcher + generated VSLAPIT verb table"]
vsl["VSL* modules — VSLCFG · VSLSEC · VSLFS · VSLIO · VSLLOG · VSLTASK · VSLPING …"]
data[("Kernel / FileMan / KIDS data — @file-declared, single-writer-per-file")]
vslapi --> vsl --> data
end
manifest[["vsl-manifest.json — generated from @publish / @mutates / @file / @call tags"]]
vbase -. "dispatch seam — versioned JSON envelope, audited mutations" .-> vslapi
manifest -. "vslgen → typed Go bindings" .-> vbase
manifest -. "→ VSLAPIT dispatch table" .-> vslapi
Editor tools sit on one shared parser and never need an engine; engine tools reach one only through the seam:
flowchart TB
subgraph editor["editor and CI tools — no engine needed"]
fmt["m fmt · m lint · m lsp"]
parse["m-parse — one shared parser (tree-sitter-m as WASM)"]
fmt --> parse
end
subgraph livetools["live-engine tools"]
test["m test · m coverage · m vista exec · m arch"]
end
msl["MSL — the STD* M standard library (40 modules: JSON · HTTP · crypto · testing · …)"]
seam["mdriver.Client — down through the engine seam"]
test -- "runs *TST.m suites over" --> msl
test --> seam
One contract, two conformance-gated drivers, all vendor specifics contained:
flowchart TB
sdk["m-driver-sdk — typed verbs (Health · Load · Exec · ReadGlobal · SetGlobal) + executable conformance gate"]
ydb["m-ydb — local · Docker · SSH transports, plus mupip/dse/gde passthrough"]
iris["m-iris — Atelier REST · Docker · local transports, plus routine sync/deploy"]
y[("YottaDB")]
i[("InterSystems IRIS")]
sdk --> ydb --> y
sdk --> iris --> i
| Repo | Delivers | Latest |
|---|---|---|
| m-cli | Cross-engine M toolchain — the m busybox (fmt/lint/lsp/test/coverage/watch/vista/arch) |
v0.1.0 |
| m-stdlib | Pure-M runtime standard library — STD* modules | v0.13.0 |
| m-driver-sdk | M engine-driver SDK — the seam contract + reference engine Client (mdriver.Client) | v0.8.1 |
| m-ydb | YottaDB engine driver — the m-ydb binary producing the driver envelope | — |
| m-iris | InterSystems IRIS engine driver — the m-iris binary producing the driver envelope | v0.1.0 |
| m-parse | Engine-neutral M parse substrate — tree-sitter-m over wazero; the foundation fmt/lint/lsp sit on (spec §4) | v0.1.0 |
| clikit | Shared CLI convention layer — Kong grammar, JSON envelope, exit-code ladder — for the m-* / v-* Go toolchain (engine-neutral; no engine transport) | v0.9.0 |
| Repo | Delivers | Latest |
|---|---|---|
| v-pkg | VistA KIDS packaging — the v pkg domain (build/install/verify/uninstall over the m engine seam) |
v0.7.0 |
| v-cli | The v CLI umbrella — VistA developer tooling (v pkg/db/config/…) aggregated under one binary |
— |
| v-stdlib | VistA Standard Library — VSL* routines (VistA-specific; consumes the engine-neutral STD* base upward) | v1.0.25 |
| v-base | Host base library for v- CLIs — VSLAPI dispatch client (envelope codec, engine profiles, pagination, attribution/--yes) + the vslgen binding generator (vista-sysadmin-base §5) | v0.3.0 |
| v-db | VistA FileMan data-dictionary / PIKS profiler — the v db domain (read-only inspect/profile over the m engine seam via v-base) |
— |
| v-web | VistA Web Services — VWEB* routines: the inbound socket adapter (listener + device/TLS adapter) that drives the m-stdlib STDHTTPD server framework over a real socket | — |
| v-rpc-debug | VistA RPC developer tools — the v rpc-debug domain |
v0.4.0 |
| v-rpc-tap | VistA scalable RPC tap — the v rpc-tap domain |
— |
| Repo | Delivers |
|---|---|
| go-cli-template | Scaffold for new Go CLIs in the house style (carries the canonical, drift-gated .golangci.yml) |
| docs | ADRs, design corpus, and cross-repo coordination |
| workspace | Clone-all manifest + idempotent bootstrap.sh for machine setup |
| vista-iris | Pre-built VistA-on-IRIS container (ghcr.io/vista-forge/vista-iris), fictitious test data only |
| .github | Reusable CI (go-ci, m-ci, arch-waterline, docs-validate), the docs standard (CONVENTIONS.md), the org repo registry, and the scheduled meta-gate |
- Git-canonical source. M code lives in
.mfiles under version control; the database is the runtime, not the repository. - Dual-engine parity. YottaDB and IRIS are both first-class; the same suites gate both, and engine specifics live only inside the drivers.
- One seam. Every tool reaches a live engine through the
m-driver-sdkcontract — no hand-rolled transports, verified by an executable conformance gate. - Gated, not aspirational. The layer boundaries, seam contracts,
registries, KIDS build specs, and doc links are all generated, drift-gated
artifacts checked in CI — not conventions enforced by review. A new layer
earns trust by adding its
source-tag → generate → registry → red-gatetriple. - Declared data ownership. A module states which VistA files it touches
and how (
@file <n> <mode>); a source scan red-gates undeclared access, and exactly one module may write a given file — the inverse of VistA's everyone-writes-everything coupling. - Reversible by proof. Engine mutations are audited into a hash-chained attestation ledger, installs capture pre-images, and a release tag can only be cut when a full install → uninstall cycle diffs byte-clean against baseline on both engines.
- Static, dependency-free binaries.
CGO_ENABLED=0everywhere; even the M parser runs as WASM. - One contract, many surfaces. A single command grammar and reflected schema mean CLI flags, JSON envelopes, and MCP tools never drift.
- Test-first. Every module is built TDD with per-module coverage gates;
m-stdlibconformance suites run against vendored RFC/NIST corpuses.
Contributions are welcome. The org-wide guides live in this .github repo:
The toolchain is Apache-2.0 org-wide (relicensed 2026-07-03) — see each
repo's LICENSE/NOTICE. The two documented exceptions are the MIT VS Code
extensions and the embedded tree-sitter-m grammar in m-parse, whose upstream
(in the separate m-dev-tools org) is still AGPL-3.0 pending relicense.
Not affiliated with or endorsed by the US Department of Veterans Affairs. "VistA" refers to the public-domain VA health information system. These tools operate on VistA source and ship with fictitious test data only — never load real patient (PHI/PII) data into instances used with them.
Go repos call the reusable workflows in this repo:
uses: vista-forge/.github/.github/workflows/go-ci.yml@main