---
title: "Platform Update — September 18, 2026"
slug: "platform-update-2026-09-18"
date: "2026-09-18"
tag: "Application"
summary: "Thirty-seven production releases in a week — the LLM model catalogue now auto-syncs behind a verification gate, credit is charged in real time with calls enforced against the balance, VM secrets let agents use credentials whose values never enter the sandbox, and the desktop app gains iOS and Android builds."
author: "ibl.ai Engineering"
linkedin: |
  Thirty-seven production releases in seven days — the LLM model catalogue now syncs itself.

  A 6-hourly task reads the gateway's model list and registers new models automatically, so a newly released model appears without a hand-edited file and a redeploy, under the company that authored it.

  Nothing goes live on trust: a discovered model stays inactive until a verification gate has tested multi-turn recall, streaming, tool calling, image input and a real, non-zero cost.

  The other headline is metering. Agent usage is now charged as it happens — chat per turn, calls while they run — with a 15-minute sweep collecting the rest. A call cannot start without credit and ends when the balance runs out.

  Also shipped this week across the ibl.ai platform:

  → VM secrets for agent sandboxes: a bound credential works against allow-listed hosts; the value never enters the guest
  → Per-agent file-upload extension restrictions, enforced at both upload paths
  → The desktop app gains iOS and Android builds with auto-update and per-platform model selection
  → Chat download — export a conversation as plain text, with a message scope picker
  → Watcher profile preview: a watched learner's enrollments and profile-card summary
  → Optional per-block scoring for Text and Video components in Studio, through the standard grading path
  → Google SSO with Google Workspace groups driving sign-in and RBAC membership
  → A declarative ModSecurity access-rule framework in the CLI, with report-only dry runs and allow-list mode

  That cadence is the point. When you run the platform yourself, upstream shipping velocity is something you receive rather than something you wait on — you pull the release on your own maintenance window.

  It is also why the ownership model matters. With ibl.ai you own all the code and the data — self-hosted inside your own perimeter, model-agnostic across any LLM, usage-based with no per-seat pricing, deployable anywhere from your own cloud to a fully air-gapped network.

  #iblai #AgenticAI #EnterpriseAI #LLMOps #ShipIt #DataSovereignty
---
Covers releases from **2026-09-11** through **2026-09-18** across `iblai/iblai-prod-images` (backend services) and `iblai/iblai-web-frontend` (web frontend monorepo). 37 releases total (17 prod-images, 20 web-frontend).

| Service | Image / Pin | Version Span |
|---|---|---|
| dm | `iblai-dm-pro` | `4.378.2-ai` → `4.385.0-ai` |
| os | `iblai-os-spa` | `0.142.3` → `0.150.2` |
| lms | `iblai-lms-spa` | `0.80.1` → `0.82.5` |
| edx | `iblai-edx-pro` | `sumac.2.59.1` → `sumac.2.62.3` |
| auth | `iblai-auth-spa` | `2.2.8` → `2.3.0` |
| cli | `ibl-cli` | `7.6.0` → `7.14.0` |
| web-containers | `@iblai/web-containers` | `1.20.0` → `1.21.0` |
| data-layer | `@iblai/data-layer` | `1.13.3` → `1.14.1` |
| auth (pkg) | `@iblai/auth` | `2.3.0` → `2.3.1` |
| web-utils | `@iblai/web-utils` | `2.4.6` → `2.4.7` |
| mcp | `@iblai/mcp` | `1.12.12` → `1.13.0` |
| iblai-js | `@iblai/iblai-js` | `2.11.0` → `2.12.2` |

---

## iblai-dm-pro (`4.378.2-ai` → `4.385.0-ai`)

**Auto-sync LLM model catalogue.** A 6-hourly task reads the gateway's model list and registers new models automatically, so newly released models appear without a hand-edited file and a redeploy. Each model is presented under the company that authored it. Discovered models are inactive until a verification gate has tested multi-turn recall, streaming, tool calling, image input and a real, non-zero cost; a model that fails stays hidden with the reason recorded. Auto-discovered providers now display the vendor's own capitalisation rather than a raw slug, and can carry a real logo sourced from the model author's public hub account (`manage.py backfill_provider_logos`). LLM search matches on provider as well as model name and offers a paginated `?view=table` shape. *(4.379.0)*
- **Migrations** `0366`–`0368`: additive, rolling-deploy safe.
- **Deploy:** `manage.py sync_llm_catalog --logos && manage.py verify_gateway_llm_models`. Recreate the ClickHouse cost-per-provider view: `manage.py create_clickhouse_views --action recreate --view mv_cost_per_provider_daily`.

