---
title: "Platform Update — September 25, 2026"
slug: "platform-update-2026-09-25"
date: "2026-09-25"
tag: "Application"
summary: "Ten services and packages moved in seven days — PII and PHI filtering now covers in-chat file uploads end to end, including image pixel redaction and PDF rebuild, every detection is recorded in a new read-only privacy-flags audit endpoint, and the CLI moves nginx to nginx.org 1.30.5 with per-service ModSecurity enforcement."
author: "ibl.ai Engineering"
linkedin: |
  Privacy filtering that stops at the text box is not privacy filtering. This week it stopped stopping there.

  PII and PHI detection on the ibl.ai platform now covers in-chat file uploads across every path a file can take to a model — text, Office documents, images and PDFs, on the OpenAI and Google multimodal routes, the graph and deep-agent routes, Claw, and the code interpreter.

  Images go through OCR and are redacted at the pixel level. PDFs are rasterized, redacted and rebuilt. The agent's own privacy_action and phi_action settings decide what happens next: block, redact, or allow.

  Privacy mode also got quieter. Conversation text now stays out of run tracking, the moderation log, application logs, Sentry events and Langfuse traces.

  And every detection is now evidence. A new read-only, platform-admin-scoped privacy-flags endpoint records each hit — from file uploads, prompt input, model output, code-interpreter extraction and memory retrieval — filterable by rail, action, source, agent, session and date. It stores entity types only, never raw values: an audit trail that is not itself a second copy of the data.

  Also shipped this week:

  → Named, platform-scoped VM network policies for agent sandboxes, with a host:port allow-list sent inline per session and a cross-tenant policy that fails closed
  → Username, email and display name on every agent and analytics endpoint that names a user
  → Content analytics sorted by enrollment, so the most-used courses and programs come first
  → OpenRouter provider preferences and explicit model candidate lists on the routing profile
  → A /connect page in the auth SPA for generating and managing API tokens
  → Chrome extension v1.1.1 — Cowork can drive the active browser tab, including inside iframes and open shadow roots
  → Gradebook in LMS analytics, and chat-history attachments rendered alongside message text
  → nginx from nginx.org pinned at 1.30.5-1 with njs and prebuilt ModSecurity, plus per-service DM and edX enforcement switches

  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 #DataPrivacy #PHI #DataSovereignty
---
Releases from **iblai/iblai-prod-images** (`1.233.0` – `1.255.0`) and **iblai/iblai-web-frontend** (`web-containers-v1.21.1` – `iblai-js-v2.20.0`), published Sep 18 – Sep 25 2026.

| Service / Package | Image / Pin | Version Span |
|---|---|---|
| dm | `iblai-dm-pro` | `4.385.0-ai` → `4.404.0-ai` |
| auth | `iblai-auth-spa` | `2.3.0` → `2.5.2` |
| os | `iblai-os-spa` | `0.150.2` → `0.155.2` |
| lms | `iblai-lms-spa` | `0.82.5` → `0.85.0` |
| cli | `ibl-cli` | `7.14.0` → `7.21.0` |
| web-containers | `@iblai/web-containers` | `1.21.1` → `1.27.0` |
| data-layer | `@iblai/data-layer` | `1.15.0` → `1.20.0` |
| web-utils | `@iblai/web-utils` | `2.4.8` → `2.5.1` |
| mcp | `@iblai/mcp` | `1.13.1` → `1.18.0` |
| iblai-js | `@iblai/iblai-js` | `2.12.3` → `2.20.0` |

---

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

**Privacy & PII/PHI compliance.** PII/PHI filtering now covers in-chat file uploads — text, Office docs, images (Presidio OCR + pixel redaction) and PDFs (rasterize → redact → rebuild) — across every file→LLM path: OpenAI/Google multimodal, graph/deep-agent, Claw, and code-interpreter.

The mentor's `privacy_action`/`phi_action` governs behaviour (`block`, `redact`/`mask`, `allow`); grading opts out. Image rebuild requires `presidio-image-redactor` (new dependency).

Privacy mode now also keeps conversation text out of run tracking, the moderation log, application logs, Sentry events and Langfuse traces (`PRIVACY_MODE_OMITTED_TEXT_MARKER` controls the placeholder, default `[redacted: privacy mode]`).

**Privacy flags audit endpoint.** Every PII/PHI detection — file uploads, text-prompt input, LLM-response output, code-interpreter text extraction and memory retrieval — is recorded as a `PrivacyFlag` alongside the existing `ModerationLog`.

