> For the complete documentation index, see [llms.txt](https://docs.openiam.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.openiam.com/installation/rpm/rpm-upgrade/release-specific-steps.md).

# Release-specific steps

Releases that need more than the standard procedure — a database to provision, a service to deploy, an Oracle grant to make, or Groovy imports to update.

## What this is

Most upgrades need nothing beyond the procedure on the [minor](/installation/rpm/rpm-upgrade/minor-upgrade.md) or [major](/installation/rpm/rpm-upgrade/major-upgrade.md) page. Some releases add a database that must exist before you start, and a service to deploy once you are on it — neither of which the upgrade can create for you.

**Work out which releases you are passing through, then open the tab for each of them and take its steps.** Passing through counts even if you are not stopping there: going from 2026.4.2 straight to 2026.8.2 crosses `pbac` and `privacy`, and needs both.

## The databases each release adds

4.2.1.x ran on two databases: `openiam` and `activiti`. By 2026.10.1 there are nine.

<table><thead><tr><th width="126">Release</th><th width="144.39996337890625">Database</th><th>Service</th><th>Carries data forward?</th></tr></thead><tbody><tr><td><strong>4.2.2</strong></td><td><code>groovy</code></td><td><code>groovy-manager</code></td><td>Yes — see <a href="/installation/rpm/rpm-upgrade/after-a-major-upgrade.md"><em>After a major upgrade</em></a></td></tr><tr><td><strong>2026.3.1</strong></td><td><code>auditlog</code></td><td><code>audit-log-manager</code></td><td>—</td></tr><tr><td><strong>2026.5.2</strong></td><td><code>batchtasks</code></td><td><code>batch-task-manager</code></td><td>Yes, on a one-shot flag</td></tr><tr><td><strong>2026.6.1</strong></td><td><code>certification</code></td><td><code>certification-manager</code></td><td>Yes, on a one-shot flag</td></tr><tr><td><strong>2026.7.1</strong></td><td><code>pbac</code></td><td><code>pbac-manager</code></td><td>—</td></tr><tr><td><strong>2026.8.1</strong></td><td><code>privacy</code></td><td><code>privacy-manager</code></td><td>New capability, no legacy data</td></tr><tr><td><strong>2026.10.1</strong></td><td><code>reporting</code></td><td><code>report-manager</code></td><td>New capability, no legacy data — but reads the other databases, see below</td></tr></tbody></table>

Nothing new arrives with 2026.8.2 or 2026.9.2 — they extend what is already there. 2026.10.1 adds `reporting`, and with it the one grant an Oracle installation has to make by hand (below). A [major upgrade](/installation/rpm/rpm-upgrade/major-upgrade.md) from 4.2.1.x to 2026.10.1 crosses **all seven** rows above.

{% hint style="danger" %}
**Each new database is one more thing your backup has to cover.** A backup routine written against 4.2.1.x captures two databases out of nine. It will still complete, still report success, and no longer be a rollback position.
{% endhint %}

## Provisioning a new database

The shape is the same for every one of them. Substitute the database name from the table above.

{% stepper %}
{% step %}

### Create the database

**Before upgrading**, create the database or schema on your existing database server — MySQL, MariaDB, MSSQL, Oracle or PostgreSQL. Each service applies its own migrations on first start, so it needs the database to exist but not to be populated.
{% endstep %}

{% step %}

### Add the two properties

In `datasource.properties`, alongside the entries already there for `openiam` and `activiti`:

{% code overflow="wrap" %}

```properties
<name>.databaseSchema.name=<name>
jdbc.<name>.url=jdbc:mysql://${jdbc.host}:${jdbc.port}/<name>?autoReconnect=true&useUnicode=true&characterEncoding=utf8&connectionCollation=utf8_general_ci&serverTimezone=UTC
```

{% endcode %}

The file ships with commented blocks for Oracle, PostgreSQL and MSSQL — take the URL form from the block matching your database rather than adapting the MariaDB one.

{% hint style="warning" %}
**The new database also needs credentials, and how they are supplied is not documented.** Ask OpenIAM support how your deployment should hold them — treat any instruction to put a username and password directly into `datasource.properties` with suspicion, since the shipped file holds neither for the databases already in use.
{% endhint %}
{% endstep %}

{% step %}

### Deploy the service

Deploy it alongside `openiam-esb`. On first start it applies its own schema.
{% endstep %}
{% endstepper %}

Two of the seven then need a one-off data migration, and the newest needs a grant, below.

## Steps by release

Each tab holds the steps for one release. **Open every tab from the release after the one you are on through to your target** — the steps are cumulative, not alternatives.

{% tabs %}
{% tab title="2026.5.2" %}

### The batch task manager

The batch task subsystem — schedulers, initialisers, the `BATCH_CONFIG` and `BATCH_SCHEDULE` tables, and the Groovy task runners — moved out of `openiam-esb` into a standalone service. It scales independently, and a batch job failing no longer affects API traffic.

After provisioning `batchtasks` and deploying `batch-task-manager`, its first start applies the schema and copies existing `openiam.BATCH_CONFIG` rows across. The runner short-circuits once the target table has rows, so restarts are no-ops.

**`BATCH_SCHEDULE` is deliberately not migrated.** The scheduler rebuilds future runs from each task's cron expression, and historical schedule rows have no operational value after cutover.
{% endtab %}

{% tab title="2026.6.1" %}

### The certification manager

Certification — the configurations, campaigns, review items, and the campaign compute — moved out of `openiam-esb` into a standalone service with its own database. The Activiti review workflow stays in the `workflow` service; its certification steps delegate the compute over RabbitMQ.

To carry existing certification data forward, start `certification-manager` once with:

{% code overflow="wrap" %}

```properties
org.openiam.certification.migrate-from-openiam=true
```

{% endcode %}

On that first start the migrations create the `certification` tables, then the runner copies the data out of `openiam` **preserving primary keys** — in-flight Activiti workflows hold reviewer and campaign ids as process variables, so the ids must not change. `ACCESS_CERTIFICATION*` becomes `CERTIFICATION*`, and the `CERTIFICATION`-typed rows of `APPROVER_ASSOC` and `ESCALATION` become `CERTIFICATION_REVIEWER` and `CERTIFICATION_REVIEWER_ESCALATION`.

Check the migrated row counts, then set the flag back to `false`. Leaving it on is safe — it is a no-op once the target tables are populated.
{% endtab %}

{% tab title="2026.7.1 and 2026.8.1" %}

### PBAC and Privacy

Both arrive as their own service and database, and both are new capability rather than an extraction, so there is no legacy data to carry forward.

* **`pbac` (2026.7.1)** backs the policy-based access control console — policies with shadow, percentage and full rollout modes, a simulation runner, and a decision log. The evaluation microservice is separately deployable and scaled on its own.
* **`privacy` (2026.8.1)** backs Privacy Management — the consent ledger, the purpose and lawful authority registry, the privacy event backbone, and the Privacy Administration Console.

{% hint style="warning" %}
**Confirm these two with support before you plan the window.** Both are newer than the rest of this page, and whether either needs anything beyond the standard provisioning above — Privacy Management in particular, whose console may need seeding before it is usable — has not been established.
{% endhint %}
{% endtab %}

{% tab title="2026.9.2" %}

### The RabbitMQ broker encryption key

The key the services encrypt RabbitMQ message bodies with used to ship as a value in `rabbitmq.properties`, so every installation ran on the same key and anyone holding a copy of the distribution could read traffic off a broker. It is now a vault secret, and the upgrade script seeds one for you when vault does not already hold it.

**This is the one release-specific step that needs the platform stopped.** Every service resolves the same key, so a message written under the old key cannot be read under the new one. Anything still sitting on the broker when the key changes fails to decrypt rather than being processed.

{% hint style="danger" %}
**Messages queued under the old key are not recoverable.** Let in-flight work drain before you stop the services, rather than discarding it afterwards.
{% endhint %}

{% stepper %}
{% step %}
Stop the OpenIAM services, so nothing is publishing to or consuming from the broker. Leave RabbitMQ and Vault running — the next two steps need both.

{% code overflow="wrap" %}

```bash
systemctl stop openiam
```

{% endcode %}
{% endstep %}

{% step %}
Purge the OpenIAM queues. With no service running, everything left on them was encrypted under the old key and cannot be read back:

{% code overflow="wrap" %}

```bash
rabbitmqctl list_vhosts
rabbitmqctl list_queues -p <vhost> name messages
rabbitmqctl purge_queue <queue> -p <vhost>
```

{% endcode %}

The management console does the same thing per queue if you would rather see the depths before you clear them.
{% endstep %}

{% step %}
Run the upgrade. It reads vault, finds no key and generates one, which is all most installations need. To choose the key yourself — to match a value you already hold, or to keep several hosts in step — export it before running the upgrade and it will be used as-is:

{% code overflow="wrap" %}

```bash
export SECRET_ORG_OPENIAM_MQ_BROKER_ENCRYPTION_KEY=$(openssl rand -hex 16)
```

{% endcode %}

The upgrade only seeds a key when vault holds none, so it is safe to re-run and it will never overwrite a key you have already set.
{% endstep %}
{% endstepper %}

Vault stores it under `vault.secret.org.openiam.mq.broker.encryption.key`, which is what every service resolves at startup. The old `org.openiam.mq.broker.encryption.key` property is gone from `rabbitmq.properties`; a value left there is now ignored. Changing the key later means repeating all three steps.

### The RabbitMQ delayed-message plugin is retired

OpenIAM used a community plugin, `rabbitmq_delayed_message_exchange`, to hold a message back before delivering it. The plugin has no build past RabbitMQ 4.2, so keeping it meant the broker could never be upgraded again. It is gone, and the delay now comes from RabbitMQ itself: the message is published with an expiry onto a queue nothing consumes, and when it expires the broker forwards it to the queue that does the work.

**An exchange cannot change type once it exists.** Two exchanges on your broker — `AM_EXCHANGE` on the `openiam_am` vhost and `ELASTIC_SEARCH_EXCHANGE` on `openiam_common` — were created as delayed exchanges. The new services ask for ordinary ones, the broker refuses the mismatch, and the service stops with `PRECONDITION_FAILED`. Deleting the two exchanges is the whole fix; the services build them again, with their queues and bindings, the first time they start.

{% hint style="warning" %}
**`AM_EXCHANGE` carries the entire auth-manager vhost.** Deleting it takes every queue bound to it with it, so this belongs in a window where the services are down — which an upgrade already is.
{% endhint %}

**`upgrade.sh` does this for you**, before it starts the services and while the plugin is still loaded. Order matters there: RabbitMQ needs the plugin present to read an exchange of that type at boot, so the exchanges have to go first and the plugin second. Nothing is left for you to run.

To see what it will do before you commit to the window, run it against the broker yourself:

{% code overflow="wrap" %}

```bash
export RABBITMQ_USER=openiam
export RABBITMQ_PASSWORD=$(/usr/local/openiam/utils/vault/vault.fetch.property.sh vault.secret.rabbitmq.password)
/usr/local/openiam/utils/rabbitmq/migrate-delayed-exchanges.sh --dry-run
```

{% endcode %}

Re-running the upgrade is safe. The script deletes an exchange only while that exchange is still a delayed one, so once the services have rebuilt them it finds nothing to do.

#### Checking it worked

No vhost should have an exchange of type `x-delayed-message` left:

{% code overflow="wrap" %}

```bash
for v in $(rabbitmqctl list_vhosts --quiet); do
  rabbitmqctl list_exchanges -p "$v" --quiet name type | grep x-delayed-message && echo "  ^ in $v"
done
```

{% endcode %}

That should print nothing. You can also confirm the replacement is in place — `openiam_audit` gains a queue called `AuditExportDispatchRetryQueue`, which is the holding queue the expiry mechanism uses:

{% code overflow="wrap" %}

```bash
rabbitmqctl list_queues -p openiam_audit --quiet name | grep AuditExportDispatchRetryQueue
```

{% endcode %}

If a service still fails with `PRECONDITION_FAILED` naming an exchange, that exchange was not deleted. Run the script again without `--dry-run`, then restart the service.

{% hint style="info" %}
**Failed audit exports are retried properly now.** The retry interval was silently ignored before, because the audit exchange was never a delayed one — a failing export burned through its retries in a tight loop instead of pausing between them. `org.openiam.audit.export.retry.interval` now takes effect.
{% endhint %}
{% endtab %}

{% tab title="2026.10.1" %}

### The IdP stops publishing signing secrets

No database and no stop are needed, but three behaviours of the OAuth 2.0 identity provider (IdP) change, and an integration that depended on the old behaviour needs attention **before** you upgrade:

* **HS-signed clients publish an empty JWKS.** Before 2026.10.1 the JWKS URL of an `HS256` or `HS512` client returned the shared secret itself. Any relying party that fetched the secret from there must receive it out of band, or the client must move to `RS256` or `RS512` and the relying party must re-read the key set after the upgrade.
* **RSA clients publish only the public key.** The private exponent is no longer in the key set or in the IdP log. If an earlier release ever exposed it, regenerate the client's key pair as part of the upgrade.
* **`jwks_uri` in the discovery document is now correct.** Relying parties configured with the literal JWKS URL keep working; ones that were patched to strip the doubled issuer name can drop the patch.

One new property, `org.openiam.oauth.token.info.public`, ships `true`, so `/idp/oauth2/token/info` behaves exactly as before. Set it to `false` once every caller of that endpoint can authenticate as an OAuth client — see [OAuth endpoints, grants, scopes and signing](/administration-guide/federation/oauth-and-oidc/oauth-and-oidc-reference.md).

### The report manager, and the read grant Oracle cannot make for you

The browser-based report builder arrives as a standalone service, `report-manager`, with its own `reporting` database for report definitions, schedules and run history. Provision `reporting` the same way as the databases above — its properties are `reporting.databaseSchema.name` and `jdbc.reporting.url`, and the service resolves its credentials from `vault.secret.reporting.jdbc.username` and `.password`.

Deploy `report-manager` alongside `openiam-esb`, the same way `batch-task-manager` is deployed.

{% hint style="info" %}
**The `reporting` schema references rows in both `openiam` and `batchtasks`** (without foreign keys). Both those databases must exist and be populated before `report-manager` starts.
{% endhint %}

Two things set this release apart.

**The service reads every other database.** Reports are built over users, logins, certification items, policies, batch tasks and workflow requests, so `report-manager` also connects to `openiam`, `certification`, `privacy`, `pbac`, `batchtasks` and `activiti` — as its own read-only user, `REPORTING` unless you named it differently, taken from the same `vault.secret.reporting.jdbc.username`. It never writes to them. `datasource.properties` ships six `jdbc.reporting.source.<name>.url` entries, one per source database, which default to that database's own URL; point one at a read replica to keep reporting load off the primary.

**On Oracle, you grant that read access yourself.** MariaDB, PostgreSQL and SQL Server have a database-wide read grant, and the installer's database initialization makes it. Oracle has neither a schema-wide grant nor default privileges — `SELECT` is granted table by table, and the tables only exist once each schema's migrations have run — so nothing in the installation can grant it for you. Run the block below once after the upgrade, as a database administrator (DBA).

{% hint style="warning" %}
**Without the grant, every relational report fails.** The service starts, the catalog and the report designer work, but a run ends **Failed** with error code `QUERY_FAILED` and `ORA-00942: table or view does not exist`, and the service log shows the same error for the `report-source-*` connection pools. Reports over OpenSearch data — the audit log, orphan accounts, provisioning results — keep working, which is how to tell this apart from a connectivity problem.
{% endhint %}

{% stepper %}
{% step %}
Confirm the names you are granting between. The reporting user is in vault; the schema owners are the users the other services connect as, under their own vault properties:

{% code overflow="wrap" %}

```bash
cd /usr/local/openiam/utils/vault
./vault.fetch.property.sh vault.secret.reporting.jdbc.username        # the grantee — REPORTING by default
./vault.fetch.property.sh vault.secret.jdbc.username                  # openiam — typically IDMUSER
./vault.fetch.property.sh vault.secret.certification.jdbc.username    # typically CERTIFICATION
./vault.fetch.property.sh vault.secret.privacy.jdbc.username          # typically PRIVACY
./vault.fetch.property.sh vault.secret.pbac.jdbc.username             # typically PBAC
./vault.fetch.property.sh vault.secret.batchtasks.jdbc.username       # typically BATCHTASKS
./vault.fetch.property.sh vault.secret.activiti.jdbc.username         # typically ACTIVITI
```

{% endcode %}

Then check that the reporting user exists and is open — `SELECT username, account_status FROM dba_users WHERE username = 'REPORTING';` as `SYSDBA`. If that returns no row, create the user with the privileges the installer gives it, using the password held in vault as `vault.secret.reporting.jdbc.password`:

{% code overflow="wrap" %}

```sql
CREATE USER REPORTING IDENTIFIED BY <password>;
GRANT RESOURCE TO REPORTING;
GRANT CONNECT TO REPORTING;
GRANT CREATE VIEW TO REPORTING;
GRANT CREATE SESSION TO REPORTING;
GRANT UNLIMITED TABLESPACE TO REPORTING;
```

{% endcode %}
{% endstep %}

{% step %}
Grant `SELECT` on every table and view of the six schemas. Connect as `SYS` (or any user holding `GRANT ANY OBJECT PRIVILEGE`), edit the two lists at the top if your names differ, and run the block. It skips objects that are already granted, reports anything it could not grant, and is safe to run as often as you like:

{% code overflow="wrap" expandable="true" %}

```sql
SET SERVEROUTPUT ON

DECLARE
    -- the reporting user: vault.secret.reporting.jdbc.username
    c_grantee CONSTANT VARCHAR2(128) := 'REPORTING';
    -- the owners of the six OpenIAM databases report-manager reads
    TYPE t_owners IS TABLE OF VARCHAR2(128);
    v_owners  t_owners := t_owners('IDMUSER', 'CERTIFICATION', 'PRIVACY', 'PBAC', 'BATCHTASKS', 'ACTIVITI');
    v_granted PLS_INTEGER := 0;
    v_skipped PLS_INTEGER := 0;
BEGIN
    FOR i IN 1 .. v_owners.COUNT LOOP
        FOR obj IN (
            SELECT owner, table_name AS object_name
              FROM dba_tables
             WHERE owner = v_owners(i) AND table_name NOT LIKE 'BIN$%'
            UNION
            SELECT owner, view_name
              FROM dba_views
             WHERE owner = v_owners(i)
            MINUS
            SELECT owner, table_name
              FROM dba_tab_privs
             WHERE owner = v_owners(i) AND grantee = c_grantee AND privilege = 'SELECT'
        ) LOOP
            BEGIN
                EXECUTE IMMEDIATE 'GRANT SELECT ON "' || obj.owner || '"."' || obj.object_name
                                  || '" TO "' || c_grantee || '"';
                v_granted := v_granted + 1;
            EXCEPTION
                WHEN OTHERS THEN
                    v_skipped := v_skipped + 1;
                    DBMS_OUTPUT.PUT_LINE('Skipped ' || obj.owner || '.' || obj.object_name || ': ' || SQLERRM);
            END;
        END LOOP;
    END LOOP;
    DBMS_OUTPUT.PUT_LINE('Granted SELECT on ' || v_granted || ' object(s) to ' || c_grantee
                         || ', skipped ' || v_skipped || '.');
END;
/
```

{% endcode %}

A schema that does not exist on your installation — `PRIVACY` on a deployment without the privacy module, say — simply contributes no objects; leave it in the list or take it out, the result is the same.
{% endstep %}

{% step %}
Verify as the reporting user, then from the product. Each query must return a count, not `ORA-00942`:

{% code overflow="wrap" %}

```sql
SELECT COUNT(*) FROM IDMUSER.USERS;
SELECT COUNT(*) FROM CERTIFICATION.CAMPAIGN;
SELECT COUNT(*) FROM PBAC.PBAC_POLICY;
SELECT COUNT(*) FROM BATCHTASKS.BATCH_CONFIG;
SELECT COUNT(*) FROM ACTIVITI.ACT_HI_PROCINST;
```

{% endcode %}

Then open the report builder in the Webconsole (`/webconsole-v2/audit/reports`) and run a shipped system report that reads relational data, such as **Users** — it should complete. To review what was granted, as a DBA: `SELECT owner, COUNT(*) FROM dba_tab_privs WHERE grantee = 'REPORTING' AND privilege = 'SELECT' GROUP BY owner;`
{% endstep %}
{% endstepper %}

{% hint style="warning" %}
**Repeat the grant after every future upgrade.** A grant covers the tables that exist when it is made; a later release that adds a table leaves reports over it failing with the same `ORA-00942` until you run the block again. It skips what is already granted, so re-running it costs nothing. And keep the user read-only — `SELECT` only, never ownership of or write privileges on the OpenIAM schemas.
{% endhint %}

### Proxy and cookie settings changed

**`OPENIAM_TrustedProxy`** is a new setting — a comma-separated list of IPs or CIDR ranges from which `X-Forwarded-For` and client-IP headers are trusted. It ships **empty (no proxy trusted)**. If OpenIAM sits behind a reverse proxy, load balancer or CDN, add that address here; otherwise audit logs and IP-based rules record the proxy's address instead of the real client. (OE-4325)

**`OPENIAM_CookieIntegrity`** is now `on` in the shipped reference configurations. Environments that explicitly set this value keep their current value; environments that never touched it switch from `off` to `on`. Check whether anything relies on cookies being unprotected before upgrading. (OE-5251)

### PBAC seed for user management

A new PBAC seed (`user-management-seed-role9`) grants the Super Security Admin role every action under `Openiam::UserManagement::Action`. Any custom role that previously served as a de-facto user administrator must be granted a matching narrower PBAC policy, or those users will be refused on the user management screens. (OE-4550)

### SoD Groovy API changes

The SoD evaluator now **distinguishes pre-existing HARD violations from newly-introduced HARD violations** in its response. Any custom Groovy script that refuses a save on any HARD violation will now refuse legitimate saves — restrict the refusal to the newly-introduced set. (OE-5090)

SoD batch evaluation responses now include a **per-element status field**. Scripts that infer status from presence or absence of a result should switch to reading the explicit status field. (OE-5091)
{% endtab %}

{% tab title="2026.10.2" %}

### The IdP accepts Client ID Metadata Document clients

No database and no stop are needed. The OAuth 2.0 identity provider (IdP) gains a tenant-level metadata document, clients that register themselves from a Client ID Metadata Document (the way Claude and other Model Context Protocol clients identify themselves), the `resource` parameter and the `iss` response parameter. Four behaviours change for existing integrations; check them **before** you upgrade:

* **Public PKCE clients must use `S256`.** A client on `AUTHORIZATION_CODE_PKCE` with no client secret that sends `code_challenge_method=plain`, or no method at all, is refused. Fix the client, or set `org.openiam.oauth.pkce.plain.allowed=true` in `idp.ui.properties`.
* **Redirect parameters are URL-encoded.** A client that read `code`, `state` or `error` out of the redirect without decoding them now sees encoded values; a standard OAuth library is unaffected.
* **The `refresh_token` grant checks the client.** A refresh token presented by a client other than the one it was issued to, or presented a second time, is refused with `invalid_grant` instead of honoured.
* **Every authorization response carries `iss`.** A client that rejects parameters it does not expect on the redirect needs to accept it.

Three properties are worth a decision at upgrade time, all in `idp.ui.properties` under the deployment's `conf/properties` directory:

* `org.openiam.oauth.cimd.allowed.origins` ships as `https://claude.ai`, which lets Claude register itself as an OAuth client on a user's first sign-in. Set it to an empty value if you do not want that yet.
* `org.openiam.oauth.issuer` ships empty, so the issuer is derived from each request's host. Set it to `https://<your host>/idp/oauth2` when the IdP is reachable under more than one host name.
* `org.openiam.oauth.token.refresh.legacy.enabled` ships `true`, so the unauthenticated `/idp/oauth2/token/refresh` endpoint keeps answering. Set it to `false` once every client refreshes through `POST /idp/oauth2/token`; a later release defaults it to `false`.

Every property the release adds, with its shipped value, is listed in [OAuth endpoints, grants, scopes and signing](/administration-guide/federation/oauth-and-oidc/oauth-and-oidc-reference.md), and the procedure for letting an MCP client register itself is in [OAuth 2.0 and OpenID Connect](/administration-guide/federation/oauth-and-oidc.md).

### The MCP server

2026.10.2 adds the OpenIAM Model Context Protocol (MCP) server, which lets an AI assistant such as Claude answer questions from OpenIAM data on behalf of a signed-in user. **The RPM packages of 2026.10.2 do not include it**: it ships as the `mcp` container image and Helm chart only. To use it next to an RPM installation, run the container where it reaches the installation's RabbitMQ, Redis and Vault, make the installation's nginx forward `/mcp` and `/.well-known/oauth-protected-resource` to it, and follow [Connect AI assistants through the MCP server](/administration-guide/federation/mcp-server.md) for the gateway patterns and the graph rebuild. Nothing changes for an RPM installation that does not use it.

### MCP agents and tool-call policies

The second half of the MCP server in 2026.10.2 gives every MCP client an **AI Agent record** in the non-human identity inventory, lets an agent act with no person behind it, and puts every tool call through a policy-based access control (PBAC) decision. No stop is needed, and the migration runs with the release's others; nothing it adds is enforced until you switch it on. Three things change for an upgraded deployment:

* **The migration touches `openiam` and `pbac`.** `NHI_IDENTITY` gains the client binding, `NHI_DRIFT_FINDING` is new, `OAUTH_TOKEN` and `OAUTH_CODE` gain the audience, the consented scopes and the resource, the user type `NHI service account` is seeded, and the `pbac` database receives seven policies, `MCP-01` to `MCP-07`, active in shadow: they record what they would decide and change no decision until promoted. On SQL Server the binding is a filtered unique index, so a `sqlcmd` or `bcp` session that writes to `NHI_IDENTITY` needs `QUOTED_IDENTIFIER ON` (`sqlcmd -I`); JDBC, SSMS and the platform itself have it on.
* **Set `org.openiam.oauth.cimd.agent.owner` before the first Claude sign-in**, in `idp.ui.properties`, to the user ID of an administrator account. The IdP creates an AI Agent record for every client that registers itself and refuses to register one while the property is empty. A client registered before the upgrade receives its record on its next sign-in.
* **`client_credentials` tokens change subject.** A client bound to an AI Agent identity receives a token for the identity's service user, with its scopes filtered by that user's entitlements. A client that is not bound keeps the built-in `0001` subject only while `org.openiam.oauth.client_credentials.legacy.subject` is `true`, which 2026.10.2 ships; the next release ships it `false`, after which such a client is refused at the token endpoint. Find every client-credentials client you depend on and bind it before then.

Two properties are worth a decision at upgrade time:

* `org.openiam.mcp.agent.required` in the MCP container's `mcp.ui.properties` ships `false`, so MCP clients without a record keep working. Leave it for this release while the records are created, then set it to `true` so that an unregistered client is refused.
* `org.openiam.pbac.mcp.catalog.url` in `pbac-manager`'s properties ships empty. Point it at the MCP server's catalog document, `http://<MCP_CONTAINER_HOST>:8080/.well-known/mcp-catalog`, so policies can name tool groups; without it a policy can only name tools one by one.

Three more things are worth doing once the upgrade is through, none of them urgent:

* **Point Webconsole at the catalog too.** `org.openiam.mcp.catalog.url` in `webconsole.ui.properties` ships empty; the policy editor's autocomplete and the declared-tools picker offer no MCP vocabulary until it names the same catalog document.
* **Schedule the two new governance jobs.** The tool-drift job (`TOOL_DRIFT`) and the orphaned-sponsor job (`ORPHANED_AGENTS`) ship without a batch task; create one for each as described in [Schedule the jobs](/administration-guide/federation/mcp-server/mcp-agents.md#schedule-the-jobs). The sponsor job's mail template, `NHI_SPONSOR_INACTIVE`, is not seeded either.
* **Decide on approval.** `org.openiam.oauth.cimd.agent.approval` ships as `auto`. With `required`, a person's first sign-in through a new Claude client ends with `access_denied` until an administrator approves the agent record.

The record and the kill switch are in [Govern AI agents as non-human identities](/administration-guide/federation/mcp-server/mcp-agents.md); the modes, the no-policy rule and the order to promote the seven policies in are in [Decide MCP tool calls with policies](/administration-guide/federation/mcp-server/mcp-policies.md).
{% endtab %}
{% endtabs %}

## Custom Groovy scripts must be re-pointed

The extractions moved the base classes that customer scripts extend. **Scripts referencing the old packages will not compile until their imports are updated.** The scripts shipped under `conf/iamscripts/` were updated for you; anything you customised was not.

Find these before you upgrade, not after a batch job or a campaign fails to initialise.

<table><thead><tr><th width="237.800048828125">Old package</th><th width="271.99993896484375">New package</th><th>Moved in</th></tr></thead><tbody><tr><td><code>org.openiam.esb.core.batch.tasks.implementation.AbstractActivateProcess</code></td><td><code>org.openiam.batch.manager.initializer.implementation.AbstractActivateProcess</code></td><td>2026.5.2</td></tr><tr><td><code>org.openiam.esb.core.batch.tasks.implementation.AbstractLeaverProcess</code></td><td><code>org.openiam.batch.manager.initializer.implementation.AbstractLeaverProcess</code></td><td>2026.5.2</td></tr><tr><td><code>org.openiam.esb.core.batch.tasks.TerminatedUsersReportHelper</code></td><td><code>org.openiam.common.batch.TerminatedUsersReportHelper</code></td><td>2026.5.2</td></tr><tr><td><code>org.openiam.esb.domain.BatchTaskEntity</code></td><td><code>org.openiam.batch.manager.domain.BatchTaskEntity</code></td><td>2026.5.2</td></tr><tr><td><code>org.openiam.esb.core.batch.tasks.AccessCertificationHelper</code></td><td><code>org.openiam.certification.initializer.CertificationHelper</code></td><td>2026.5.2, and again in 2026.6.1</td></tr></tbody></table>

{% hint style="info" %}
**The certification base class moved twice.** It went to `org.openiam.batch.manager.initializer.AccessCertificationHelper` in 2026.5.2 and then to `org.openiam.certification.initializer.CertificationHelper` in 2026.6.1. If you upgraded through 2026.5.2 you may have already made the first move; the table shows the destination, not the intermediate stop.
{% endhint %}

More broadly, `org.openiam.esb.core.batch.tasks.*` moved wholesale to `org.openiam.batch.manager.initializer.*`, and the certification packages moved from `org.openiam.esb.*` to `org.openiam.certification.*`. The shared DTO contract is unchanged.

## Related

* [Minor upgrade](/installation/rpm/rpm-upgrade/minor-upgrade.md) — the standard within-line procedure
* [Major upgrade](/installation/rpm/rpm-upgrade/major-upgrade.md) — crossing to a different release line
* [After a major upgrade](/installation/rpm/rpm-upgrade/after-a-major-upgrade.md) — what to put right once the upgrade completes
* [Release-specific steps — Kubernetes](/installation/kubernetes/kubernetes-upgrade/release-specific-steps.md) — the same releases, configured through Helm

*Last reviewed 20 October 2026.*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.openiam.com/installation/rpm/rpm-upgrade/release-specific-steps.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