**ibl.ai model availability and reasoning.** ibl.ai-funded tenants now see all available models in the model picker and can save them in mentor settings. ibl.ai models reason normally and stream thinking to the learner as it arrives (reasoning tokens are billed). The "show reasoning" switch in mentor settings now governs thinking visibility for every model, not just Claw mentors. Tenants using their own provider key keep calling that provider directly; funded tenants can be restricted to an agreed model list via `enforce_allowed_llms` (off by default), enforced in both the picker and at chat time. Realtime voice calls for tenants without their own provider key now fall back to inference mode instead of not connecting. *(4.379.0)*

**LLM usage analytics self-access.** The LLM usage analytics endpoint (`GET /api/v2/llm-usage/`) now allows self-access for non-admins, matching the sibling conversation/message analytics endpoints. A non-admin reads only their own usage; the row-scope resolver clamps the upstream `userId` filter to the caller. Platform admins, mentor owners and watchers keep their existing broader RBAC scope. *(4.380.0)*

**VM secrets for mentor sandboxes.** A tenant admin stores a credential as a `VirtualMachineSecret` (encrypted at rest, migration `0371`) and binds it to a mentor through the admin settings endpoint. The agent can use the credential (e.g. `curl -H "Authorization: Bearer $ACME_KEY" https://api.acme.com/...`) while `printenv ACME_KEY` prints only a placeholder. The value never enters the guest — the sandbox control plane substitutes it host-side on TLS requests to the secret's `allow_hosts` only. New CRUD endpoints at `…/orgs/<org>/virtual-machine-secret/` under `Ibl.Mentor/VirtualMachineSecrets/{list,write,delete}`. No endpoint ever returns the value. Binding a secret requires `virtual_machine_egress='public'`. VM output is scanned for bound values as a backstop. *(4.381.0)*
- **Deploy:** `ibl dm migrate`; `seed_rbac_data`. Requires the `iblai-sandbox` fix in `iblai/iblai-sandbox#5`.

**Global catalog search enrollment status.** The catalog search endpoint (`GET /api/search/catalog/`) now returns `is_enrolled` on each course/program/pathway result, resolved via one batched query per content type per page. Enrollment changes invalidate the user's cached catalog response, so the field stays fresh. *(4.382.0)*

**Real-time credit charging and call enforcement.** Mentor usage is now charged as it happens — chat per turn, calls while they run — with a 15-minute sweep collecting anything left over. Calls enforce credit: a call cannot start without credit and ends when the balance runs out (the client receives `{"event": "call_ended", "reason": "out_of_credit"}` on topic `ibl.call.event`). Admin, non-billing and auto-recharge exemptions still apply. Speech-to-text is now attributed to the user and priced on every provider. The credit ledger stores amounts to 6 decimal places. *(4.383.0)*
- **Migrations** `0093` (retypes credit columns with a 5s lock timeout; re-run if it times out) and `0094` (index added `CONCURRENTLY`).
- **Deploy:** `ibl dm migrate`. Ensure a Celery worker consumes the `credit_capture` queue. Run `manage.py manage_cost_rollups create-refresh && manage.py manage_cost_rollups run`. Run `manage.py manage_transcription_pricing`, then seed any model it lists as missing with `--model <name> --per-second <rate> --apply`.
- **Optional:** `ENABLE_MENTOR_CALL_CREDIT_ENFORCEMENT`, `MENTOR_CALL_CREDIT_BUFFER_USD` (default `0.50`), `MENTOR_CALL_CREDIT_CHECK_INTERVAL_SECONDS` (default `30`) — settable per platform in Global Configuration.

**Per-mentor file upload extension restrictions.** A new `allowed_file_upload_extensions` field on `Mentor` (migration `0372`, nullable JSON) lets a tenant restrict which extensions learners may upload, layered on top of the existing model-capability MIME check. Empty/null preserves the existing allow-all behaviour; values are normalized lowercase/no-dot/deduped. Enforced at both upload paths (REST presigned-URL returns `400`; websocket consumer skips the file with a `file_error`). Writable through the admin mentor-settings endpoint and the create-with-settings endpoint. Readable by the seeded **Students** role via `Ibl.Mentor/Settings/allowed_file_upload_extensions/read`. *(4.385.0)*

