
Initial Release
A browser-based administration console for InterSystems IRIS. IRIS Atrium is a
single, demo-ready Angular single-page app that inspects and operates an IRIS
instance through the platform’s native management APIs (security, tasks,
processes, web applications, OS and logs) — no SQL, no direct global access
from the browser, and no dependency on the vendor’s own Management Portal UI.
The console is organized into six sections:
mp ISOS package,%CSP.REST dispatch class mp.disp).web/).iris-atrium, GitHub Actions CI,%UnitTest.Manager suites under tests/UnitTest.Browser (Angular SPA)
│ HTTPS/HTTP, Basic auth on every request
▼
/webapps, /permissions, /security, /tasks, /system, /logs
│
▼
CSP web application "/csp/iris-atrium" (static SPA shell)
CSP REST web application "/api/mp" (mp.disp %CSP.REST router)
│
▼
mp.disp → mp.impl.{webapps, permissions, security, tasks, system, logs}
│
▼
IRIS native management APIs (what actually powers the console):
Security.Users / Security.Roles / Security.Resources / Security.Applications
Security.SSLConfigs / Security.X509Credentials / Security.Wallets / SSL
%SYS.Task + TaskHistory / %SYS.ProcessQuery / Config.Namespaces
%SYSTEM.OBJ (file system) / ^$SYSTEM files (messages.log, console.log)
%SYS.Configuration, HSM, etc.
The browser never talks to IRIS globals or SQL. Every panel is served by
/api/mp, a thin %CSP.REST facade (dispatch class mp.disp) that delegates to
one implementation class per domain. Each implementation maps the request onto
the corresponding InterSystems IRIS management API — the same classes the
native Management Portal uses — and returns a small JSON envelope to the SPA.
/api/mp, e.g. GET /api/mp/tasks.mp.disp (the %CSP.REST router) matches the path, checks that the caller%Admin_Task), and invokesmp.impl.tasks.List().##class(%SYS.Task).%ExistsId(), ##class(%SYS.Task).%OpenId(),##class(%SYS.Task).RunNow(), %SYS.TaskHistory) and shapes the rows.mp.util.json) and returned with thePOST /api/mp/login) validates the credentials against IRIS and theAuthorization: Basic <base64(user:password)>.localStorage (keys mp.credentials,mp.username) and an Angular HTTP interceptor re-attaches it to every call./api/mp route carries a%Admin_Manage, %Admin_Secure, %Admin_Task,%Admin_Operate) and returns 403 for callers who lack it.The 2026.1 community CSP gateway strips double quotes from POST/PUT bodies,
corrupting application/json payloads. The API therefore transports JSON as
base64 in a body64 query parameter (/api/mp/...?body64=<encoded>) with an
empty HTTP body. The SPA handles this transparently in api.service.ts.
| Section | SPA routes | /api/mp endpoints |
IRIS management API behind it |
|---|---|---|---|
| Web Apps | /webapps, /webapps/explorer |
/webapps, /webapps/installed, /webapps/spec |
SysAdmin /v2/web-apps, /v2/web-app |
| Permissions | /permissions |
/users, /roles, /resources, /users/:name/roles, /users/:name/enabled |
Security.Users, Security.Roles, Security.Resources |
| Security & Secrets | /security |
/security/credentials[/:type[/:alias]] |
Security.SSLConfigs, Security.X509Credentials, Security.Wallets, OAuth2 config |
| Tasks | /tasks |
/tasks, /tasks/history, /tasks/:guid/run, /tasks/:guid/suspend, /tasks/:guid/resume |
%SYS.Task, %SYS.TaskHistory |
| Operating System | /system |
/system/metrics, /system/disks, /system/namespaces, /system/processes, /system/processes/:pid/terminate |
SysAdmin /v2/namespaces, /v2/namespace, /v2/databases, /v2/database, /v2/processes, /v2/process, /v2/process/terminate; CPU/memory/uptime from /proc |
| Logs | /logs |
/logs/sources, /logs, /logs/export |
IRIS log files (messages.log, console.log, audit) parsed in ObjectScript |
src/mp/impl/admin.cls is the only outbound HTTP client in the package. It
targets the InterSystems SysAdmin API as published in
mainspec_v2.json
(base path /api/admin), speaks GET/POST/PUT/DELETE, and unwraps the
BaseResponse envelope — failures are read from status.Errors / status.summary
and surfaced as a %Status with the API’s own message.
Calls are made against 127.0.0.1 on the request’s own port, replaying the
caller’s HTTP Basic credentials, so a SysAdmin call is only permitted when the
caller already holds the matching %Admin_* privilege. Set MP_ADMIN_API to
point at a different base path.
The one exception is the OpenAPI spec proxy behind /api/mp/webapps/spec:
the SysAdmin API exposes no endpoint for a REST application’s OpenAPI
document, so that proxy still reads the native spec path through
mp.impl.admin.Raw().
The console requires InterSystems IRIS 2026.2 or newer. The SysAdmin API
reports its version at /api/admin (apiVersion), and every SysAdmin route the
console uses lives under /v2. On IRIS 2026.1 the same base path answers with
apiVersion: 1 and all /v2/* paths return 404, so the Web Apps, namespaces
and processes panels have nothing to talk to. tests/UnitTest/Admin/Integration.cls
asserts apiVersion >= 2 and fails loudly rather than passing on empty results.
Two details of the API are not what the published spec suggests, and both are
covered by the integration tests:
Type is not accepted in a PUT /v2/web-app body. The application type isDispatchClass; sending the field is rejected with #40307.ServeFiles is validated against the numeric value list ofSecurity.Applications (,0,1,2,3), not against the enum names the spec"Always" is rejected with #7205; write 1/0. Reading"No".ServeFiles is therefore translated in both directions by
mp.impl.webapps.ServingMode() (write) and mp.impl.webapps.Serving() (read).
Only Docker is required (Docker Desktop or Docker Engine + Compose). All other
tooling runs inside the container. Port 52773 must be free.
The base image is pinned to intersystemsdc/iris-community:2026.2, the first
community release that serves the SysAdmin v2 API. An older IRIS_IMAGE builds,
but the Web Apps, namespaces and processes panels will not work against it.
The InterSystems IRIS Community Edition license limits the runtime to a maximum number
of visible CPU cores (up to 20 for current community releases). On hosts that expose
more cores than the license allows, IRIS aborts at startup with “Invalid Community
Edition license, may have exceeded core limit”. Because this image boots IRIS during
docker compose build too, the build can hit the same failure.
This stack ships a self-contained fix — no license key, no Docker VM changes:
intersystemsdc/iris-communitylatest stream (valid, non-expired community license; overridable via theIRIS_IMAGE build argument, e.g. docker compose build --build-arg IRIS_IMAGE=...).scripts/iris-entrypoint.sh heads off /iris-main at build and runtime, wrappingtaskset CPU-affinity mask via scripts/limit-cores.sh so IRIS neverMP_MAX_CORES cores (default 19, i.e. under the 20-core ceiling).MP_MAX_CORES cores the wrapper is a no-op, so low-coreTo override the visible-core bound (must stay below the license ceiling for the fix to
hold):
MP_MAX_CORES=4 docker compose up -d
docker compose up -d --build
docker compose ps shows the iris service healthy.SuperUser / SYSBase image notes: the
latestcommunity stream runs a full CSP subsystem, so
class-dispatch web apps (including/api/mp) work over HTTP exactly as on a
licensed instance. Theintersystemsdc/iris-community:latest-em-zpmstream boots
with a limited CSP subsystem: static pages serve normally, but no web
application that dispatches to a class (DispatchClass) accepts requests — every
such endpoint answers401/403/404regardless of credentials. When that image
is selected viaIRIS_IMAGE, the portal’s HTTP API cannot be demonstrated over
HTTP; the%UnitTest.Managersuites still run in the terminal inside the image.
/csp/... applications with name, path, namespace and authentication. Open/api/mp), confirm thePurge Audit) and confirm a new Finished history entry./v2/processes endpoint. Terminating a process requires %Admin_Operatesource=Console filter, then Export theAll endpoints live under /api/mp and require HTTP Basic
credentials (Authorization: Basic <base64(user:password)>). The SPA obtains those
credentials from its login screen and sends them on every request.
| Method | Path | Purpose | Resource gate |
|---|---|---|---|
| POST | /api/mp/login |
Validate credentials | none |
| GET | /api/mp/session |
Current session | none (login) |
| GET | /api/mp/webapps |
List web applications | %Admin_Manage |
| GET | /api/mp/webapps/installed |
REST apps via SysAdmin /v2/web-apps |
%Admin_Manage |
| GET | /api/mp/webapps/spec?ns=&app= |
OpenAPI spec proxy | %Admin_Manage |
| POST | /api/mp/webapps |
Create web app (name in JSON) |
%Admin_Manage |
| PUT | /api/mp/webapps |
Modify web app (name in JSON) |
%Admin_Manage |
| DELETE | /api/mp/webapps?name= |
Delete web app | %Admin_Manage |
| GET | /api/mp/users, /api/mp/roles, /api/mp/resources |
List security objects | %Admin_Secure |
| PUT | /api/mp/users/:username/roles |
Replace role assignments | %Admin_Secure |
| POST | /api/mp/users, PUT /api/mp/users/:username, PUT /api/mp/users/:username/enabled |
User CRUD | %Admin_Secure |
| GET | /api/mp/security/credentials[/:type[/:alias]] |
Credentials dashboard | %Admin_Secure |
| GET | /api/mp/tasks, GET /api/mp/tasks/history |
Schedule + history | %Admin_Tasks |
| POST | /api/mp/tasks/:guid/run, PUT /api/mp/tasks/:guid/suspend, PUT /api/mp/tasks/:guid/resume |
Task control | %Admin_Tasks |
| GET | /api/mp/system/metrics, /disks, /namespaces, /processes |
OS dashboard | %Admin_Operate |
| POST | /api/mp/system/processes/:pid/terminate |
Terminate process | %Admin_Operate |
| GET | /api/mp/logs, /api/mp/logs/sources, /api/mp/logs/export |
Unified log viewer | %Admin_Operate |
Response envelope: 200 {...} on success; errors are { "error": "...", "message": "..." } with HTTP 4xx/5xx.
Note on request bodies. The 2026.1 community CSP gateway strips double quotes
from POST/PUT bodies, corruptingapplication/jsonpayloads. The API therefore
transports JSON as base64 in abody64query parameter (/api/mp/login?body64=<...>)
with an empty body; the SPA adds this automatically inapi.service.ts.
ObjectScript unit tests run inside the container (mirrors CI):
docker compose exec -T iris bash -lc 'iris session IRIS -U %SYS < /opt/mgmt/dev/scripts/run-tests.script'
scripts/run-tests.script runs the whole UnitTest tree. Most suites are pure
shape tests, but UnitTest.Admin.Integration issues real HTTP requests against
the live SysAdmin API — it creates, reads, edits and deletes a
/csp/mp-selftest web application and asserts apiVersion >= 2. It needs an IRIS
identity with %Admin_Secure, supplied through:
docker compose exec -T -e MP_SELFTEST_USER=SuperUser -e MP_SELFTEST_PASSWORD=SYS \
iris bash -lc 'iris session IRIS -U %SYS < /opt/mgmt/dev/scripts/run-tests.script'
Without those variables the live tests report a single skip and the rest of the
tree still runs. This split is deliberate: the shape tests stay fast and hermetic,
while the integration suite is what catches a wrong base path, a missing verb, a
dropped Content-Type or a payload the API rejects — none of which the shape
tests can see.
Angular unit tests run locally in web/:
cd web && npm ci && npm test -- --watch=false --browsers=ChromeHeadless
cd web
npm ci
npm run build # outputs to web/dist (not required for the Docker path)
The container bakes the SPA at build time; a local build is only needed to iterate.
src/mp/ ObjectScript backend (package mp)
disp.cls %CSP.REST router
Installer.cls USER-namespace + web-app bootstrap
impl/ Endpoint implementations (six areas + session)
util/ json + security helpers
web/ Angular SPA
tests/UnitTest/ %UnitTest.Manager suites (per portal section)
docker-compose.yml IRIS service, port 52773
Dockerfile node build stage + IRIS community stage
scripts/ Core-cap wrapper (limit-cores.sh) + entrypoint override
iris.script Container bootstrap
module.xml ZPM module metadata
| Symptom | Fix |
|---|---|
| Port 52773 in use | Change the ports mapping in docker-compose.yml |
401 Unauthorized on login |
Username/password must reference an existing IRIS user |
| Web Apps empty | Log in with an account holding %Admin_Manage (e.g. SuperUser) |
| Logs empty | Open Logs, check sources health; audit requires audit read rights |
| OS dashboard blank | Returns []/zeros for metrics the container cannot read (kept graceful) |
| Build or startup aborts: “Invalid Community Edition license, may have exceeded core limit” | Keep MP_MAX_CORES set (default 19); rebuild with docker compose build so the build-time boot runs under the core cap. See “IRIS Community Edition license & high-core hosts”. |
MIT — see https://github.com/guilhermendes1/iris-atrium/blob/main/LICENSE.