Home Applications MyOwn Portal

MyOwn Portal

by Murilo Przybyloviecz and 1 others
Community Project
This project is maintained by its author and is not officially supported by InterSystems. For technical support, please contact the project developer.
4.5
1 reviews
1
Awards
246
Views
4
IPM installs
2
Add to bundle
Details
Releases (5)
Reviews (1)
Awards (1)
Issues
Videos (2)
Articles (2)
This app has a demo View
An AI Hub agent inside an IRIS interoperability production.

What's new in this version

MyOwn Portal 1.1.0

Release for the InterSystems Programming Contest "AI Agents in Interoperability Workflows". 1.0.3 was a compact administration portal for InterSystems IRIS and IRIS for Health; 1.1.0 adds an AI agent that works inside an interoperability production. The agent decides and proposes; a person confirms every change with their own credentials.

  • Online demo: http://163.176.71.47:52774/myown/index.html (user demo, password demo; replay mode)
  • Video: https://www.youtube.com/watch?v=q7SpKb9q8BE
  • Developer Community article: to be added before release

What changed since 1.0.3

An AI agent inside an interoperability workflow

  • New production MyOwn.Production.Main. Business Services turn health anomalies, failing tasks, serious log entries and role requests into events. Business Processes ask the AgentGateway operation for a decision, run the Gate and store the proposal. Every step leaves a message or an Event Log entry with the trace id.
  • Two decision engines behind the gateway. The AI Hub engine (%AI.Agent with a read-only ToolSet, an allow-list authorization policy, an audit policy and its own deadline) runs on IRIS AI Hub builds. The Rules engine is always available. When AI Hub is missing, there is no key, the kill switch is on, the budget is spent or the provider fails, Rules decide, and the reason is recorded and shown (for example Rules (provider_timeout)).
  • The agent's tools are read-only class queries (GetTaskRuns, GetMetricHistory), and the ToolSet excludes every tool whose name does not start with Get.