**Watcher profile preview.** Watchers can now read a watched user's course enrollments (`GET /api/catalog/enrollment/courses/search/`) and a new profile-card summary (`GET /api/core/users/profile/summary/` — name, username, email, avatar, bio, about, social links). Both endpoints validate the watcher relationship. *(4.378.3)*

**Spend-caps RBAC actions registered.** The `Ibl.Mentor/SpendCaps/{read,write,delete}` RBAC actions are now registered in the action-definitions endpoints across tenant, agent and per-user-agent resource paths. *(4.378.4)*

---

## iblai-os-spa (`0.142.3` → `0.150.2`)

**Chat download.** A new download control in the AI message toolbar lets users download a chat conversation as plain text, with a scope picker dialog for selecting which messages to include. *(0.145.0)*

**Backend-served LLM provider names and logos.** The frontend LLM provider map and bundled logos are removed. Provider names and logos are now rendered from backend data via a `useLlmProviderCatalogue` hook and a new `LLMProviderLogo` component, keeping the SPA in sync with the auto-discovered catalogue from dm 4.379.0. *(0.147.0)*

**Cloud storage picker credential gating.** The Datasets tab's cloud pickers (OneDrive, etc.) now report the tenant's credential state and disable pickers the tenant has no credential for, showing a "not configured" hint. *(0.146.0)*

**Embed controls consolidated into Settings.** The duplicated Embed controls are moved from a standalone tab into the Settings tab, preventing a save in one from overwriting the other. *(0.144.0)*

**Tauri iOS and Android.** The desktop app (Tauri) gains iOS and Android builds with auto-update, local LLM thread safety, responsive UI, and per-platform model selection. Releases app-v0.95.20 and app-v0.95.21. *(0.148.0, 0.149.1, 0.150.2)*

**Lesson completion time tracking.** `course_id` is now passed to the time tracker on lesson completion and grading events. *(0.150.0)*

**SSO redirect stability.** `redirect-to` is now read from localStorage first, with embed URLs overriding only when appropriate. *(0.146.1)*

**Language preference sync.** `LanguagePreferenceSync` renders at the top of the provider tree, ensuring language state is available to all child components. *(0.146.2)*

**Tenant switch auth handoff.** The SDK tenant-switch loader-hang fix is pulled in, completing the auth handoff during tenant switches. *(0.142.5)*

**CDN scheme defaulting.** A scheme-less `NEXT_PUBLIC_ASSET_CDN` now defaults to `https://` instead of producing invalid URLs. *(0.142.4)*

---

## iblai-lms-spa (`0.80.1` → `0.82.5`)

**edX JWT validation.** `hasNonExpiredAuthToken` now requires a valid edX JWT, tightening the auth check beyond a simple presence test. *(0.82.0)*

---

## iblai-edx-pro (`sumac.2.59.1` → `sumac.2.62.3`)

**Optional per-block scoring for Text and Video components.** Course authors can mark a Text (`html`) or Video component as Scored with a Problem Weight in Studio settings; completing it awards those points through the standard grading path (subsection and course grade). Components not marked Scored behave as before. New app `apps/ibl-edx-gradable-content-app`, installed in both PRO images via `apps.txt`. `IBL_GRADABLE_CONTENT_ENABLED` (default `True`) gates score publishing; requires the `completion.enable_completion_tracking` waffle switch. Usage and verification guide in the app's `USAGE.md`. *(sumac.2.60.0)*

**Google SSO with Google Workspace groups.** A new `googlegroup-oauth2` backend reads the user's Google Workspace groups on login and uses them to restrict who can sign in, link users to platforms as admin or non-admin, and manage custom RBAC group membership. Users outside the allowed groups are sent back to the SPA with an access-denied error. The existing Google sign-in is unchanged. *(sumac.2.62.0)*
- Bump `ibl-base-oauth-sso-backend` `1.11.1` → `1.12.0`: encrypted service account key upload in Django admin (new migration), dry-run mode for platform and group sync. Setup in the app's `USAGE.md`.

