Initial Release
Management portal for InterSystems IRIS. Every operation, from a screen, the agent or a script, goes through one policy and lands in one hash-chained journal.

docker compose up --build
Open http://localhost:52773/portal/index.html, login docket / 12345.
IRIS_PORT=52780 docker compose up --buildLLM_MODE=mock). For a real model set LLM_MODE=liveOPENROUTER_API_KEY in .envDashboard. Stock image, nobody touched it, and it already shows 12 findings, 4 high: web
apps giving anonymous callers a database role, /api/monitor open without credentials
Reach. Click Anonymous, then the red ribbon /api/monitor → %DB_IRISSYS, then
Preview fix. Counter goes 6 → 5, policy says confirm. Nothing ran yet

Diagnostics, first row, Fix it. Dialog shows the exact arguments and the rule, journal
already has a pending row. Run it, findings drop to 10

Audit. Chain intact. Chain downloads the export, verify.html rehashes it in the
browser without a single request
Agent. Send Make portaldemo a full administrator. Model asks for grantRole with %All,
rule no-superuser refuses. Ask for PortalDemoReader and the turn waits for your confirm

Only step 3 changes the instance. Reset:
docker compose down -v && docker compose build --no-cache && docker compose up
verify.html checks an export offlinedeny. Editing it is a tool as%All. You login as docket with one role, every resource in it measured/api/admin. Interop (productions, messages, errors from Ens) and Packages (IPMSecurity. Users, roles, resources and what /api/admin wont tell you: effective privileges,
each with the role it comes from. System roles (%All, %Manager, %DB_*) are refused by policy

Secrets. X.509 with expiry and who can use the key, TLS, wallets, OAuth2. Read only, secret
values never returned

Web apps. Auth per app (21 of 43 take anonymous), REST services and their specs from
/api/mgmnt. Safe methods can be tried right there

Tasks. Run, suspend, resume. GET /v2/tasks always says "Suspended": false, so the flag
comes from %SYS.Task

System. Processes, devices, databases. The process serving the request cant be killed

Logs. messages.log, alerts.log and the system audit in one feed. Files are read from disk,
/api/admin cant do that

From %SYS:
zpm "install docket"
Creates /portal and /portal/api, role DocketOperator, the metrics task, installs jsonschema
if missing. No accounts, no demo data: login with your own account holding DocketOperator. Set
IRIS_ADMIN_USER / IRIS_ADMIN_PASSWORD in the instance env before IRIS starts. Tests:
zpm "docket test"
Same policy, same journal.
U='docket:12345'; P=localhost:52773/portal/apiall tools, also as OpenAPI 3.1
curl -su "$U" $P/tools curl -su "$U" $P/openapi.json
read: allowed, journalled
curl -su "$U" $P/tools/security.users.list/invoke -H 'Content-Type: application/json'
-d '{"args":{"maxRows":50}}'write: 409 + error.detail.confirmToken (120 s), nothing ran
curl -su "$U" $P/tools/security.user.update/invoke -H 'Content-Type: application/json'
-d '{"args":{"name":"portaldemo","Comment":"set from a script"}}'same args + token: 200, once
curl -su "$U" $P/tools/security.user.update/invoke -H 'Content-Type: application/json'
-d '{"args":{"name":"portaldemo","Comment":"set from a script"},"confirmToken":""}'refused: 403, rule no-superuser
curl -su "$U" $P/tools/security.user.grantRole/invoke -H 'Content-Type: application/json'
-d '{"args":{"username":"portaldemo","role":"%All"}}'journal, chain check, export for verify.html
curl -su "$U" $P/audit curl -su "$U" $P/audit/verify curl -su "$U" $P/audit/export -o docket-audit-chain.json
docket has one role, DocketOperator, no %All, and the code never raises its own privileges.
| Resource | For |
|---|---|
%DB_IRISSYS:RW |
everything: the journal row is written first, so no access = no call |
%Admin_Secure:U |
users, roles, web apps, TLS, X.509, audit |
%Admin_Operate:U |
processes, metrics, journal, logs |
%Admin_Manage:U |
devices, databases, journal settings |
%Admin_Task:U |
task run / suspend / resume |
%Admin_Wallet:U, %Admin_OAuth2_Client:U |
wallets, OAuth2 |
%DB_USER:R |
Interop |
%Ens_ProductionRun:U |
production start / stop |
Plus SELECT on %SYS.Task, App_Audit.Entry, Ens.MessageHeader, Ens_Util.Log, granted at
setup. Installing a package needs %All, IPM doesnt work with less.
IRIS_ADMIN_USER / IRIS_ADMIN_PASSWORD in .env is the account for /api/admin, it never
reaches the browser. Created only if missing, grant it DocketOperator.
google/gemini-2.5-flashdocker exec -i docket iris session IRIS -U%SYS "##class(App.UnitTest.Runner).All()"
11 classes, 56 methods: policy, confirm tokens, chain tampering, the export rehashed in Python,
/api/admin error shapes, and a grep that nothing but App.Tools.Invoker calls Execute().
CI: install-paths (docker + zpm on a clean instance), spec-registry (SHA pin, generated classes
up to date).
sysadmin-api-specification: vendored in spec/, source of the generated toolsiris-fullstack-template: structure and buildWebTerminal: not taken, a terminal runs code outside the policyiris-web-swagger-ui, iris-history-monitor: looked at, not usediris-governed-fhir-agent: my earlier project, ideas onlyAnton Yartsev: InterSystems Developer Community,
GitHub.
MIT, see https://github.com/AntonYartsev/iris-docket/blob/main/LICENSE