Guardrails: the agent never writes

  • Validator. The model output must be one JSON object that matches schema v1: strict types, allow-listed fields and actions, bounded text, targets taken only from the candidates the server supplied. 92 hostile model outputs are in the adversarial test corpus.
  • Gate. Kill switch, read-only mode, prohibited targets (for example the %All role), high-risk decisions downgraded to escalation, one open proposal per target and action, and cooldown. The token budget is enforced separately (MyOwn.Agent.Policy.Budget, called by the agent service); over budget, Rules decide.
  • Human confirmation. A state change exists only as a proposal that expires. Confirming it revalidates everything: state, expiry, separation of duties (a requester cannot confirm their own request), prohibited targets, the target fingerprint (TARGET_CHANGED) and the confirming user's live permission. The change is then executed with that user's credentials, read back and audited (MyOwn/Agent/*).
  • Untrusted text (log lines, task errors, justifications) reaches the model only inside delimiters, sanitized.

Replay mode

  • MYOWN_AI_MODE=replay answers from real model decisions recorded earlier (Gemini gemini-3.8-flash and gemini-3.5-flash-lite), one fixture per scenario in aihub/fixtures/, each with its provider, model and recording date. It needs no key and no network, and the screen says "Replay of recorded real decisions". A scenario without a recording falls back to Rules (replay_missing); nothing is invented.

Demo mode and the demo user

  • MYOWN_DEMO_MODE=1 makes the portal safe to expose. The demo account (role MyOwnDemo) can view the portal, run the scenarios and confirm or reject proposals; it has no IRIS administration privilege.
  • Confirmations change only synthetic targets (demo tasks, users and roles) and end as simulated; IRIS itself is never changed. Routes that would change the real system are off for everyone. A reset task and a "Reset demo" button restore the synthetic state, and scenarios are rate-limited per client and session.
  • The demo password is generated by scripts/New-DemoPassword.ps1 into the git-ignored .env (MYOWN_DEMO_USER_PASSWORD) and never printed. Without it the demo account stays disabled.

Custom inbound adapters

  • TaskWatcher and LogWatcher use the portal's own adapters, MyOwn.Production.Adapter.TaskHistoryInbound and LogEntriesInbound (both Ens.InboundAdapter), with their settings in the production: TaskNamespace, HistoryRows, FailureThreshold, MinSeverity, MaxEntries.

Packaging and installation

  • One IPM module, myown-portal. The AI Hub engine is part of it and is compiled only where the %AI package exists; elsewhere the portal runs on Rules. The registry package contains no test classes.
  • Installing into a namespace without interoperability enables it first (MyOwn.Bootstrap). The agent production is set to start automatically and starts once the install finishes. Uninstalling stops it and removes the web applications and classes; roles, resources and data are kept.
  • Docker for IRIS AI Hub build 2026.3.0AI.154.0. The image build sets the production to start at boot, so a new installation has no error in its Event Log. An incomplete installation never starts. The ports are published on 127.0.0.1 only by default.
  • scripts/Test-All.ps1 runs every test suite, prints one table and exits with 1 when anything fails.

The portal itself (Overview with an Embedded Python health score and a Vector Search nearest-pattern match, Logs, users and roles through SysAdmin API v2, tasks, web applications, API explorer) is unchanged in purpose and still works without the agent.

How to install

Docker with IRIS AI Hub build 154 (recommended for the agent)

Load and tag the build 154 image once per machine, copy .env.example to .env (set your own MYOWN_DEMO_PASSWORD, and a key, or MYOWN_AI_MODE=replay), then:

docker compose -f compose.yaml -f compose.aihub.yaml up --build -d

Open http://localhost:52774/myown/index.html. The README has the full steps, the memory needed (tested with WSL limited to 8 GB on a 16 GB machine) and how to verify the installation.

IPM

When 1.1.0 is published in the community registry, in the namespace where the portal should live:

zpm "install myown-portal"

Until then the registry has 1.0.3; to install 1.1.0 from a clone, zpm "load /path/to/Portal-Iris".

Known limitations

  • Only the Gemini provider was used for real calls (gemini-3.8-flash and gemini-3.5-flash-lite, free tier). OpenAI, Anthropic and Bedrock go through the same %AI.Provider path but are untested.
  • Gemini free-tier latency was 12 to 83 s per decision, against the portal's 20 s default deadline, so live decisions often fall back to Rules (provider_timeout). The provider also retries HTTP 429 and 503 on its own before reporting them. The online demo therefore uses replay mode.
  • The S3 replay fixture comes from an earlier call with an equivalent, not identical, context (its header says so). The other scenarios use their exact context; S2, S4, S8 and S9 were recorded with gemini-3.5-flash-lite after gemini-3.8-flash timed out or answered 503 "high demand" on three re-recording attempts, and its S3 answer (ignore instead of a resume proposal) was not used.
  • The IRIS edition of build 154 was not tested; only IRIS for Health.
  • A viewer (MyOwnViewer) cannot file an access request: SysAdmin API v2 refuses it even its own account. The portal answers with a clear 403 and administrators file requests.
  • The image build needs about 2.3 GB inside the Docker VM at its peak, and loading the image archive also needs memory; with other programs open, Windows can run low.
  • The UI test suite needs Node.js; without it Test-All.ps1 skips that suite.
  • The default _SYSTEM password in .env.example is a well-known local convenience value. Change it before exposing the portal.

MyOwn Portal — AI Agents in Interoperability Workflows

MyOwn Portal is an administration portal for InterSystems IRIS and IRIS for Health. This entry adds an interoperability production in which an AI agent triages incidents and access requests: it reads the instance, decides and explains, and proposes a change, while a person confirms it.

  • What it does: health anomalies, failing tasks, log alerts and role requests become events in an IRIS production; the agent returns a decision (ignore, notify, escalate, propose a task action, approve or deny a role) with a rationale, evidence and a confidence value.
  • How it fits the theme: the agent is a step inside a real interoperability workflow (Business Services → Business Processes → Business Operations), built on IRIS AI Hub (%AI.Agent, ToolSets, policies) in build 2026.3.0AI.154.0.
  • What is technically interesting: the agent never writes. Every decision passes a schema Validator and a deterministic Gate; a human confirms with their own credentials; the change is revalidated, executed and read back. Read-only tools are enforced by an allow-list policy, every AI call lands in a token/cost/latency ledger, and when the model is slow, wrong or unavailable the deterministic Rules engine decides instead and the screen says why.

Links

Contest bonuses

What the project has for each technology bonus; whether it counts is up to the organizers.

Bonus Status Where to see it
Custom Adapter Two custom inbound adapters used in the production MyOwn.Production.Adapter.TaskHistoryInbound (src/objectscript/MyOwn/Production/Adapter/TaskHistoryInbound.cls, used by TaskWatcher) and MyOwn.Production.Adapter.LogEntriesInbound (LogEntriesInbound.cls, used by LogWatcher), both extending Ens.InboundAdapter
Embedded Python Used by the health score, anomaly detection and host metrics AnalyzeWithPython (health score) and DetectAnomaliesWithPython (z-score anomalies) in src/objectscript/MyOwn/Service/HealthAnalysis.cls; GetDiskUsage and GetPhysicalMemoryKB in src/objectscript/MyOwn/Service/System.cls
Vector Search Matches the current health vector to the closest stored pattern FindClosestPattern in src/objectscript/MyOwn/Service/HealthAnalysis.cls (VECTOR_COSINE over MyOwn.HealthPattern.MetricVector, a %Vector(DOUBLE, 6))
Docker container Dockerfile, compose.yaml and compose.aihub.yaml docker compose -f compose.yaml -f compose.aihub.yaml up --build -d (see Run it locally)
Online demo Live IRIS demo in replay mode http://163.176.71.47:52774/myown/index.html (user demo, password demo; see Demo mode)
Community idea DPI-I-767 “Automatically Expose Interactive API Documentation” implemented MyOwn.REST.Docs; live: http://163.176.71.47:52774/myown/api/docs (see API docs)
AI Hub bugs Two bugs reported ai-hub-eap-bugreports #5, #6
Embedded Python bug One bug reported python-bugreports #25

Try it in a minute

Run three scenarios on the hosted demo through the REST API, as the demo account (Windows PowerShell 5.1 or later):

.\scripts\Try-Demo.ps1 -BaseUrl http://163.176.71.47:52774

The same calls with curl.exe (from PowerShell; in cmd write the body as "{\"scenario\":\"S1\"}"):

curl.exe -s -u demo:demo -H "Content-Type: application/json" -d '{\"scenario\":\"S1\"}' http://163.176.71.47:52774/myown/api/agent/simulate
curl.exe -s -u demo:demo http://163.176.71.47:52774/myown/api/agent/decisions
curl.exe -s -u demo:demo http://163.176.71.47:52774/myown/api/agent/traces/

Output of a real run on 2026-10-29 (proposal numbers depend on what the demo already holds):

Server http://163.176.71.47:52774, AI mode replay, engine preference auto, model gemini-3.5-flash-lite.

Scenario Action Engine Tokens ToolCalls Proposal


S1 escalate aihub 587 0 - S2 propose_task_suspend aihub 1325 0 #2 task.suspend (pending) S5 approve aihub 571 0 #3 role.assign (pending)

Proposals stay pending until a human confirms or rejects them in Agents > Approvals.

The demo allows 6 scenarios per minute per address; beyond that the script reports HTTP 429.

Why this design

  • The agent only decides. It has read-only tools and no write path. A state change exists only as a proposal that expires.
  • Validator. The model output must be a single JSON object matching schema v1: strict types, an allow-list of fields and actions, bounded text, and targets taken from the candidate list the server supplied.
  • Gate. Kill switch, read-only mode, prohibited targets (for example the %All role), downgrade of high-risk decisions to escalation, one open proposal per target and action, and cooldown.
  • Budget. MyOwn.Agent.Policy.Budget, called by the agent service before each AI call, checks the daily token budget and the calls per hour; over budget, the Rules engine decides.
  • Human confirmation revalidates everything: state, expiry, separation of duties (a requester cannot confirm their own request), prohibited targets, the target fingerprint (TARGET_CHANGED) and the confirming user’s live permission. The change runs with that user’s credentials and is read back.
  • Untrusted data stays data. Log lines, task errors and justifications reach the model only inside delimiters, sanitized and with the delimiter characters escaped.
  • Always a working system. Without AI Hub, without a key, with the kill switch on, over budget, or when the provider fails, the Rules engine decides. The reason is recorded and shown as a badge such as Rules (provider_timeout).

Architecture

HealthMonitor · TaskWatcher · LogWatcher · AccessRequestService      Business Services
        │  event (context with untrusted data delimited)
        ▼
TriageProcess · AccessProcess                                       Business Processes
   1. decide (AgentGateway)  2. Gate  3. proposal (ProposalStore)
        │
        ▼
AgentGateway ─┬─ AI Hub engine: %AI.Agent + read-only ToolSet,      Business Operations
              │  allow-list policy, audit policy, own deadline
              └─ Rules engine (fallback, always available)
        │  Validator → Gate → proposal → human confirmation → read-back → audit
        ▼
REST /myown/api/agent/*  →  React UI: Decisions, Approvals, Access, Production, Guardrails, Settings

The production MyOwn.Production.Main has ten items:

Layer Name Class Role
Service HealthMonitor MyOwn.Production.Service.HealthMonitor Every 60 s reads the instance health report and sends an incident to TriageProcess when it finds anomalies; also the entry point of the demonstration scenarios
Service TaskWatcher MyOwn.Production.Service.TaskWatcher Through the adapter TaskHistoryInbound, every 120 s, reports tasks of MYOWN with 3 or more consecutive failures, or suspended with a last run that succeeded, to TriageProcess
Service LogWatcher MyOwn.Production.Service.LogWatcher Through the adapter LogEntriesInbound, every 120 s, sends up to 10 log entries of severity 2 or higher to TriageProcess
Service AccessRequestService MyOwn.Production.Service.AccessRequest Receives access requests filed in the portal and sends them to AccessProcess
Service ExecutionFeedbackService MyOwn.Production.Service.ExecutionFeedback Receives the result (executed or failed) of a confirmed proposal and sends it to Notifier
Process TriageProcess MyOwn.Production.Process.Triage Routes the incident with the rule MyOwn.Production.Rule.Routing (or drops it), then decide (AgentGateway) → Gate → proposal (ProposalStore); notifies Notifier when the AI budget is exhausted
Process AccessProcess MyOwn.Production.Process.Access The same three steps for access requests
Operation AgentGateway MyOwn.Production.Operation.AgentGateway Step 1: chooses the engine (AI Hub or Rules), validates the decision and writes the ledger
Operation ProposalStore MyOwn.Production.Operation.ProposalStore Step 3: re-checks the Gate on the final decision and creates the proposal (before/after diff, expiry)
Operation Notifier MyOwn.Production.Operation.Notifier Stores execution feedback and budget notifications

The production MyOwn.Production.Main running, with its five services, two processes and three operations active

The production MyOwn.Production.Main running, with all ten items active (Interoperability > Production Configuration).

Visual Trace of one session: IncidentEvent from HealthMonitor to TriageProcess, AgentRequest and AgentResponse with AgentGateway, ProposalRequest and ProposalResponse with ProposalStore, and the AgentResponse back to HealthMonitor

The Visual Trace of one message: from the business service HealthMonitor through TriageProcess to the agent operation AgentGateway, then ProposalStore, and the response back to the service.

The production runs inside IRIS and is viewed in the native Management Portal (Interoperability > Configure > Production); the hosted demo shows only the safe areas of MyOwn Portal.

Each event enters the production as a message sent by a Business Service, so its path through the process and the operations can be followed in the Visual Trace of the Message Viewer; the Gate runs inside the process and writes an Event Log entry with the same trace id. AgentGateway is where the AI Hub agent is called: it calls MyOwn.Agent.Service.Agent.Decide, which runs MyOwn.AIHub.Engine when it is installed and ready and the kill switch, the engine preference and the budget allow it; otherwise, or when the AI call fails or its reply fails the Validator, the Rules engine decides.

The AI Hub engine (aihub/src, part of the one IPM module myown-portal) is compiled only where the %AI package exists; elsewhere the portal runs on Rules.

Technologies used

Embedded Python ([ Language = python ] methods, standard library only), called on every health report: the Overview screen, the MyOwn Portal health snapshot task and the HealthMonitor Business Service of the production all go through MyOwn.Service.HealthAnalysis.GetReport.

Method File What it does
AnalyzeWithPython src/objectscript/MyOwn/Service/HealthAnalysis.cls:49 Weighted health score 0–100 from five normalized risks (disk, database, locks, alerts, processes), status Healthy / Attention / Critical, and the main risk factor
DetectAnomaliesWithPython src/objectscript/MyOwn/Service/HealthAnalysis.cls:176 z-score of each current metric against its own history (at least 5 snapshots); above 2 is a warning, above 3 critical. These anomalies are what the HealthMonitor sends to the agent
GetDiskUsage src/objectscript/MyOwn/Service/System.cls:62 Disk total, free and used percent of the data directory (shutil.disk_usage)
GetPhysicalMemoryKB src/objectscript/MyOwn/Service/System.cls:75 Physical memory, on Linux containers and on Windows

Vector Search. MyOwn.HealthPattern (src/objectscript/MyOwn/HealthPattern.cls:14) stores six known health patterns (healthy, disk pressure, database capacity, lock contention, active alerts, process pressure; written by the installer, Installer.cls:187-197), each as a %Vector(DOUBLE, 6) of the same normalized risks plus a healthy reserve, with a recommendation. GetReport turns the current metrics into the same six-dimension vector and picks the closest pattern with VECTOR_COSINE(MetricVector, TO_VECTOR(?, DOUBLE)) (HealthAnalysis.cls:87). The match and its similarity appear on the Overview and are part of the health context the agent receives. It is a small, explainable nearest-pattern lookup, not a document or embedding index.

Custom inbound adapters. The TaskWatcher and LogWatcher Business Services of the production use the portal’s own adapters, both derived from Ens.InboundAdapter and polled every CallInterval (120 s). The scan lives in each adapter’s OnTask, and the service only turns what it receives into an IncidentEvent. A failed query is written to the Event Log and retried at the next interval; the service keeps running.

Adapter File What it hands the service Settings (production)
TaskHistoryInbound src/objectscript/MyOwn/Production/Adapter/TaskHistoryInbound.cls One TaskState per task of the namespace that failed FailureThreshold times in a row (a run fails when its history Status is not 1), or that is suspended although its newest run succeeded TaskNamespace (MYOWN), HistoryRows (200 newest history rows), FailureThreshold (3)
LogEntriesInbound src/objectscript/MyOwn/Production/Adapter/LogEntriesInbound.cls One LogBatch with the newest sanitized log entries of at least MinSeverity, when there is any MinSeverity (2), MaxEntries (10)

Docker. Dockerfile builds on the build 154 image (ARG IMAGE), installs IPM, enables interoperability on namespace MYOWN, loads both modules (docker/iris.script) and refuses an image whose installation did not finish. compose.yaml publishes the web port 52774 and keeps data in a named volume; compose.aihub.yaml adds the AI Hub settings.

IPM module. One module, myown-portal (module.xml). MyOwn.Bootstrap (in preload/, compiled first) enables interoperability on the namespace before the Ens-based classes compile. The module installs the MyOwn package and copies the built React UI from web/ to the /myown web application. MyOwn.Installer.LoadAIHub compiles the AI Hub engine from aihub/src (engine, read-only tools and ToolSet, policies, replay fixtures) only where %AI exists. MyOwn.Installer.Setup seeds the health patterns and agent defaults, creates the MyOwn.* resources, roles and the /myown/api REST application, registers the agent audit events, schedules the health snapshot and agent retention tasks, grants SQL read access to the portal schemas and sets the agent production to start automatically. The test classes are not installed from the registry: MyOwn.AIHub.Test comes only in developer mode (-dev), and MyOwn.Test (Scope="test") is loaded by the Docker build itself (docker/iris.script), because the test scripts need both. The community registry has myown-portal 1.1.0; zpm "install myown-portal" was verified on a clean instance of the 2026.3.0AI.154.0 image, after zpm "enable -community" (see https://github.com/muriloprzy-code/Portal-Iris/blob/master/docs/PUBLISH-CHECKLIST.md).

Ideas Portal

Two ideas from the InterSystems Ideas Portal are implemented in this repository:

Idea Implemented in Where
DPI-I-767 “Automatically Expose Interactive API Documentation” this entry MyOwn.REST.Docs: /openapi.json and an interactive /docs for any %CSP.REST class (see API docs)
DPI-I-512 “add AI to Instance Monitor to prevent major incident” MyOwn Portal, for the earlier Build Your Own Management Portal contest, and kept here the AI Instance Monitor on the Overview page: health snapshots and Embedded Python anomaly detection against the instance’s own history (MyOwn.Service.HealthAnalysis, see https://github.com/muriloprzy-code/Portal-Iris/blob/master/docs/REFERENCE.md). Its anomalies are now also what the HealthMonitor service sends to the agent

API docs

Try it live: http://163.176.71.47:52774/myown/api/docs (user demo, password demo). It lists 56 operations; GET operations can be tried in the page.

The portal API documents itself: GET /myown/api/docs is an interactive page and GET /myown/api/openapi.json the OpenAPI 3.0 document, both behind the same sign-in as the rest of the API. This answers what the community still asks for in the Ideas Portal idea DPI-I-767 “Automatically Expose Interactive API Documentation”: a FastAPI-style /docs for %CSP.REST classes.

  • What IRIS already has, and why we did not reuse it for the page. %REST.API.GetWebRESTApplication (also behind /api/mgmnt) builds a Swagger 2.0 document from the UrlMap of a %CSP.REST web application: on this portal it found 51 paths with the /// comments and the method names. It needs the web application name and access to the security definitions (a read-only account cannot call it), produces Swagger 2.0, and lists no parameters. There is no interactive page.
  • What is ours. MyOwn.REST.Docs, a base class for %CSP.REST dispatch classes. Any class that extends it serves /openapi.json and /docs with nothing to declare. The spec is built on every request from the compiled class, so it always matches the code that is compiled (no test recompiles a class to check this). It reads the routes of XData UrlMap (:id becomes {id}), the /// comment of each route method (first sentence = summary), the query parameters each method reads (%request.Data("name", 1) or %request.Get("name")), and whether it reads a JSON body. The page is our own interactive viewer, not Swagger UI: inline HTML, CSS and script with a per-response nonce in a strict Content-Security-Policy, no CDN and no external file, so it works offline.
  • Read-only and safe. GET operations can be tried from the page with the browser’s own session; the others only show a curl example with placeholders. The document holds code metadata only (no configuration value, key or password), and the examples are synthetic (101, demo.alice, DemoOperator).
  • A class that overrides OnPreDispatch calls ##super after its own access checks, so the documentation follows the same rules. MyOwn.REST does: a viewer sees the docs, and in demo mode the demo account does too.

Tests: scripts/Test-ApiDocs.ps1. It checks that the document is valid and covers every UrlMap route of REST.cls, that /docs and /openapi.json answer 401 without credentials, that nothing leaks, and that a second class that only extends the base (MyOwn.Test.DocsSample, served by a temporary web application) gets its own /docs.

How the demo works

You pick a scenario (S1 to S10) on the Agents page. Each one injects a synthetic event into the production (HealthMonitor or AccessRequestService). The event goes through TriageProcess or AccessProcess to AgentGateway, where the agent decides (in the hosted demo, by replaying a real decision recorded earlier). The Gate checks the decision, and it may become a proposal. You, as the human, confirm or reject it. In the demo a confirmation changes only a synthetic record; IRIS is never changed.

In a local installation the scenarios are off by default: turn on Demo mode in Agents > Settings (as an administrator), or set MYOWN_DEMO_MODE=1 in .env, which turns on the whole public demo mode (the demo account, synthetic targets only, real-system routes off; see Demo mode). On the AI Hub build, with the engine preference on Automatic (the default): with MYOWN_AI_MODE=live and your own key, the model decides for real and the answer may vary; without a key the Rules engine decides; with MYOWN_AI_MODE=replay you reproduce the recorded decisions.

The Agents page, tab by tab

These are the tabs the demo account sees. Next to the tabs, Reset demo restores the synthetic state and Refresh reloads the data.

Tab What it shows
Overview The active engine (AI Hub or Rules), the number of decisions, pending proposals and the daily token budget. Below them, “Run a deterministic scenario”: choose S1 to S10 and press Run scenario; the result shows the outcome, the action, the engine, the trace id and an Open trace button.
Decisions Every decision: id, workflow, action, engine (with the fallback reason when Rules decided), risk, confidence, validation, proposal and time. Open shows the engine, risk, validation, confidence, Gate result, proposal, rationale, evidence and conditions, with View trace.
Approvals The proposals the agent created, with target, expiry, required privilege and how to undo. Pending ones have Confirm and Reject.
Observability Ledger totals (decisions, tokens, estimated cost, average latency) and the full timeline of one event (the trace).
Guardrails The four guardrails (human confirmation, read-only tools, untrusted output, separation of duties), the data that may be sent to the model and the data that never leaves IRIS.

Other accounts also see Access (administrators request an IRIS role there for the agent to analyze; viewers follow existing requests), and administrators see Production (start and stop the production, list its ten items) and Settings (kill switch, preferred engine, budgets, cooldown). The demo account does not see them.

What each scenario means

# Scenario What it shows
S1 Critical disk anomaly The AI escalates to a person; the Rules engine would only notify
S2 Task failing repeatedly Proposes to suspend the task (creates a proposal)
S3 Suspended task whose last run succeeded Proposes to resume the task
S4 Normal conditions Ignores it: no false alarm
S5 Low-risk role request Approves, and creates a proposal
S6 High-risk role request Escalates to a person
S7 Request for the prohibited %All role The Gate blocks it (the AI denies it)
S8 Prompt injection in a log line The text is treated as data, not as an instruction
S9 Prompt injection in a justification Same, and the AI denies the request
S10 Budget exhausted The Rules engine decides (reason budget); no AI call is made

A good first run: Overview, run S2 and press Open trace; then Approvals (confirm the proposal), then Decisions (Open on the decision). If a proposal for the same task and action is still pending, it is reused; if one was confirmed within the cooldown, the Gate creates no new one and says why.

Demonstration scenarios

Run them from the Agents page or with POST /myown/api/agent/simulate (an administrator with Demo mode on in Agents › Settings, or the demo account where MYOWN_DEMO_MODE=1; see Try it in a minute).

A live model’s decision can vary from one run to the next. Replay reproduces one real decision recorded earlier, and each recording names its model and date (last column).

# Scenario Rules engine Recorded AI decision (replay) Recorded with
S1 Critical disk anomaly notify escalate exact context, gemini-3.8-flash, 2026-10-24
S2 Task failing repeatedly proposal: suspend task proposal: suspend task exact context, gemini-3.5-flash-lite, 2026-10-28
S3 Suspended task whose last run succeeded proposal: resume task proposal: resume task equivalent context, gemini-3.8-flash, 2026-10-21
S4 Normal conditions ignore ignore exact context, gemini-3.5-flash-lite, 2026-10-28
S5 Low-risk role request approve, proposal created approve exact context, gemini-3.8-flash, 2026-10-24
S6 High-risk role request escalate escalate exact context, gemini-3.8-flash, 2026-10-24
S7 Request for the prohibited %All role blocked by the Gate deny exact context, gemini-3.8-flash, 2026-10-24
S8 Prompt injection in a log line treated as data, notify escalate exact context, gemini-3.5-flash-lite, 2026-10-28
S9 Prompt injection in a justification treated as data, escalate deny exact context, gemini-3.5-flash-lite, 2026-10-28
S10 Budget exhausted Rules decide (budget) not applicable: no AI call is made —

The Gate and the confirmation step apply to both engines.

Real events, no simulation

The scenarios S1–S10 are only a shortcut to trigger an event on demand. The production does not need them: three services watch the instance and send real events through the same pipeline.

Service Reads Every Creates an event when
TaskWatcher the task history and the tasks of the MYOWN namespace 120 s a task has 3 or more consecutive failures, or is suspended and its last run succeeded
LogWatcher the IRIS messages.log 120 s a recent entry has severity Error or Fatal
HealthMonitor the live health report and the snapshot history 60 s a metric has a z-score above 2 (needs at least 5 snapshots)

Access requests made on the Access tab are also real events: they enter through AccessRequestService.

These events follow the same path as the scenarios: Triage, AgentGateway, Validator, Gate, proposal and human confirmation. None of the watchers depends on Demo mode: the Demo mode setting in Agents > Settings only enables the scenario button, and the public demo mode (MYOWN_DEMO_MODE=1) also refuses confirmations on real targets.

Verified end to end with TaskWatcher: a task that always fails ran three times, the watcher raised the event by itself, the agent (Gemini, through the AI Hub) proposed to suspend it, and the proposal waited for a person. scripts/Try-RealTaskIncident.ps1 repeats this check. It needs the MyOwn.Test package, which the Docker build loads (the IPM install does not).

Safety in one screen

Guard What it does
The agent only decides It has two read-only tools (GetTaskRuns, GetMetricHistory). MyOwn.AIHub.Policy.ReadOnlyAuth denies any other tool call.
Proposal, then a person A change exists only as a proposal that expires (30 minutes by default, “Proposal lifetime” in Agents > Settings). It runs only after a person confirms it with their own credentials, and the confirmation revalidates state, expiry, separation of duties (a requester cannot confirm their own request), prohibited targets and the target fingerprint.
Validator and Gate The Validator accepts one JSON object of schema v1, with allowed fields and actions and a target from the server’s candidate list. The Gate applies the kill switch, prohibited targets (%All, _SYSTEM, the portal’s own roles, web applications and production), the downgrade of high-risk approvals to escalation, read-only mode, one open proposal per target and action, and the cooldown. Over the token budget, the Rules engine decides.
Untrusted data stays data Log lines, task errors and justifications are sanitized and reach the model only inside <untrusted_data> delimiters, with < and > escaped.
Audit Eleven IRIS audit events MyOwn/Agent/* (decision, proposal created, confirmed, rejected or expired, execution result, config change, kill switch, tool denied, demo refused, demo reset). MyOwn.AIHub.Policy.LedgerAudit, a %AI.Policy.Audit, records every tool call and denial in the ledger.
See it work S7: the decision denies a request for %All, and the Gate still marks the target as prohibited, escalates it and creates no proposal; an approval of %All is turned into an escalation the same way (cases ADV-R07 and ADV-R08 of the corpus below). S8 and S9 (prompt injection treated as data), and the Guardrails tab. scripts/Test-AgentAdversarial.ps1 runs the corpus of 92 hostile model outputs.

Code: Gate, prohibited targets and Validator.

Community contributions

  • ai-hub-eap-bugreports #5: %AI.Provider silently retries HTTP 429 and 503 (four requests per Chat), with no setting and no signal to the caller.
  • ai-hub-eap-bugreports #6: the session timeout_ms does not stop a slow provider request.
  • python-bugreports #25: a finite Python float outside about 1e-128 to 9.2e145 cannot be returned to ObjectScript, even as %Double.
  • Ideas Portal DPI-I-767, implemented in MyOwn.REST.Docs: an OpenAPI 3 document and an interactive /docs page for %CSP.REST classes; the portal API lists 56 operations (see API docs).
  • IRIS Agentic Dev 1.5.0 (MCP server iris-agentic-dev) was used to compile classes, run SQL and read the %AI signatures on the build 154 container.

Run it locally

Memory

Before you start: this was tested with WSL limited to 8 GB on a machine with 16 GB of RAM. Close other programs during both docker image load and the build: each of them left Windows with only about 250 to 300 MB free here.

The image build starts IRIS for Health, enables interoperability (which also loads the FHIR packages) and compiles the portal. Measured inside the Docker VM: about 1 GB in use before the build and about 2.3 GB at its peak; the running container uses about 1 GB. On the Windows side, a build started with 4.9 GB free dropped to about 250 MB free during the FHIR load and still finished. Lower WSL limits are not tested.

The WSL limits live in %UserProfile%\.wslconfig (run wsl --shutdown and restart Docker Desktop after changing it). The tested configuration:

[wsl2]
memory=8GB
swap=8GB

If docker compose up is stopped for lack of memory: close programs or raise memory= in .wslconfig, then run the same docker compose ... up --build -d again; Docker reuses the layers that finished. Closing the terminal does not stop a build already running inside Docker (docker buildx history ls shows it), so wait for it before starting another one. An image whose installation did not finish is never used: the build writes the marker docker/.install-complete only after the portal compiled, fails without it, and the container refuses to start an image that lacks it, with a message saying to rebuild.

Install

Image. InterSystems IRIS AI Hub Community build 2026.3.0AI.154.0 (IRIS and IRIS for Health are both accepted; this repository defaults to IRIS for Health). Download it from the AI Hub Early Access portal, then load and retag it once per machine:

docker image load -i irishealth-community-2026.3.0AI.154.0-docker.tar.gz
# prints: Loaded image: docker.iscinternal.com/docker-intersystems/intersystems/irishealth-community:2026.3.0AI.154.0
docker tag docker.iscinternal.com/docker-intersystems/intersystems/irishealth-community:2026.3.0AI.154.0 irishealth-community:2026.3.0AI.154.0

If docker image load prints a different name, tag that name instead (docker images lists it). Loading the 5 GB archive took 4 to 8 minutes here, and it needs memory too: Windows free memory fell to about 300 MB while it ran (see Memory).

For the IRIS edition, load iris-community-2026.3.0AI.154.0-docker.tar.gz, tag it iris-community:2026.3.0AI.154.0 and set IRIS_IMAGE in .env.

Configuration. Copy .env.example to the git-ignored .env and set:

Variable Meaning
MYOWN_AI_PROVIDER openai, anthropic, gemini or bedrock
MYOWN_AI_MODEL model name accepted by your key
MYOWN_AI_API_KEY your key; never committed, never shown by the portal
MYOWN_AI_MODE live (default) or replay (recorded decisions, no key, no network)
MYOWN_AI_TIMEOUT_SECONDS the portal’s own deadline per decision, 5 to 55 (default 20)
MYOWN_DEMO_PASSWORD initial _SYSTEM password of a new container volume; choose your own
IRIS_BIND_ADDRESS where the ports are published; default 127.0.0.1 (this computer only)

The defaults in .env.example are what we tested: provider Gemini, model gemini-3.5-flash-lite (free tier, tested on 2026-10-28; gemini-3.8-flash was used from 2026-10-21 to 2026-10-24 and later stopped answering). OpenAI (for example MYOWN_AI_MODEL=gpt-4.1-mini), Anthropic and Bedrock are accepted by the same %AI.Provider path but not tested by us; see Limitations. Without a key the portal starts and uses the Rules engine.

Start:

docker compose -f compose.yaml -f compose.aihub.yaml up --build -d

Open http://localhost:52774/myown/index.html (the full path: the container’s web server serves no default page for /myown/) and sign in with _SYSTEM and the password you set in MYOWN_DEMO_PASSWORD.

The default password in .env.example is for local use only; change it before exposing the portal. The ports are published on 127.0.0.1 only (IRIS_BIND_ADDRESS), so the portal is reachable from this computer and not from the network until you change that setting.

Verify (replace <password> with your MYOWN_DEMO_PASSWORD; Invoke-RestMethod -Credential does not work here, because Windows PowerShell sends the credential only after a challenge that IRIS does not send):

curl.exe -s -u "_SYSTEM:" http://localhost:52774/myown/api/health
curl.exe -s -u "_SYSTEM:" http://localhost:52774/myown/api/agent/status
$cred = Get-Credential _SYSTEM
.\scripts\Test-Agents.ps1 -BaseUrl http://localhost:52774 -Credential $cred -IrisImage irishealth-community:2026.3.0AI.154.0 -Edition irisforhealth
.\scripts\Test-Agents-AIHub.ps1 -BaseUrl http://localhost:52774 -Credential $cred
.\scripts\Test-AgentSecretScan.ps1

/health answers "status":"ok"; /agent/status shows aiHubAvailable 1 on build 154 and activeEngine rules until a key (or replay mode) is set. The secret scan covers tracked files in a git clone and every text file in a folder without git (a ZIP download), never .env.

Reinstalling over an earlier version. IRIS keeps its accounts, passwords and data in the Docker volume named by MYOWN_DATA_VOLUME (default myown-portal-data). An existing volume keeps the old accounts and passwords, and a new MYOWN_DEMO_PASSWORD is not applied to it. For a clean install, either set a new MYOWN_DATA_VOLUME in .env or remove the old volume first (this deletes its data):

docker compose -f compose.yaml -f compose.aihub.yaml down
docker volume rm myown-portal-data

The container is always named myown-portal-iris. If an earlier copy of the portal, in another folder, still has its container (even a stopped one), up builds the image and then fails with The container name "/myown-portal-iris" is already in use. Run docker compose -f compose.yaml -f compose.aihub.yaml down in that folder first (it keeps that folder’s volume), then the same up again; the image is not rebuilt.

Do not push the AI Hub image, or any image built on it, to a public registry. Share only the Dockerfile and Compose files.

Install with IPM (zpm)

On any InterSystems IRIS or IRIS for Health 2024.1+ with IPM, in the namespace where the portal should live:

zpm "install myown-portal"

On a new instance with no repository configured, run zpm "enable -community" first.

This installs the published version from the community registry: myown-portal 1.1.0, verified with zpm "install myown-portal" on a clean instance of the 2026.3.0AI.154.0 image (after zpm "enable -community"). To install the code of a clone instead, give IPM the folder as the server sees it: zpm "load /path/to/Portal-Iris". The test classes are not part of either install (the Docker build adds them; see docker/iris.script).

  • If the namespace is not interoperability-enabled yet, the install enables it first; on IRIS for Health that also loads the FHIR packages and takes a few minutes.
  • The AI Hub engine is compiled only on builds with the %AI package; elsewhere the Rules engine decides.
  • The agent production is set to start automatically and starts in the background once the install finishes.
  • Open <web server>/myown/index.html and give users MyOwnViewer or MyOwnAdministrator.
  • zpm "uninstall myown-portal" stops the agent production, clears its auto-start, and removes /myown, /myown/api and the classes. It keeps the roles, resources and the portal’s data.

Tested on build 154 (IRIS for Health) in a new namespace without interoperability: install from the code 105 s (most of it enabling interoperability), then package, uninstall with the production running and reinstall from the package twice (2 s and 8 s); the production was running after each install and the Event Log had no error.

Installing into a local IRIS instance is described in https://github.com/muriloprzy-code/Portal-Iris/blob/master/docs/REFERENCE.md.

Demo mode

The online demo runs with MYOWN_AI_MODE=replay: each AI answer is a real model decision recorded earlier, not generated live. Every fixture in aihub/fixtures/ names the provider, model and recording date; the screen shows the badge “Replay of recorded real decisions”. Every scenario except S3 now uses its exact context: S1, S5, S6 and S7 were recorded with gemini-3.8-flash (2026-10-24), S2, S4, S8 and S9 with gemini-3.5-flash-lite (2026-10-28, after gemini-3.8-flash stopped answering). S3 keeps its 2026-10-21 recording from an equivalent context: the flash-lite answer for S3 was ignore instead of a resume proposal, so it was not used. Replay still goes through the tool manager, policies, Validator, Gate and ledger.

The hosted demo shows only the safe areas (Overview, Logs, Agents) for the low-privilege demo account. Administration pages (users and roles, web applications, task actions, API explorer, Settings) are switched off in demo mode and answer 403; the full portal runs in a local installation with an administrator account. The demo runs on a live IRIS instance, not on recorded HTTP responses: only the model’s answer is replayed from a recording of a real decision. It is served over plain HTTP and its credentials are public by design.

Running the agent live requires a local installation with your own key (MYOWN_AI_MODE=live).

With MYOWN_DEMO_MODE=1 the portal is safe to expose:

  • Demo account. A user demo with the role MyOwnDemo: it can view the portal, run the demonstration scenarios, and confirm or reject proposals. It has no IRIS administration privileges, no Settings, no access to the provider configuration or key, and every administration route answers 403. Database and SQL read access are granted only inside /myown/api (application role MyOwnPortalData, shared with the viewer role). On the hosted demo its password is the public demo; if you host your own instance, generate yours with scripts/New-DemoPassword.ps1 (below).
  • Synthetic targets only. The scenarios use seeded demo tasks (9001, 9002), users (demo.alice, demo.bob) and roles (DemoOperator, DemoAuditor). A confirmation revalidates as usual and then changes only that synthetic record; the proposal ends as simulated, and IRIS is never changed. A confirmation on any other target is refused (DEMO_TARGET_REFUSED) and audited (MyOwn/Agent/DemoRefused). Prohibited targets such as %All stay blocked.
  • Real-system routes are off for everyone in this mode (users and roles, web applications, task actions, the API explorer), administrators included.
  • Reset. The synthetic state is restored every MYOWN_DEMO_RESET_HOURS (default 6) by the task MyOwn Demo reset, and by the “Reset demo” button that only the demo account sees; both are audited (MyOwn/Agent/DemoReset).
  • Rate limit. MYOWN_DEMO_SIMULATIONS_PER_MINUTE (default 6) scenarios per minute per client address and per session; beyond that the API answers 429 with Retry-After.

Instructions for whoever hosts the demo:

.\scripts\New-DemoPassword.ps1   # random password, written only to .env (MYOWN_DEMO_USER_PASSWORD), never printed
# then set MYOWN_DEMO_MODE=1 and MYOWN_AI_MODE=replay in .env
docker compose -f compose.yaml -f compose.aihub.yaml up --build -d

At every start docker/demo-start.sh creates or updates the demo account from the environment, and disables it when demo mode is off. Give visitors the user demo and the password from .env. Never publish the _SYSTEM password; set your own in MYOWN_DEMO_PASSWORD before the first start.

Tests

Run every suite once, with a final table (passed / failed / skipped per suite) and a non-zero exit code if anything failed:

.\scripts\Test-All.ps1 -BaseUrl http://localhost:52774 -Credential (Get-Credential _SYSTEM)

The UI suite needs Node.js on PATH or in .tools\node (tested with Node 24) and, the first time, network access to install the pinned Playwright 1.55.0 and its Chromium (an isolated browser, never yours). Without them Test-All.ps1 skips that suite and prints UI: skipped (<reason and how to enable it>); add -RequireUI to count that as a failure. The other suites need only Windows PowerShell and the running container.

Reports go to test-results/all/ (one log and JSON report per suite, plus test-all.json). The last row checks that the run left no error in the interoperability Event Log. -SkipEndToEnd skips the adapter end-to-end check (a real task failing three times, about 3 to 5 minutes); -DemoCredential adds the REST part of the demo suite when demo mode is on.

GitHub Actions (.github/workflows/checks.yml) runs on every push and pull request without secrets: the secret scan, a check that no .env, key or archive is tracked, module.xml and the compose files, and the front-end build. It does not compile the ObjectScript classes or run Test-All.ps1, because those need the AI Hub build 154 image, which must not be pushed to a registry; run them locally.

Suite Script IRIS 2026.2 (local) Build 154 (IRIS for Health)
Portal smoke scripts/Test-MyOwn.ps1 pass pass
SysAdmin API mutations scripts/Test-SysAdminMutations.ps1 pass pass
Agent REST scenarios scripts/Test-Agents.ps1 24 pass, 1 optional skip 24 pass, 1 optional skip
Adversarial corpus (92 hostile model outputs) scripts/Test-AgentAdversarial.ps1 92 / 92 92 / 92
Core and interoperability smoke scripts/Test-AgentSmoke.ps1 14 / 14, 19 / 19 14 / 14, 19 / 19
AI Hub engine (replay: timeouts, invalid JSON, provider error, budget, kill switch, blocked write tool, ToolSet Exclude, query tools, fixtures) scripts/Test-Agents-AIHub.ps1 skipped (no %AI) 20 / 20
Agents UI (isolated browser) scripts/Test-AgentsUI.ps1 15 / 15 15 / 15
Inbound adapters (failure counting, empty scan, query failure, window, hand-off, Event Log left clean; end to end: a real task fails three times and the TaskWatcher ends in a suspend proposal) scripts/Test-Adapters.ps1 8 / 8 (end to end not run) 9 / 9
API docs (OpenAPI 3 from the UrlMap and comments, every route, 401 without credentials, no secrets, a second class gets its own /docs) scripts/Test-ApiDocs.ps1 4 / 4 in-IRIS (HTTP part not run) 9 / 9
Viewer role without %All (screens allowed, administration refused) scripts/Test-PortalViewer.ps1 5 / 5 5 / 5
Public demo (synthetic targets, refusals, reset, rate limit; REST checks with the real demo account) scripts/Test-AgentDemo.ps1 8 / 8 in-IRIS (REST part needs demo mode) 16 / 16 with MYOWN_DEMO_MODE=1
Secret scan (tracked and staged files) scripts/Test-AgentSecretScan.ps1 pass —

Reports are written to test-results/. The full regression passed on a fresh build 154 container on its first run.

Limitations

  • Live calls are fragile on a free tier. Gemini on the free tier answered in 10 to 27 s when available (gemini-3.5-flash-lite, 2026-10-28; gemini-3.8-flash 11 to 18 s before it stopped answering), but it also returned HTTP 429 (quota) and 503 (“high demand”), or no answer at all. The %AI provider retries 429 and 503 internally, so they often look like a slow provider. The portal stops waiting at its own deadline and the Rules engine decides, with the badge Rules (provider_timeout) or Rules (provider_error).
  • The default deadline is shorter than scenario S2 on the free tier. S2 took 23 to 26 s with gemini-3.5-flash-lite, above the default MYOWN_AI_TIMEOUT_SECONDS of 20 s, so live it falls back to Rules with the reason provider_timeout. Set MYOWN_AI_TIMEOUT_SECONDS=40 to see the AI decision in that scenario. The engine always records the fallback reason (ledger, decision, badge).
  • Only Gemini was tested. OpenAI, Anthropic and Bedrock go through the same %AI.Provider code path but have not been run.
  • Only the IRIS for Health edition of build 154 was tested; the IRIS edition image was not available here.
  • One replay fixture, S3, still comes from an equivalent rather than identical context (see Demo mode). gemini-3.8-flash did not answer the re-recording on 2026-10-24, 26 and 28 (timeouts and 503 “high demand”); gemini-3.5-flash-lite answered the other scenarios in 10 to 27 s, but chose ignore for S3.
  • Agent audit records become visible in the audit log about a minute after the event (IRIS writes its audit buffer periodically).
  • The portal has no audit viewer yet, so there is no “agent events only” filter.
  • The hosted online demo is served over plain HTTP, with public credentials. The demo account exists only where MYOWN_DEMO_MODE=1 and a password are set.
  • A viewer (MyOwnViewer) cannot file an access request: the privilege delta is read through SysAdmin API v2 with the requester’s own credentials, and that API refuses a viewer even its own account (it needs %Admin_Secure, which a read-only role should not have). The portal answers ACCESS_REQUEST_NEEDS_SECURITY_READ and the Access tab explains it; administrators file requests.
  • The demo rate limit uses the client address seen by IRIS; behind a reverse proxy all visitors share one address.

AI Hub experience

What worked well:

  • %AI.Agent, sessions and GetStats() gave a working agent loop quickly; per-session token statistics feed the ledger.
  • ToolSets declared in XML, and authorization and audit policies attached to the tool manager, made “read-only tools” enforceable: a denied tool is logged through %LogDenial and never runs.
  • ToolMgr.ExecuteTool runs a tool through the same policies without a model, which made an offline replay mode and policy tests possible.

What was hard:

  • There is no structured-output API in ObjectScript, so the JSON schema is requested in the prompt and enforced by our own Validator.
  • The session timeout_ms does not interrupt a provider call in progress; the portal runs each decision in a separate job and stops it at its own deadline.
  • Provider options pass through extra_params at the top level of the request; Gemini rejected a native generationConfig and accepted reasoning_effort.
  • The provider retries 429 and 503 internally before reporting them, which hides the cause unless the %AI trace log is enabled.
  • ListModels() reports zero pricing, so cost needs a local price table.

IRIS Agentic Dev (MCP server iris-agentic-dev) was used to compile classes, run SQL and read the %AI signatures on the build 154 container. Details in https://github.com/muriloprzy-code/Portal-Iris/blob/master/docs/AI-HUB-NOTES.md.

The portal underneath

The agent workflow sits on MyOwn Portal, built for the earlier Build Your Own Management Portal contest: system overview and metrics, an AI Instance Monitor (Ideas Portal DPI-I-512), users, roles and permissions, web applications, tasks, logs and certificates, all through the official SysAdmin API v2 where an endpoint exists. See https://github.com/muriloprzy-code/Portal-Iris/blob/master/docs/REFERENCE.md.

Built with AI assistance

I’m an ObjectScript developer, and I used AI assistants as a pair-programming partner on this project. The idea, the safety design and every decision about what ships are mine; the assistant sped up the typing and the testing. I reviewed and verified the work.

Team

License

MIT

Made with
Install
zpm install myown-portal download archive
Version
1.1.003 Nov, 2026
Category
Technology Example
Works with
InterSystems IRISInterSystems IRIS for Health
First published
25 Sep, 2026
Last edited
04 Nov, 2026