`GET …/privacy-flags/` (list) and `GET …/privacy-flags/<id>/` (detail), read-only, platform-admin scoped, tenant-scoped by `platform_key`. Filterable by `rail` / `action_taken` / `source` / `mentor` / `session_id` / `username` / `date_created` range.

Flags store entity *types* only (e.g. `["EMAIL_ADDRESS", "US_SSN"]`) — never raw values. RBAC actions `Ibl.Mentor/PrivacyFlags/{list,read}` granted to Mentor Viewer, Mentor Editor, Limited Editor and Limited Viewer. **Deploy:** `ibl dm migrate` (migrations `0376`, `0377` backfill); `seed_rbac_data`.

**User identity across all reads.** Every mentor and analytics endpoint that names a user now carries `username`, `email` and `display_name`. New fields use the prefix their payload already uses (`owner_username` → `owner_email`/`owner_full_name`); nothing is renamed.

Coverage spans memories, memsearch, spend caps, moderation/safety logs, chat-run trackers, hosting, projects, workflows, artifacts, connected services, MCP servers, course creation, disclaimers, periodic agents, trained documents, voices, chat feedback, AI-generated images, paywall payments, evaluations, analytics transcripts/chat-history/cost-per-user/top-students, and user-scoped finance invoices. The mentor audit log gains `actor_full_name`.

**Content analytics improvements.** Content listings (`GET /api/analytics/content/`) now default to descending total-enrollment order instead of alphabetical — the most-used courses, programs and pathways appear first.

Explicit `sort_by=enrollments` (new), `sort_by=name` (alphabetical), `sort_by=active_enrollments` (unchanged). Program results include `program_id` and `program_key`; `id` is deprecated for programs.

**Catalog search.** The `price=free` filter now matches the facet count, and the documented `self_paced`/`courseformat` parameters both filter correctly (previously one spelling was ignored). Course-format filtering now returns what the format facet counted.

Catalog search enforces agent-level access checks consistent with the dedicated agent search endpoint. Two requests that filter on different values of the same parameter no longer share a cached response.

Course-completion embeddings drop the block-level outline (previously 40k–147k tokens per embed, ≈$470 / 90 days across tenants); `ENABLE_COURSE_COMPLETION_OUTLINE_EMBEDDING` (per-platform, IBL-set, default off) restores it.

**Global mentor search.** `V2GlobalMentorSearchView` correctly unions main-tenant `VIEWABLE_BY_ANYONE` mentors when `include_main_public_mentors=true` and the requesting tenant is not main.

**LLM usage analytics.** Traces (`GET /api/analytics/llm-usage/?resource=traces`) now include `userFullName` and `userEmail`, resolved from the username. Watchers can view a watched learner's LLM usage with a new `IsAnalyticsWatcher` permission; access is clamped to the caller's watched set.

Conversation list items expose `llm_provider` alongside `model`; document source counts are now accurate and document `source` URLs regenerate at read time so they stay valid.

**OpenRouter custom parameters.** `IBLAIRoutingProfile` gains `provider_params` (OpenRouter `provider` preferences such as `quantizations`), `allowed_models` / `fast_allowed_models` (explicit candidate lists, wildcards allowed).

`LLMModelRegistry` gains `gateway_params`, sent only when the model is served through the gateway. Admin-only; unknown keys are rejected. **Deploy:** `ibl dm migrate` — migrations `0379`, `0380` (additive, blue-green safe).

**Embedding provider selection.** With no explicit `UseMainLLMKey.embedding_provider`, embeddings resolve: tenant's own `iblai` key → own `openai` key → gateway. `azure_openai` and `google` embed only when explicitly configured. The `google` provider accepts `gemini_google_api_key` (Gemini API), embedding with `gemini-embedding-001` at 768 dimensions on both Gemini API and Vertex.

**Deploy:** tenants with `google` configured explicitly and documents indexed on Vertex `text-embedding-005` may need re-indexing; tenants with only a Google key and no `embedding_provider` should either pin to `google` or re-index on the gateway.

**Sandbox VM.** Named, platform-scoped VM network policies (`orgs/<org>/virtual-machine-network-policy/`, admin-only) and a `custom` egress profile on mentor settings. The policy's `host:port` allowlist is sent inline to the sandbox per session; a missing or cross-tenant policy fails closed.

