
Initial Release
FlightDeck is a keyboard-first management portal for InterSystems IRIS that ships as a container you
can run in one command: you drive it from a command palette, every browser tab starts read-only, and
every change is shown to you first — the exact fields that would change and the users who would lose
access — and only applied once you confirm. It is built entirely on IRIS’s official SysAdmin API
(the REST management API under /api/admin), so what you can see and do is exactly what your own
IRIS account is allowed to see and do, and your password is never stored by any part of it.

Requirements
One command, after cloning
git clone https://github.com/kcedd34/iris-flightdeck.git
cd iris-flightdeck
docker compose up -d # the install
docker compose logs -f iris
This pulls intersystemsdc/iris-community:2026.2-zpm, pinned in docker-compose.yml, and builds
a small image on top of it that adds FlightDeck. So the default install gives you the full
portal, not the limited mode described under Compatibility — you have to ask for
an older IRIS to get that.
Wait for this line — about 15 seconds after the first build, which itself takes a few minutes while
the base image downloads:
FlightDeck is ready at http://localhost:52780/flightdeck/ — sign in with the default account documented in the README (local evaluation only).
Open http://localhost:52780/flightdeck/ and sign in with _SYSTEM / SYS, the default account
of the InterSystems Community image. FlightDeck creates and changes no credential. Use it for local
evaluation only.
FlightDeck installs into the USER namespace and does not replace the InterSystems Management
Portal — it runs beside it, on the same instance, and points you back to it for the few operations it
declines to perform.
This container also creates the demonstration objects, so that every screen
has something to show on first open. One of them, /csp/fd-demo, is deliberately unauthenticated
— it exists so the exposure warning has something real to point at. That is fine on a throwaway
container bound to your own machine, and it is the reason the IPM path creates nothing of the sort
unless you ask.
Then try these three, in order — they are the portal in ninety seconds:
FD_Demo. The demonstrationdelete and press Enter on “Delete a role” — the palette matches against theFD_Demo_Operator: before anythingTo stop: docker compose down. To remove everything including IRIS data: docker compose down -v.
If the ready line never appears, or sign-in refuses you, see Troubleshooting.
On IRIS for Health — the same procedure, one variable. --build is needed here and not above
because the local image has already been built on top of IRIS Community, and changing the base means
rebuilding it:
docker compose down -v
IRIS_IMAGE=intersystemsdc/irishealth-community:2026.2-zpm docker compose up -d --build
If the port is already in use, docker compose up -d stops with a message like this one —
Docker’s exact wording varies by version, but it always names the port:
Error response from daemon: failed to set up container networking: driver failed programming
external connectivity on endpoint iris-flightdeck-iris-1: failed to bind host port
0.0.0.0:52780/tcp: address already in use
Pick another one. Nothing else changes, and the ready line shows the new URL:
FLIGHTDECK_PORT=52790 docker compose up -d
That syntax is POSIX shell. In PowerShell or cmd, or whenever you would rather not repeat the
variable, put it in a .env file next to docker-compose.yml — Compose reads it automatically:
FLIGHTDECK_PORT=52790
The install binds to localhost only. FlightDeck is an administration portal and this install
signs in with the Community image’s documented default account, so on a machine with a public
address, publishing it on every interface would put full administrative access to that IRIS on the
internet. Locally nothing changes — the portal is at http://localhost:52780/flightdeck/ either way.
If you do want it reachable from elsewhere:
FLIGHTDECK_BIND=0.0.0.0 docker compose up -d
Before you do, at least: change _SYSTEM‘s password, and put it behind something that terminates
TLS. deploy/vm/ in this repository is a worked example — a reverse proxy that forwards only
FlightDeck’s own two paths and answers 404 to the rest of IRIS, an unprivileged container with no
bind mounts, and a restricted egress rule. It is what runs the public demo.
This path needs IPM (ZPM) already installed on the instance — the -zpm Community images carry it.
On IRIS 2026.2 or later (2026.1 installs in limited mode), in the namespace where you want
FlightDeck, from a clone of this repository:
zpm "load /path/to/iris-flightdeck"
This installs two web applications — /flightdeck for the interface and /api/flightdeck for the
API, with Password authentication — a role FlightDeck_Runtime granting read access to FlightDeck’s
code database inside those two applications only, and a small capture routine in %SYS. Nothing else
is created or changed.
On an instance older than 2026.1 there is no SysAdmin API at all: FlightDeck installs, and sign-in
refuses with the version it detected rather than half-working.
Demonstration objects are off by default on this path. To add them:
zpm "load /path/to/iris-flightdeck -DDemo=1"
⚠️ -DDemo=1 creates /csp/fd-demo, a deliberately unauthenticated web application, so that the
exposure warning has something real to point at. That is what you want on a scratch instance and not
what you want on a shared one. Without the flag, nothing of the sort is created.
The installer creates the role and every demonstration object through the SysAdmin API. The
installing user needs %All, or equivalent rights for the web applications and the %SYS routine.
There is a public demo at http://109.123.244.170/.
It runs against a real InterSystems IRIS Community instance, not a simulator: every operation you
perform there is executed against the platform, which is why the instance is rebuilt every hour.
Two accounts are published, and both are printed on the demo’s own sign-in form:
| User | Password | What it shows |
|---|---|---|
demo |
printed on the demo’s sign-in form | Full administrator: every domain and every operation this IRIS offers. |
demo_reduced |
printed on the demo’s sign-in form | Reduced privileges — the stock %Operator role and nothing else. 57 of the 273 operations are offered; the rest are disabled naming the privilege they need, and Permissions and Web applications cannot be opened at all. It is the capability map with something to show, which the first account cannot do because an administrator holding %All never sees a disabled control. |
These are not the credentials above. The local Docker install signs in with _SYSTEM / SYS,
the InterSystems Community image’s own default. The demo instance has a different administrative
password that exists only on that machine, and both accounts you are given there are separate ones.
Four things are true of that demo and are worth knowing before you judge it:
%All, because permissions and security are the partIf you want an instance that is yours, with nothing shared and nothing reset, the one-command install
above takes a couple of minutes.