**Cross-provider SSO account matching.** Learners are now matched to their existing account by email regardless of which SSO provider they use, keeping their username, enrollments and progress. A new learner whose provider username is already in use gets a username built from their email. *(sumac.2.62.1)*

**SSO sign-in log detail trimmed.** The SSO callbacks and backends now record the step, provider, response status and the field names returned by the identity provider, instead of full request and response payloads. *(sumac.2.62.2–2.62.3)*

---

## iblai-auth-spa (`2.2.8` → `2.3.0`)

**Unauthenticated tenant redirect.** The deploy pin picks up `@iblai/auth` v2.3.0: when a user is not logged in, the auth SPA redirects to the tenant's authentication UI rather than a generic login page.

---

## ibl-cli (`7.6.0` → `7.14.0`)

**CLI-driven ModSecurity access rules (7.14.0).** A declarative access-rule framework for the native nginx ModSecurity layer, configured from `config.yml`.
- **`IBL_REVERSE_PROXY.MODSECURITY_ACCESS_RULES`** — inline allow/deny rules matching on host, path (prefix/exact/regex), HTTP method and client IP. `IBL_MODSECURITY_IP_WHITELIST` IPs always bypass deny rules.
- **`IBL_REVERSE_PROXY.MODSECURITY_ACCESS_RULE_FILES`** — per-host rule files for large or frequently-edited lists, with `{BASE_DOMAIN}` substitution for portability. Ready-to-copy examples in `ibl/data/rulesets/` (`dm.yml`, `edx-lms.yml`, `edx-studio.yml`).
- **`default_action: deny`** — allow-list mode: list the paths that stay reachable as `allow` entries; everything else returns 403.
- **`enforce: false`** — report-only dry run: denies are logged to the ModSecurity audit log but block nothing. Roll out in report mode, watch the log with `ibl global-proxy access-report`, add what is legitimate, then flip to `enforce: true`.
- **`mirror_alias: true`** — emits rules for both `ai-mentor` and `ai-agent` spellings on DM routes.
- **`vpn_only: true`** — scopes a rule to `IBL_REVERSE_PROXY.IBL_VPN_IPS`, so endpoints like `/admin` are VPN-only.
- **`IBL_REVERSE_PROXY.MODSECURITY_ENABLE_CRS`** (default `false`) — the OWASP Core Rule Set is no longer loaded by default; ModSecurity runs only as the engine for the explicit IBL rules, eliminating CRS false positives.
- **`ibl global-proxy access-rules`** — renders rules and reloads nginx. **`ibl global-proxy access-report`** — reads the audit log and groups hits by mode (report/deny), host and path prefix with client IPs.
- **`ibl global-proxy firewall`** — `status` shows what the rules are set to do; `mode off|report|enforce` sets it and applies in one step via `IBL_REVERSE_PROXY.MODSECURITY_MODE`.
- **`ibl global-proxy vpn-ips`** — show/edit VPN IPs with `--add`/`--remove`/`--set`, validated as IPs/CIDRs. Defaults to `IBL_MODSECURITY_IP_WHITELIST`.
- **Edge blocks marked.** Requests refused by ModSecurity return an `IBLAI-WAF: blocked` header and `Cache-Control: no-store` with a generic JSON body, distinguishing edge denials from application 403s.
- Hardened path matching with `urlDecodeUni` and `normalizePath`. Prefix paths ending in `/` now also match the bare form (e.g. `/api/foo/` also matches `/api/foo`). Generated rulesets are validated with `nginx -t` before reload.
- Rate-limit rules (ids 2002–2005) are removed — rate limiting is handled by nginx's `limit_req`.
- The DM example file is the real DM allow-list with rollout checklist in `docs/MODSECURITY_DEFAULT_DENY_ROLLOUT.md`. Operator manual: `docs/MODSECURITY_QUICKSTART.md`.

**CLI exit code propagation (7.13.0).** CLI commands now exit with the command's actual exit code, so `set -e` in deploy scripts trips correctly on failures. `ibl spa restart --all` stops at the first SPA that fails. Best-effort call sites (monitoring silencing, nginx choreography around container bring-up) keep their resilient behaviour.