VM secrets can source values from integration credentials (`source_credential_id` + `source_field`), read at runtime so rotation propagates. Sandbox runtime is billed at `SANDBOX_COST_USD_PER_10_MINUTES` (Config, default `$1`, platform-overridable, `0` disables), prorated per second, charged at session end and recorded as the Langfuse `sandbox_session` cost.

**Deploy:** migration `0381` (additive). Requires iblai-sandbox with inline `egress_allowed_hosts`.

**Hosting.** Deployments now land on a real address and domains their holder cannot serve are freed. Vercel billing tracks near-real-time costs with verified deltas and deletion-proof attribution. **Deploy:** migrations required.

**SEO & marketing.** Mentor SEO tags and marketing conversations generate only on advertising platforms and on `iblai-fast`, requesting just the needed fields. Guided prompts send the last four messages truncated instead of the whole conversation.

**Artifact-only replies** (canvas mode) are now kept in chat history as `[Artifact: <title>]` so they link to the turn and appear in message endpoints.

**Legacy cleanup.** Removed `ibl-axd-search-app` (superseded by `ibl-dm-search-app`). The app was gated behind `ENABLE_IBL_SEARCH_APP` (default `False`, unused in any environment). Endpoints removed: `/api/search/search/`, `/api/search/documentsearch/`, `/api/search/es-health/`, `/api/search/search/personalized/…`, `/api/search/ai-search/detail/`.

**Platform-key billing.** Catalog embedding and catalog search now charge under the platform *key* instead of the org. `manage.py rectify_trace_platform_keys audit` → `retag --apply` → `rebuild-rollups --apply` corrects history.

**Scheduled tasks.** Course-invitation auto-enrollment, expired program/course-enrollment cleanup, and Stripe checkout-session cleanup now register and run on schedule. Controllable via `CATALOG_COURSE_ENROLLMENT_AUTO_UNENROLL_ENABLED`, `CATALOG_INVITATION_COURSE_AUTO_ENROLL_ENABLED`, and `STRIPE_CHECKOUT_SESSION_CLEANUP_ENABLED`.

**File-only messages.** Messages sent as attachments with no typed text now appear correctly in conversation views, including `human_files` and `ai_files` on each turn in `messages/details/`.

**OpenRouter embedding tokens.** The embedding path now reports `output: 0` explicitly, eliminating inflated token totals from Langfuse's inference.

---

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

**Connect page.** New `/connect` page for the "Connect to ibl.ai" flow — generates and manages API tokens from the auth SPA.

**API token management.** Token names are slugified, expiry is configurable, and org search supports scrollable lists.

**Microsoft Clarity.** Analytics integration added to the auth SPA.

**Profile modal revamp.** Redesigned profile modal with integrated LLM usage display.

**Auth redirect improvements.** Login/complete and SSO-login-complete flows now route to a sensible default when no redirect was requested; tenant selection carries through account creation.

---

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

**Chrome extension — Cowork browser control.** The Chrome extension side panel (v1.1.0) can now drive the active browser tab from Cowork. v1.1.1 adds support for elements inside iframes and open shadow roots.

**Explore UI revamp.** Refreshed agent-discovery interface.

**Analytics platform scope.** Platform-scope selector lets admins switch analytics context without navigating away.

**Show additional agents toggle.** Agents → Explore section visibility controlled by `show_explore_mentors` in mentor settings, gated by the new backend toggle.

**Disclaimer agreements.** New modal listing who accepted a user agreement, accessible from the Disclaimers tab with RBAC gating.

**Chat history attachments.** Uploaded files and images now render in the history tab alongside message text.

---

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

**Gradebook in analytics.** Gradebook view enabled on the analytics page.

**Global search integration.** The discover hook now uses the global search endpoint.

**Agent-based completion popup.** Course-completion popup powered by an AI agent, behind a feature flag.

**Program detail analytics.** Course and program URLs passed to the analytics program detail page for direct navigation.

---

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

**nginx from nginx.org.** `ibl global-proxy launch` now installs nginx from nginx.org (pinned via `IBL_REVERSE_PROXY.NATIVE_NGINX_VERSION`, default `1.30.5-1`) with njs, instead of Ubuntu's nginx 1.18. Packages are held so `apt upgrade` cannot move the version. Existing `nginx.conf` is preserved.