FlightDeck needs the official SysAdmin API — v2 for the full portal, v1 for a reduced one — and
reads capability from the API’s own declarations rather than from a version number: an operation the
instance does not offer is a disabled control with the reason on it, never a missing screen.
| IRIS version | Channel | Offered to you | Not offered by that IRIS | Declined by FlightDeck | Total |
|---|---|---|---|---|---|
| IRIS 2026.2 | latest-cd |
262 | 0 | 11 | 273 |
| IRIS 2026.1 | latest |
200 | 62 | 11 | 273 |
“Not offered by that IRIS” is what the instance’s own API does not expose; “Declined by FlightDeck”
is what the portal refuses to do on every version. These numbers were read from a
running instance of each, and you can reproduce them on yours:
curl -s -u _SYSTEM:SYS -H 'X-FlightDeck-Tab: check' \
http://localhost:52780/api/flightdeck/v1/session | grep -o '"capabilitySummary":{[^}]*}'
(The v1 there is FlightDeck’s own API version, not the SysAdmin API’s. The header can be any
string: FlightDeck’s API requires each browser tab to identify itself, which is how safe mode is
scoped to one tab.)
IRIS for Health Community Edition 2026.2 reports the same numbers as IRIS 2026.2.
Implemented is not the same as exercised. Of the 268 operations across the six domains, 138 are
executed by a test that then reads the result back from the instance through the official API, 91 are
exempt by name with a written reason, and 39 have no such test yet — which is why
check-functional-coverage is red. The breakdown, operation by operation, is in
https://github.com/kcedd34/iris-flightdeck/blob/001-foundation-shell/https://github.com/kcedd34/iris-flightdeck/blob/001-foundation-shell/verification/functional-coverage.md.
IRIS 2026.1 runs in limited mode. That release — still the latest tag of the Community images —
exposes API v1 only. FlightDeck translates what v1 offers, shows a persistent Limited indicator,
and disables the rest with the reason on each control. Namespaces are still browsable, read through a
native provider because v1 has no endpoint for them.
An instance with no SysAdmin API at all refuses sign-in and says so, naming the version it detected.
Why disabled controls name a version, if nothing checks one. FlightDeck never asks the instance
what release it is in order to decide what to offer: it asks the API what it declares, and an
operation that is absent is disabled. The sentence on the disabled control names the release that
first shipped the operation, because “Requires IRIS 2026.2” is more useful to a reader than “this
instance does not declare this operation”. The version is in the message, not in the decision.
.. segments (plain or percent-encoded) or backslashes are refused, and so areAuthorization, Cookie, Proxy-Authorization and Host headers. The executor is not anGET, HEAD and OPTIONS run directly. POST, PUT, PATCH andDELETE are refused by the server while the tab is read-only; otherwise they open the shared-u '<user>:<password>' and never your credentials.Eleven official operations are implemented nowhere in FlightDeck by decision, on every version.
They appear as disabled controls stating the reason and the native path that performs them, and
docs/api-coverage.md lists them as declined. They share one property: the portal can neither
rehearse them nor undo them.
The journal is a different decision, and a more interesting one. Three of the five log sources
exist precisely because the official API offers nothing for them, so a missing operation is normally
a candidate for a native provider. The journal is where that stops. On IRIS 2026.1 the journal
operations are withheld by the platform, and FlightDeck does not read journal records natively to
fill the gap:
FlightDeck does not read the journal natively: filtering records by the databases you can read is
an authorization decision that belongs to IRIS.
Implementing it would mean the portal deciding which records you may see. That decision belongs to
the platform, so on that version the journal source says why it is absent and the other four keep
streaming.
Created on the Docker install, and with -DDemo=1 on IPM. Every object is created through the
SysAdmin API, and re-running creates no duplicates.
| Kind | Name |
|---|---|
| Resources | FD_Demo_Reports, FD_Demo_Billing |
| Roles | FD_Demo_Operator, FD_Demo_Auditor |
| Web application | /csp/fd-demo, intentionally unauthenticated so the exposure warning has something to show |
| Tasks | FD Demo daily no-op, FD Demo nightly no-op, FD Demo failing task (fails on purpose) |
| Wallet collection | FD_Demo_Vault (no secrets) |
%Admin_Secure:U). FlightDeck crosses those declarations with the privileges IRIS reports for you.Role:<name>, Owner Privilege), so FlightDeck renders that and does%Admin_Secure:USE or the %All role. If it can read what itinfo, warning, error, fatal. Eachunknown, which sits outside the ordering — not info, and never a guess fromOne class: FlightDeck.Native.HostMetrics, the provider that reads host CPU and host memory. It is
written in Embedded Python because reading /proc/stat and /proc/meminfo is filesystem access
and text parsing, which is Python’s natural work and not ObjectScript’s.
This is not a feature added to use a language. It is the same reading, moved to the language that
suits it, and the move paid for itself in the only way that counts — code removed:
| Before, in ObjectScript | After, in Embedded Python |
|---|---|
A sequential device opened by hand, read line by line, with the device closed and $io restored in every path — because %Stream.FileCharacter reads nothing from procfs, where every file reports size 0 |
with open(path) as handle: … |
$piece/$zstrip walking the text with whitespace collapsed by a "<=>W" strip |
str.split() and str.partition(":") |
A whole ReadFile helper whose only reason to exist was that workaround |
Deleted |
What stayed in ObjectScript, deliberately: the %Status contract its callers expect, the previous
CPU sample held in the session, and the arithmetic — because the published percentages are rounded
with $normalize, and moving that would have changed values that are checked against free and
top. Python reads and parses; ObjectScript keeps what is its own.
No version is tested anywhere in it. Embedded Python needs IRIS 2021.2 or later, far below the
2026.1 FlightDeck itself requires, so any instance that can run this portal has it. And if the Python
runtime should fail regardless, every entry point degrades through the status the class already
returned: the vital is marked unavailable with its reason, like any other capability that is absent,
and the screen does not break.
Everything else — the log readers, the REST executor, the whole domain layer — remains ObjectScript,
because none of it is filesystem or text work.
scripts/verify/verify_platform.py checks, in order, the eight platform facts FlightDeck depends on:
SysAdmin API v2; which sign-in path works without storing credentials; monitor data shapes;
asynchronous database metrics; the wallet; the REST management API; log file locations and the
interoperability log; and auditing.
Each fact is classified confirmed_present, confirmed_absent or inconclusive, with the raw
response recorded. You do not need to run it — the reports it produced are committed — but if you
want to:
scripts/verify/run-both-images.sh
It needs Python 3, starts and removes its own throwaway containers on ports of its own, and does
not touch the install you are running or the data in it.
Reports are written to https://github.com/kcedd34/iris-flightdeck/blob/001-foundation-shell/verification/<product>-<version>.json. Exit code 0 when nothing is
inconclusive, 1 otherwise, 2 for a usage error. The committed reports and findings are in
https://github.com/kcedd34/iris-flightdeck/blob/001-foundation-shell/verification/.
Every assertion here is enforced somewhere, because a claim nobody can check is a claim that drifts.
| Claim | Where it is enforced |
|---|---|
| The operation counts, and that every operation is implemented or declined for a stated reason | scripts/build/check-coverage.py, in the build: it fails naming any operation of a shipped domain that no code reaches and no policy declines, and it keeps no list of tolerated gaps |
| That an implemented operation has actually been executed, with its effect read back from the instance | scripts/build/check-functional-coverage.py, run after the end-to-end suite: it reads the record those tests write and fails naming every operation no test executed with an independent read-back. It is red today, on purpose — see https://github.com/kcedd34/iris-flightdeck/blob/001-foundation-shell/https://github.com/kcedd34/iris-flightdeck/blob/001-foundation-shell/verification/functional-coverage.md |
| This README’s own structure and numbers | scripts/build/check-readme.py, in the build |
| No credential stored, cached or logged; no secret in any response, trail or export | frontend/e2e/audit.spec.ts and secrets.spec.ts sweep the responses and the exports; scripts/build/check-secrets.py fails on an undeclared secret field |
| Safe mode is enforced by the server, not the interface | scripts/dev/check-safe-mode-enforcement.sh sends every mutating route directly, bypassing the interface, and requires each to be refused |
| Every mutation goes through the one confirmation path | npm run check:mutation-boundary and scripts/dev/check-mutation-enforcement.sh |
| The REST executor opens no outbound connection | frontend/e2e/rest-confinement.spec.ts |
| A 100 MB log pages at constant cost | Measured and recorded in https://github.com/kcedd34/iris-flightdeck/blob/001-foundation-shell/verification/feature-005-signoff.md |
| It runs on both Community images, from clean | https://github.com/kcedd34/iris-flightdeck/blob/001-foundation-shell/verification/install-runs.md, one entry per install |
| That the three installs in the compatibility table above really were three products, each serving this build | scripts/build/check-matrix-identity.py, run after the end-to-end matrix: each run records what /api/admin/info says it is and the hash of the bundle that instance served, and the gate fails naming any install that never ran or answered with different assets. A port does not identify a product and neither does a docker tag |
FLIGHTDECK INSTALL FAILED: indocker compose logs iris. It names the step and the IRIS error. IRIS stays running so you canlatest Community_SYSTEM / SYS on the Docker%Admin_* resources, for example through the %Operator or %Manager role.| Area | Command |
|---|---|
| Frontend dev server (proxies the API to the Docker install) | cd frontend && npm ci && npm run dev, then open http://localhost:5173/flightdeck/ |
| Frontend checks | npm run lint && npm run check:tokens && npm run check:dialect && npm run contrast && npm run test |
| End-to-end tests (Docker install running) | npx playwright install chromium && npm run e2e |
| Backend unit tests (inside the container) | docker compose exec iris iris session iris -U USER, then zpm "iris-flightdeck test" |
| Rebuild the committed frontend bundle | cd frontend && npm run build (verify with scripts/build/check-dist.sh) |
| Regenerate the capability map from the official spec | python3 scripts/build/gen-capability-spec.py (verify with scripts/build/check-generated.sh) |
| Regenerate the documentation images | cd frontend && FD_CAPTURE=1 npx playwright test --project=docs |
Specifications, plans and research for all six features live in https://github.com/kcedd34/iris-flightdeck/blob/001-foundation-shell/specs/. The project
constitution is in https://github.com/kcedd34/iris-flightdeck/blob/001-foundation-shell/.specify/memory/constitution.md.
FlightDeck implements an idea published on the InterSystems Ideas Portal:
_link to be added by the author before submission_.MIT. See https://github.com/kcedd34/iris-flightdeck/blob/001-foundation-shell/LICENSE.