
2026-09-27 —
Verified IRIS 2026.2 reactor
This release makes the contest workflow reproducible and live-tested on IRIS 2026.2. The only supported change is DEPLOY_WEB_APP for /anvil-demo, with the declared USER namespace, Anvil.REST dispatch class, and enabled state.
Added
Fixed
Hardened
Verified
The live smoke path passed on IRIS Community 2026.2 (Build 221U): it rejected an out-of-scope proposal, confirmed a planned proposal was unverified, performed the official SysAdmin PUT and GET, sealed an independently checked receipt, replayed it without changing the receipt, and read process and task inventories.
Upgrade notes and limits
Pull the 2026.2 image and recreate the demo container before bootstrapping. The generated AnvilDemo account has %All and belongs only in the dedicated local demo container. Ports now bind to localhost by default.
The only mutation remains /anvil-demo. Process and task features are read-only; optional Sling/Oak mirroring is outside the mutation trust boundary.
Watch the live IRIS 2026.2 demo ·
View the verified cockpit and walkthrough
IRIS Anvil is a proof-first management cockpit for InterSystems IRIS.
See https://github.com/mcc0nnell/iris-anvil/blob/main/RELEASE_NOTES.md for the latest verified changes and
upgrade notes.
Administrative work is modeled as a deterministic reactor:
Validate → Resolve → Plan → Execute → Verify → Receipt
Every proposed change gets a canonical SHA-256 digest. Execution is restricted to an explicit allowlist, verification re-derives the proposal digest, and successful runs seal a separate execution receipt. Proposal commands support idempotency keys, and repeat execution returns the original sealed receipt rather than applying the change twice. Verified evidence can be mirrored into Apache Sling / Jackrabbit Oak.
The first real management cartridge is DEPLOY_WEB_APP. It is deliberately constrained to /anvil-demo, NameSpace=USER, DispatchClass=Anvil.REST, and Enabled=1. The browser authenticates through the official IRIS 2026.2 SysAdmin API at POST /api/admin/login, performs the change with PUT /api/admin/v2/web-app, and reads it back with GET /api/admin/v2/web-app. Anvil then independently rereads the application through ObjectScript, verifies the postconditions, and binds the observed state into the execution receipt.
Management is split into two surfaces. /anvil/api is the unauthenticated read/program feed. /anvil/admin uses IRIS password authentication and is the producer/mutation surface; the operator must authenticate with an IRIS account authorized for %Admin_Secure:USE. The browser asks for those credentials only when a reactor run is requested and retains them only in memory.
/api/admin SysAdmin API, authoritative runtime, ObjectScript verifier, ledger, receipt boundaryThe browser never receives Sling credentials.
The cockpit also includes a read-only operational inventory using the official GET /api/admin/info, GET /api/admin/v2/processes, and GET /api/admin/v2/tasks endpoints. This adds authenticated authority discovery, operating-system/process visibility, and task-management visibility without granting the observation control authority to mutate, suspend, terminate, or run anything.
The current demo allowlists only DEPLOY_WEB_APP for the exact target /anvil-demo. Anything else is rejected by the execution endpoint. The official SysAdmin API performs the mutation; Anvil will seal a receipt only after both the SysAdmin API readback and its independent ObjectScript readback match the declared state. The proposal digest is independent from the execution receipt so both intent and execution can be checked.
The contest container grants unauthenticated CSP requests the built-in %DB_USER role so the public demo can execute the REST class in the USER namespace. This is intentionally scoped to the dedicated demo container and should not be copied into a shared production IRIS instance.
Start the two runtimes:
docker compose -f docker-compose.anvil.yml up -d
Bootstrap the IRIS class and web applications:
./scripts/bootstrap.sh
For a fresh, disposable local demo container, create a randomly generated operator:
python3 scripts/create_demo_operator.py
The command prints the AnvilDemo password once. Enter that username and password
when the cockpit asks for IRIS credentials. This local demo account has %All in
the dedicated container; use an appropriately authorized account in any other
environment. Do not expose the demo ports to an untrusted network.
Open the cockpit:
http://localhost:52773/anvil/index.html
Click RUN REACTOR to exercise the bounded /anvil-demo change, or SCAN IRIS
to view live process and scheduled-task counts. Both controls require the operator.
The cockpit retains credentials and the SysAdmin bearer token only in the open page.
To reproduce the full server-side integration check, set the operator credentials
in the current shell and run:
export ANVIL_OPERATOR_USER=AnvilDemo
read -rs -p 'AnvilDemo password: ' ANVIL_OPERATOR_PASSWORD; echo
export ANVIL_OPERATOR_PASSWORD
python3 scripts/smoke_iris.py
unset ANVIL_OPERATOR_PASSWORD
The smoke check requires IRIS 2026.2. It tests a planned proposal cannot verify,
then exercises the official SysAdmin login, web-app PUT and GET, independent
ObjectScript receipt, idempotent replay, and read-only process and task endpoints.
It only changes /anvil-demo.
Sling runs on host port 8082. Its Oak repository is persisted by the named volume mounted at /opt/sling/launcher.
Load the six-phase deterministic plan:
curl http://localhost:52773/anvil/api/reactor/plan
Create a proposal:
curl -u 'operator:password' -X POST \
-H 'Content-Type: application/json' \
--data '{"action":"DEPLOY_WEB_APP","target":"/anvil-demo","payload":"NameSpace=USER|DispatchClass=Anvil.REST|Enabled=1"}' \
http://localhost:52773/anvil/admin/proposals
The cockpit then authenticates at /api/admin/login, executes the declared change using the official PUT /api/admin/v2/web-app, and confirms it using GET /api/admin/v2/web-app. Seal the returned proposal id after that readback:
curl -u 'operator:password' -X POST http://localhost:52773/anvil/admin/proposals/ID/execute
Verify it:
curl -u 'operator:password' http://localhost:52773/anvil/admin/proposals/ID/verify
A successful verification returns "verified":1 plus the proposal digest and sealed execution receipt.
scripts/sync_oak.py reads the IRIS ledger and mirrors only receipts that pass the current verifier. Credentials are supplied at runtime:
SLING_USER=... SLING_PASS=... python3 scripts/sync_oak.py
The helper writes web/oak-status.json after a successful sync. The cockpit uses that credential-free status file to distinguish IRIS ONLINE from IRIS + OAK.
Oak evidence is stored under:
/content/iris-anvil/receipts/
Each mirrored execution contains the IRIS state, action, target, proposal digest, execution receipt, verification flag, and source=IRIS.
web/echarts.min.js; the cockpit has no CDN dependency.scripts/bootstrap.sh is idempotent.docker-compose.anvil.yml pins IRIS Community to 2026.2 and Sling to 14.iris-anvil-net Docker network.IRIS Anvil contract workflow checks the required API and safety code paths on every push and pull request. Run scripts/smoke_iris.py for a live IRIS integration check.The implemented path is intentionally narrow but real:
PUT /api/admin/v2/web-app.GET /api/admin/v2/web-app, then independently re-derive and verify state and proposal digest in ObjectScript.The SCAN IRIS control authenticates through the same in-memory SysAdmin session and reports current live-process and scheduled-task counts. It performs no mutation and stores no credentials.