ModSecurity is no longer compiled on the node — `libmodsecurity` 3.0.16 and `ModSecurity-nginx` 1.0.4 are pulled prebuilt from `MODSECURITY_MODULE_IMAGE` (or built locally as a fallback). Upgrade guide: `docs/NGINX_UPGRADE.md`.

**Per-service firewall switches.** `IBL_REVERSE_PROXY.MODSECURITY_DM_MODE` and `MODSECURITY_EDX_MODE` let the DM and edX enforce independently. `MODSECURITY_MODE` stays the master switch; a service blocks only when both its own switch and the master say `enforce`. `firewall status` shows every switch and which one governs each rule file.

**edX allow-lists.** `edx-lms-allow.yml` and `edx-studio-allow.yml` provide default-deny allow-lists for the LMS and Studio under the edX service switch.

**`ibl global-proxy allow-report`.** Reports how many requests each ModSecurity allow rule admitted (from the nginx access log), flags unused rules, and lists paths a default-deny would block that no allow matched.

**CSP partner hosts.** `IBL_SPA.MENTOR.CSP_PARTNER_HOSTS` renders into `.env.mentor` and `.env.os` (defaults to the DM ASGI websocket origin); `IBL_SPA.AUTH.CSP_PARTNER_HOSTS` renders into `.env.auth` (defaults to the three platform hosts). Both derived from `BASE_DOMAIN`.

**ModSecurity setup validation.** The setup script validates that directives are active (not commented out) and re-enables all three when needed. `--reinstall` on `launch`, `launch-without-security` and `mod-sec-setup` forces reinstallation. `launch` restarts nginx after syncing config (previously used `start`, which was a no-op on a running instance).

**DM default-deny.** The DM allow-list `/` path now uses `path_match: exact` and `vpn_only`, matching the `/oidc/` door it redirects to. The DM default-deny rollout validated: over one day on production, 4 of 4,582 requests to `/oidc/` completed the round trip — all from VPN.

**New DM env vars.** `IBL_DM.HEYGEN_COST_TRACKING_ENABLED` / `_INTERVAL_MINUTES` for HeyGen cost tracking; `IBL_DM.VERCEL_SHARED_DOMAIN` for the Vercel shared domain.

---

## @iblai/web-containers (`1.21.1` → `1.27.0`)

**Transcript file attachments.** Analytics transcripts render file-only turns and include a `TranscriptFiles` attachment list.

**Show additional agents toggle.** New toggle in Capabilities → Advanced, wired to `show_explore_mentors` in mentor settings.

**Profile modal revamp.** Redesigned profile modal with integrated LLM usage analytics.

**Hosted custom domains.** Domains panel added to the Advanced tab; pasted domains are normalised and the serving state is displayed.

**Disclaimers tab.** Info box, View Agreements button with RBAC gating, and `AgreementsModal` listing who accepted a user agreement.

**Analytics.** Content listings sorted by enrollment count; platform-scope selector integrated.

**User-Agent on document training.** Optional User-Agent input when retraining a document, sent as `crawler_extra_headers`.

**Watchers profile summary.** Profile summary card for watchers.

**LLM provider labels.** Backend-served provider names and logos replace the bundled frontend map; `LLMProviderLogo` component renders backend-served logos.

---

## @iblai/data-layer (`1.15.0` → `1.20.0`)

**Transcript message attachments.** Types for transcript message attachments (`human_files`, `ai_files`).

**Disclaimer agreements API.** `useGetDisclaimerAgreementsQuery` hook and endpoint constants/types.

**Global search.** `getSearchGlobalQuery` endpoint implemented.

**LLM credential types.** `service_info` typed on credential schemas.

---

## @iblai/web-utils (`2.4.8` → `2.5.1`)

**Agent-based completion popup.** Course-completion popup powered by an AI agent, behind a feature flag.

**Text block rendering.** Text blocks split by a tool call are now separated correctly so paragraphs don't merge across tool-call boundaries.

**Auth redirect.** The per-tab boot effect no longer redirects to auth on public routes.

---

## @iblai/mcp (`1.13.1` → `1.18.0`)

**Component MCP documentation.** Updated component MCP documentation for the latest components.

Rolls up `web-containers`, `data-layer` and `web-utils` changes above.

---

## @iblai/iblai-js (`2.12.3` → `2.20.0`)

Aggregator package — bundles all `web-containers`, `data-layer`, `web-utils` and `mcp` updates listed above.