**`IBL_SPA.MENTOR.PER_TAB_AUTH` type correction (7.13.0).** The setting is now `value_type=bool` rendered lowercase, so Jinja `{% if %}` evaluates correctly and the SPAs compare against the expected `"true"`/`"false"` strings.

**Vercel cost tracking settings (7.12.0).** New `IBL_DM.VERCEL_COST_TRACKING_ENABLED` and `IBL_DM.VERCEL_COST_TRACKING_INTERVAL_MINUTES` for the DM's Vercel cost tracking task.

**App site association for the os SPA (7.11.0).** The os SPA server block now serves `/.well-known/apple-app-site-association` and `/.well-known/assetlinks.json` as `application/json`, matching the mentor block. Gated on the existing `IBL_SPA.MENTOR.ENABLE_APP_SITE_ASSOCIATION` flag. Apply: `ibl config save && ibl global-proxy reload`.

**Google Groups SSO backend (7.10.0).** New `IBL_EDX.IBL_EDX_BASE_OAUTH_SSO_BACKEND.ENABLE_GOOGLE_GROUPS_SSO_BACKEND` (default `false`) appends the `googlegroup-oauth2` backend to `THIRD_PARTY_AUTH_BACKENDS`. Enable only on nodes running edX 2.62.0+. `IBL_GOOGLE_GROUPS_SSO_NAME` (default `googlegroup-oauth2`) configures the provider slug.

**Per-tab auth for mentor and os SPAs (7.9.0).** New `IBL_SPA.MENTOR.PER_TAB_AUTH` (default `false`) renders `NEXT_PUBLIC_IBL_PER_TAB_AUTH` into `.env.mentor` and `.env.os`, scoping sessions to each browser tab instead of sharing across tabs. One key shared by both SPAs.

**Sentry DSN for skills SPA (7.8.0).** New `IBL_SPA.SKILLS.SENTRY_DSN` renders `NEXT_PUBLIC_IBL_SENTRY_DSN` into `.env.skills` — the last SPA without error reporting. Defaults to empty (Sentry SDK disables itself).

**JSON output for services list (7.7.0).** `ibl services list` gains a `--json` option.

---

## @iblai/web-containers (`1.20.0` → `1.21.0`)

**Backend-served LLM provider naming and logos.** The frontend LLM provider map and bundled logos are removed. The LLM tab, evaluation tab, profile credentials, and Claw sandbox now render provider names and logos from `service_info` returned by the backend API. A new `LLMProviderLogo` component fetches backend-served logos. *(1.20.2)*

**User-Agent for document retraining.** The train-or-delete modal now includes an optional User-Agent input field. The selected User-Agent is sent as `crawler_extra_headers` when retraining a document, useful for sites that block default crawlers. *(1.20.3)*

**Watcher profile summary.** A profile summary view for watched users is now available, showing the watcher the profile details of their watched students. *(1.21.0)*

---

## @iblai/data-layer (`1.13.3` → `1.14.1`)

**`service_info` on credential schemas.** The `service_info` field is now typed on credential schemas, enabling the frontend to render backend-provided provider names and logos. *(1.13.4)*

**API client retry improvements.** 5xx responses are now retried in the queryFn loop, while 400, 401 and 403 responses are never retried. *(1.13.5)*

**Export surface gating.** A CI gate validates the published export surface, and dropped exports from earlier releases are restored. *(1.14.1)*

---

## @iblai/auth (`2.3.0` → `2.3.1`)

**Auth request cleanup.** Auth credentials are no longer sent on the `mfe_context` request. Logged errors are properly stringified. *(2.3.1)*

---

## @iblai/web-utils (`2.4.6` → `2.4.7`)

**Advertising-tenant redirect disabled.** The advertising-tenant redirect is turned off, preventing unwanted tenant switches when a tenant advertises itself in the SDK. *(2.4.7)*

---

## @iblai/mcp (`1.12.12` → `1.13.0`)

Rollup releases tracking upstream changes from `web-containers`, `data-layer`, and `auth` — backend LLM provider naming, User-Agent for document retraining, watcher profile summary, API retry improvements, and credential schema updates. No independent feature changes in this window.

---

## @iblai/iblai-js (`2.11.0` → `2.12.2`)

Rollup releases aggregating all monorepo package versions. 7 releases in this window; version bumps track the packages above. No independent feature changes.
