Skip to main content

RBAC File Map

When you need to change something in the auth path, this table tells you which file owns the behavior. Keep this file in sync whenever a new file enters the auth path β€” see the workspace rule in CLAUDE.md for the full update policy.

What you want to changeFile
Short primer: Keycloak JWTs prove identity/context while OpenFGA proves relationship accessdocs/docs/security/rbac/jwt-and-openfga.md
Deep-dive: where the OpenFGA permission union is evaluated, what gets stored vs. computed (no can_* tuples are ever written), and a worked end-to-end Probe-button example walking the model, atom tuples, BFF gate code path, and OpenFGA server-side union evaluationdocs/docs/security/rbac/openfga-permission-evaluation.md
Shared team-membership sync helpers — resolveKeycloakUserSubject (email→Keycloak sub), writeTeamMembershipTuples (writes user:<sub>#{member,admin} team:<slug>), and mongoRoleToOpenFgaRelations (creator gets both admin and member). Both POST /api/admin/teams (team creation) and POST /api/admin/teams/[id]/members (add member) MUST go through these helpers so OpenFGA, Mongo teams.members, and team_membership_sources stay in sync. The original team-creation route used to skip the OpenFGA write, leaving team:<slug>#can_use false for the creator.ui/src/lib/rbac/team-membership-sync.ts, ui/src/app/api/admin/teams/route.ts, ui/src/app/api/admin/teams/[id]/members/route.ts, ui/src/app/api/admin/teams/__tests__/team-creation-openfga-sync.test.ts
Issue #1509 β€” scoped team admins (members with role=owner or role=admin) can manage their own team end-to-end without holding the platform-wide organization:<org>#admin tuple. All six team-mutation routes (PATCH/DELETE /api/admin/teams/[id], PUT /api/admin/teams/[id]/roles, PUT /api/admin/teams/[id]/resources, POST/DELETE /api/admin/teams/[id]/members, POST /api/admin/teams/[id]/openfga/reconcile) go through requireTeamMembershipManagementPermission(session, actorEmail, team) in team-admin-guards.ts, which first tries the platform-admin bypass (admin_ui#admin) and falls back to isScopedTeamAdmin(actorEmail, team).ui/src/lib/rbac/team-admin-guards.ts, ui/src/app/api/admin/teams/[id]/route.ts, ui/src/app/api/admin/teams/[id]/roles/route.ts, ui/src/app/api/admin/teams/[id]/resources/route.ts, ui/src/app/api/admin/teams/[id]/members/route.ts, ui/src/app/api/admin/teams/[id]/openfga/reconcile/route.ts, ui/src/app/api/admin/teams/[id]/__tests__/team-admin-edit.test.ts, ui/src/app/api/admin/teams/[id]/members/__tests__/membership-sources.test.ts
Team OpenFGA sync diagnostic β€” computeTeamMembershipSyncReport is a pure function that compares team_membership_sources against OpenFGA tuples on team:<slug> and reports each active row as synced, pending (no Keycloak sub resolved yet), drifted (sub resolved but tuple missing), or unknown (OpenFGA unreachable). readTeamOpenFgaTuples is the IO wrapper that reads tuples one team at a time. GET /api/admin/teams/[id] decorates its response with a top-level openfga_sync field consumed by the Teams settings dialog (banner on the Details tab + per-member badge on the Members tab). POST /api/admin/teams/[id]/openfga/reconcile is the on-demand repair endpoint β€” same auth as add-member (team admin or platform admin) β€” that re-resolves missing subjects and writes any missing tuples.ui/src/lib/rbac/team-openfga-sync-status.ts, ui/src/app/api/admin/teams/[id]/route.ts, ui/src/app/api/admin/teams/[id]/openfga/reconcile/route.ts, ui/src/components/admin/TeamDetailsDialog.tsx, ui/src/lib/rbac/__tests__/team-openfga-sync-status.test.ts, ui/src/app/api/admin/teams/__tests__/team-openfga-sync-status.test.ts
Helm installation and upgrade runbook for the RBAC/OpenFGA refactor, including optional in-chart Keycloak, AgentGateway, OpenFGA, and OpenFGA bridge runtime componentsdocs/docs/security/rbac/helm-install-upgrade.md
Keycloak Helm realm import: roles, clients, and optional demo userscharts/ai-platform-engineering/charts/keycloak/realm-config.json
Keycloak Helm runtime: deployment args, realm-JWKS startup/readiness probes, management liveness probe, ingress, and mounted realm import; filters demo users unless keycloak.demoUsers.enabled=truecharts/ai-platform-engineering/charts/keycloak/templates/deployment.yaml, charts/ai-platform-engineering/charts/keycloak/templates/ingress.yaml, charts/ai-platform-engineering/charts/keycloak/templates/configmap-realm.yaml
Keycloak Helm login theme: packaged caipe theme ConfigMap, conditional mount, value-driven branding (keycloak.theme.brandName, keycloak.theme.colors.*, keycloak.theme.files.*), and optional keycloak.theme.existingConfigMap overridecharts/ai-platform-engineering/charts/keycloak/templates/configmap-theme.yaml, charts/ai-platform-engineering/charts/keycloak/templates/deployment.yaml, charts/ai-platform-engineering/charts/keycloak/themes/caipe/, charts/ai-platform-engineering/charts/keycloak/values.yaml
Keycloak first-run bootstrap: shell-owned pre-BFF escape hatch for direct-admin app-realm bootstrap, IdP broker login bootstrap, caipe-ui client-secret reconciliation from KEYCLOAK_UI_CLIENT_SECRET, caipe-platform client-secret reconciliation from KEYCLOAK_PLATFORM_CLIENT_SECRET, bot client-secret reconciliation in init-token-exchange.sh, optional strict mode (keycloak.strictClientSecrets=true β†’ KEYCLOAK_STRICT_CLIENT_SECRETS=true in all three init Jobs) that fails the Helm install if any of the four dev placeholder client_secrets are still accepted by Keycloak, optional demo personas (KEYCLOAK_SEED_DEMO_USERS=true), and master-realm admin-console settings that must not depend on BFF auth being healthycharts/ai-platform-engineering/charts/keycloak/templates/job-auth-reconcile.yaml, charts/ai-platform-engineering/charts/keycloak/templates/job-init-idp.yaml, charts/ai-platform-engineering/charts/keycloak/templates/job-init-token-exchange.yaml, charts/ai-platform-engineering/charts/keycloak/templates/ui-client-external-secret.yaml, charts/ai-platform-engineering/charts/keycloak/templates/platform-client-external-secret.yaml, charts/ai-platform-engineering/charts/keycloak/scripts/init-idp.sh, charts/ai-platform-engineering/charts/keycloak/scripts/init-token-exchange.sh, tests/test_keycloak_ui_client_secret_reconcile.py, tests/test_keycloak_platform_client_secret_reconcile.py, tests/test_keycloak_strict_client_secrets.py, tests/integration/test_keycloak_platform_client_reconcile.sh, tests/integration/test_keycloak_strict_client_secrets.sh
Keycloak IdP-redirector / kc_idp_hint plumbing: init-idp.sh wires the identity-provider-redirector execution to IDP_ALIAS as the default broker, optionally enforces it (KEYCLOAK_FORCE_IDP_REDIRECT=true β†’ requirement REQUIRED), and NextAuth conditionally forwards kc_idp_hint=${OIDC_IDP_HINT} so Keycloak skips its own login page and bounces straight to the upstream IdP (Okta / Duo SSO / Azure AD / …). The UI Jest test pins the conditional spread (must be present when OIDC_IDP_HINT is set, must be omitted when unset or empty). The integration test boots a throwaway Keycloak, runs init-idp.sh end-to-end, and validates that /realms/caipe/protocol/openid-connect/auth 302/303s to /broker/${IDP_ALIAS}/login with no hint, with an explicit hint, and that an unknown hint does not 5xx.ui/src/lib/auth-config.ts (kc_idp_hint conditional spread on authorization.params), ui/src/lib/__tests__/auth-config.test.ts (OIDC kc_idp_hint forwarding describe), charts/ai-platform-engineering/charts/keycloak/scripts/init-idp.sh (Setting '<alias>' as default IdP redirector + Enforcing '<alias>' as the only browser login path blocks), tests/integration/test_keycloak_idp_hint_redirect.sh, .github/workflows/ci-keycloak-init.yml (idp-hint-test job)
Export client secrets to env/dotenv/K8s Secretdeploy/keycloak/export-client-secrets.sh
UI session & NextAuth OIDC config, deployment-configured Web UI admission group check, group claim extraction, login-time OpenFGA baseline/admin bootstrap (organization, system_config, user_profile, baseline admin_surface readers, admin admin_surface managers, and admin mcp_server:agentgateway manager), Mongo-backed baseline FGA profile overrides, login-time identity sync trigger, and AccessTokenMissing invalidation when the server-side token cache is lostui/src/lib/auth-config.ts, ui/src/lib/rbac/baseline-access.ts, ui/src/lib/rbac/login-openfga-bootstrap.ts, ui/src/lib/rbac/__tests__/login-openfga-bootstrap.test.ts, ui/src/components/token-expiry-guard.tsx, ui/src/lib/__tests__/auth-config.test.ts, ui/src/components/__tests__/token-expiry-guard.test.tsx
UI RBAC middleware (dual bearer/session auth, server-side rejection of sessions that failed Web UI admission, per-route OpenFGA enforcement, compatibility gates for legacy withAuth handlers, and best-effort persistence of users.keycloak_sub / users.metadata.keycloak_sub)ui/src/lib/api-middleware.ts, ui/src/lib/__tests__/api-middleware.test.ts
R4 (NEXTAUTH_SECRET strict mode, May 2026): NEXTAUTH_SECRET HS256-signs both NextAuth session cookies and the internal skills-API tokens minted by signLocalSkillsToken. Sharing one across installs is a cross-install identity compromise. The guard assertNextAuthSecretSafe rejects known dev placeholders (caipe-dev-secret, caipe-dev-secret-change-in-production, changeme, your-secret-here, …) and secrets shorter than 32 chars in strict mode; an explicit ALLOW_NEXTAUTH_DEV_SECRET=true override exists for CI parity with R1. signLocalSkillsToken throws at mint time when the gate trips; validateLocalSkillsJWT returns null (caller falls through to OIDC) instead of 5xx-ing. The Makefile run-caipe-ui-docker target embeds the same placeholder list as a shell-level guard so an operator who copies that one-liner gets the same loud failure even before the BFF code runs. The compose-file docker-compose.yaml uses ${NEXTAUTH_SECRET:?…} to abort docker compose up if the env var is unset.ui/src/lib/nextauth-secret-guard.ts, ui/src/lib/jwt-validation.ts, ui/src/lib/__tests__/nextauth-secret-guard.test.ts, ui/src/lib/__tests__/jwt-validation.test.ts, Makefile, docker-compose.yaml, docker-compose.dev.yaml, ui/env.example, docs/docs/ui/auth-flow.md
R3 (MongoDB rootPassword strict mode, May 2026): the caipe-ui-mongodb subchart used to ship auth.rootPassword: "changeme" as the chart default. An operator who ran helm install without explicitly overriding auth.rootPassword (and without enabling externalSecrets.enabled=true) shipped admin/changeme to the cluster β€” the in-cluster Secret comes directly from this value. The new strictPasswords value is a chart-render gate that mirrors keycloak.strictClientSecrets: when strictPasswords=true AND externalSecrets.enabled=false, the _strict-passwords.tpl helper calls {{ fail }} if auth.rootPassword is in a known-placeholder set (changeme, admin, password, mongo, root, test, secret, your-password-here, …) or shorter than 8 chars. When externalSecrets.enabled=true the gate skips its check (the in-cluster Secret comes from Vault/AWS Secrets Manager/etc., not auth.rootPassword). Pinned by 14 chart-render tests in tests/test_mongodb_strict_passwords.py and a 4-step helm template integration test that mirrors tests/integration/test_keycloak_strict_client_secrets.sh. Default ships strictPasswords=false so the docker-compose dev flow and CI matrix runs continue to work; production GitOps installs should flip it to true.charts/ai-platform-engineering/charts/caipe-ui-mongodb/values.yaml (strictPasswords flag + comment), charts/ai-platform-engineering/charts/caipe-ui-mongodb/templates/_strict-passwords.tpl, charts/ai-platform-engineering/charts/caipe-ui-mongodb/templates/secret.yaml (calls mongodb.assertStrictPasswords), charts/ai-platform-engineering/values.yaml (umbrella caipe-ui.mongodb.strictPasswords), tests/test_mongodb_strict_passwords.py, tests/integration/test_mongodb_strict_passwords.sh
R2 (setup-caipe.sh MongoDB random password, May 2026): the workshop on-ramp script setup-caipe.sh used to bake the literal password changeme into four sites: the bitnami/mongodb helm upgrade (auth.rootPassword=changeme, auth.passwords[0]=changeme), the MONGODB_URI written into the dynamic-agents seed values file, and the MONGODB_URI patched into the caipe-dynamic-agents-config, caipe-supervisor-agent-env, caipe-single-node-agent-env ConfigMaps and the caipe-ui-secret Secret. Every workshop install inherited the same admin password and the same cluster-internal mongodb://admin:changeme@… URI. The new _resolve_mongodb_password helper generates an openssl rand -hex 24 value (48 hex chars, URL-safe for the connection string) on first run and persists it in the caipe-mongodb-credentials Secret; subsequent runs read the existing value back out, so the install stays idempotent. The "Services Ready" banner now prints a kubectl get secret caipe-mongodb-credentials … one-liner for recovery (same pattern the script already used for langfuse-credentials). Pinned by tests/integration/test_setup_caipe_mongodb_password.sh which: extracts the helper from setup-caipe.sh, mocks kubectl with a sandboxed Secret store on PATH, and asserts (1) a fresh run generates and persists a password, (2) a re-run reuses it, (3) the password is exactly 48 hex chars with no URL-special characters, (4) the password is never the literal changeme, and (5) the string changeme no longer appears in any non-comment line of setup-caipe.sh.setup-caipe.sh (_resolve_mongodb_password helper + four changemeβ†’${MONGODB_ROOT_PASSWORD} substitutions + banner hint), tests/integration/test_setup_caipe_mongodb_password.sh
Web UI backend Keycloak Admin REST credentials (KEYCLOAK_ADMIN_CLIENT_ID / KEYCLOAK_ADMIN_CLIENT_SECRET) for admin users, teams, roles, and per-team scope provisioning. fetchFreshAdminToken tries client_credentials first; on missing env vars or 401 it MAY fall back to a password grant against /realms/master using the dev-only admin/admin credentials. R1 (production-safety gate, May 2026): the fallback is now off by default in production builds β€” adminPasswordFallbackAllowed() only returns true when ALLOW_KEYCLOAK_ADMIN_PASSWORD_FALLBACK=true (or =1), or implicitly when NODE_ENV != "production". Anything else throws a configuration error so a misconfigured prod install fails loudly instead of silently calling /realms/master. The keycloak-admin-token.test.ts Jest suite has 8 tests: 4 pinning the legacy fallback chain (when the flag is on / NODE_ENV != "production") and 4 in the "production safety gate (R1)" describe block pinning the new prod-default-deny behavior, the per-call re-raise without a master fallback, and both directions of the explicit override flag. R1 upstream fix (May 2026): the umbrella values.yaml now defaults keycloak.platformClient.secretRef=caipe-platform-secret AND caipe-ui.keycloakAdminClient.secretName=caipe-platform-secret; the caipe-ui Deployment auto-injects KEYCLOAK_ADMIN_CLIENT_SECRET via valueFrom.secretKeyRef (key OIDC_CLIENT_SECRET) and the ConfigMap auto-injects KEYCLOAK_ADMIN_CLIENT_ID=caipe-platform whenever keycloakAdminClient.secretName is non-empty. The ConfigMap uses hasKey to skip auto-injection when the operator has set caipe-ui.config.KEYCLOAK_ADMIN_CLIENT_ID explicitly (no-clobber). The ESO target name now also resolves to caipe-platform-secret for the default Path 3 install (was <release>-keycloak-platform-client before the fix) β€” see the migration matrix in secrets-bootstrap.md Β§ "R1 upstream fix". The chart-render test tests/test_caipe_ui_keycloak_admin_client_env.py pins 9 things total: Pins 1–4 (default install + explicit secretRef + ESO + legacy existingSecret back-compat all wire KEYCLOAK_ADMIN_CLIENT_* correctly), Pins 5–6 (R1 BFF gate: NODE_ENV=production default + ALLOW_KEYCLOAK_ADMIN_PASSWORD_FALLBACK not default-true + dev opt-in flag propagation), and Pins 7–9 (operator override of keycloakAdminClient.secretName, explicit-empty-skips-wiring escape hatch for standalone caipe-ui installs, and operator-supplied config.KEYCLOAK_ADMIN_CLIENT_ID wins over auto-injection).ui/src/lib/rbac/keycloak-admin.ts, ui/src/lib/rbac/__tests__/keycloak-admin.test.ts, ui/src/lib/rbac/__tests__/keycloak-admin-token.test.ts, charts/ai-platform-engineering/values.yaml (umbrella defaults for keycloak.platformClient.secretRef + caipe-ui.keycloakAdminClient), charts/ai-platform-engineering/charts/caipe-ui/values.yaml (keycloakAdminClient block), charts/ai-platform-engineering/charts/caipe-ui/templates/configmap.yaml (R1 KEYCLOAK_ADMIN_CLIENT_ID auto-injection with hasKey no-clobber guard), charts/ai-platform-engineering/charts/caipe-ui/templates/deployment.yaml (R1 KEYCLOAK_ADMIN_CLIENT_SECRET valueFrom.secretKeyRef), charts/ai-platform-engineering/charts/caipe-ui/templates/external-secret.yaml, charts/ai-platform-engineering/charts/keycloak/templates/platform-client-external-secret.yaml, docker-compose.dev.yaml, tests/test_caipe_ui_keycloak_admin_client_env.py
Supervisor middleware stack (auth + JWT context)ai_platform_engineering/multi_agents/platform_engineer/protocol_bindings/a2a/main.py
Per-request user identity (contextvar)ai_platform_engineering/utils/auth/jwt_context.py
JWT context middleware (Starlette)ai_platform_engineering/utils/auth/jwt_user_context_middleware.py
Supervisor agent executor (ENABLE_USER_INFO_TOOL)ai_platform_engineering/multi_agents/platform_engineer/protocol_bindings/a2a/agent_executor.py
Dynamic agents JWT validation & userinfoai_platform_engineering/dynamic_agents/src/dynamic_agents/auth/auth.py
Dynamic Agents Web UI backend execution proxy auth and UI shell: session/bearer authentication, stable subject and role propagation, X-User-Context browser-session fallback when the server-side Keycloak token cache is missing, backend bearer forwarding when available, W3C traceparent forwarding, no OIDC_REQUIRED_DYNAMIC_AGENTS_GROUP/admin-only UI gate for the Agents page, Conversations tab shown only when OpenFGA admin audit-log access is granted, the Models tab uses the LLM provider credential surface plus the OpenFGA-filtered model list, and conversation-scoped file proxies gated by dynamic_agent#invokeui/src/lib/da-proxy.ts, ui/src/lib/auth-config.ts, ui/src/components/layout/AppHeader.tsx, ui/src/app/(app)/dynamic-agents/page.tsx, ui/src/app/(app)/dynamic-agents/__tests__/page.test.tsx, ui/src/components/dynamic-agents/LLMProvidersTab.tsx, ui/src/components/dynamic-agents/__tests__/LLMProvidersTab.test.tsx, ui/src/components/layout/__tests__/AppHeader.test.tsx, ui/src/app/api/dynamic-agents/conversations/[id]/files/list/route.ts, ui/src/app/api/dynamic-agents/conversations/[id]/files/content/route.ts, ui/src/lib/__tests__/da-proxy-auth-result.test.ts, ui/src/lib/__tests__/auth-config.test.ts
Dynamic Agents Web UI backend OpenFGA execution, picker, and conversation binding gates: subject-first, email-fallback can_use agent:<agent_id> checks for start/invoke/resume/cancel, chat agent selection, subagent selection, and new conversation agent bindings; implicit-owner-or-explicit conversation checks on chat routes; authz trace creation and openfga_rebac audit emissionui/src/lib/rbac/openfga-agent-authz.ts, ui/src/lib/rbac/authz-tracing.ts, ui/src/lib/rbac/resource-authz.ts, ui/src/lib/rbac/conversation-implicit-authz.ts, ui/src/app/api/v1/chat/_conversation-authz.ts, ui/src/app/api/dynamic-agents/available/route.ts, ui/src/app/api/dynamic-agents/available-subagents/route.ts, ui/src/app/api/chat/conversations/route.ts, ui/src/lib/rbac/__tests__/resource-authz.test.ts, ui/src/lib/rbac/__tests__/openfga-agent-authz.test.ts, ui/src/lib/rbac/__tests__/authz-tracing.test.ts, ui/src/lib/rbac/__tests__/audit-tracing.test.ts, ui/src/app/api/v1/chat/stream/start/route.ts, ui/src/app/api/v1/chat/invoke/route.ts, ui/src/app/api/v1/chat/stream/resume/route.ts, ui/src/app/api/v1/chat/stream/cancel/route.ts, ui/src/app/api/v1/chat/__tests__/routes.test.ts, ui/src/app/api/dynamic-agents/__tests__/route-rbac.test.ts, ui/src/app/api/__tests__/chat-conversations-agent-auth.test.ts
Connections & Secrets credential management: feature flag, signed-in-only and organization:<org>#can_use page gate, MongoDB envelope store, env/ESO OAuth connector startup bootstrap, browser guardrails for raw retrieval, service-to-service retrieve/exchange APIs with service JWT validation, JWT-sub provider-key exchange, future AgentGateway credential injector route, and secret_ref:provider_connection:<id>#can_use, browser-safe OAuth profile validators and automatic refresh endpoints that return only redacted provider/refresh metadata, deep-linked user My Secrets/My Connections tabs, user-visible enabled OAuth connector list with Connect/Relink and Check Profile actions, Dynamic Agents LLM provider credentials saved as OpenFGA-governed credential secrets, OpenFGA-backed global admin secret metadata management, deep-linked OAuth connector/admin secret/audit tabs, org-admin-only global credential audit/admin tab, Dynamic Agents MCP provider credential-ref resolution, and Jira MCP provider-token header consumptionui/src/lib/feature-flags/credentials.ts, ui/src/lib/credentials/, ui/src/lib/seed-config.ts, ui/src/app/(app)/credentials/page.tsx, ui/src/app/(app)/credentials/__tests__/page.test.tsx, ui/src/app/api/credentials/, ui/src/app/api/credentials/inject/[provider]/route.ts, ui/src/app/api/credentials/inject/[provider]/__tests__/route.test.ts, ui/src/app/api/credentials/connections/[connection_id]/profile/route.ts, ui/src/app/api/credentials/connections/[connection_id]/profile/__tests__/route.test.ts, ui/src/app/api/credentials/connections/[connection_id]/refresh/route.ts, ui/src/app/api/credentials/connections/[connection_id]/refresh/__tests__/route.test.ts, ui/src/app/api/admin/credentials/secrets/route.ts, ui/src/app/api/admin/credentials/secrets/[secret_id]/route.ts, ui/src/app/api/admin/credentials/oauth-connectors/route.ts, ui/src/app/api/admin/credentials/oauth-connectors/[connector_id]/route.ts, ui/src/app/api/admin/credentials/audit/route.ts, ui/src/components/credentials/, ui/src/components/dynamic-agents/LLMProvidersTab.tsx, docker-compose.dev.yaml, docker-compose.yaml, charts/ai-platform-engineering/charts/caipe-ui/values.yaml, charts/ai-platform-engineering/values.yaml, ai_platform_engineering/dynamic_agents/src/dynamic_agents/services/credential_exchange.py, ai_platform_engineering/dynamic_agents/src/dynamic_agents/services/mcp_client.py, ai_platform_engineering/dynamic_agents/src/dynamic_agents/models.py, ai_platform_engineering/agents/jira/mcp/mcp_jira/api/client.py, ai_platform_engineering/agents/jira/mcp/tests/test_client.py
Remaining OpenFGA cutovers for conversations, skills, workflow runs, MCP servers, and RAG tools: shared/search/trash conversation candidate filtering, conversation share/pin/archive/restore resource checks, skill nested route/import authorization, workflow-run parent-config checks, self-service mcp_server owner/team tuples, and concrete RAG tool checksui/src/app/api/chat/shared/route.ts, ui/src/app/api/chat/search/route.ts, ui/src/app/api/chat/conversations/trash/route.ts, ui/src/app/api/chat/conversations/[id]/share/route.ts, ui/src/app/api/chat/conversations/[id]/pin/route.ts, ui/src/app/api/chat/conversations/[id]/archive/route.ts, ui/src/app/api/chat/conversations/[id]/restore/route.ts, ui/src/app/api/__tests__/chat-conversations-remaining-rbac.test.ts, ui/src/lib/agent-skill-visibility.ts, ui/src/app/api/skills/configs/route.ts, ui/src/app/api/skills/configs/import-zip/route.ts, ui/src/app/api/skills/configs/[id]/revisions/route.ts, ui/src/app/api/skills/configs/[id]/revisions/[revisionId]/route.ts, ui/src/app/api/skills/configs/[id]/revisions/[revisionId]/restore/route.ts, ui/src/app/api/skills/configs/[id]/export/route.ts, ui/src/app/api/skills/configs/[id]/clone/route.ts, ui/src/app/api/__tests__/agent-skill-subroutes-rbac.test.ts, ui/src/app/api/skills/configs/__tests__/route-rbac.test.ts, ui/src/app/api/skills/configs/import-zip/__tests__/route.test.ts, ui/src/app/api/workflow-runs/route.ts, ui/src/app/api/workflow-runs/[id]/resume/route.ts, ui/src/app/api/workflow-runs/[id]/cancel/route.ts, ui/src/app/api/__tests__/workflow-runs-rbac.test.ts, ui/src/app/api/mcp-servers/route.ts, ui/src/lib/rbac/openfga-owned-resources.ts, ui/src/app/api/__tests__/mcp-servers-rbac.test.ts, ui/src/app/api/rag/tools/route.ts, ui/src/app/api/rag/tools/[toolId]/route.ts
Plain Web UI supervisor SSE proxy and legacy message/metadata persistence β€” forwards cookie-session or first-party bearer access tokens to the backend and uses implicit-owner-or-explicit conversation read/write checks, including Slack OBO thread metadata updatesui/src/app/api/chat/stream/route.ts, ui/src/app/api/chat/conversations/[id]/messages/route.ts, ui/src/app/api/chat/conversations/[id]/metadata/route.ts, ui/src/app/api/__tests__/chat-stream-route.test.ts, ui/src/app/api/__tests__/chat-conversation-metadata-auth.test.ts, ui/src/app/api/__tests__/owner-id-denormalization.test.ts
Dynamic Agents runtime OpenFGA execution gate, email fallback, durable MongoDB openfga_rebac runtime audit event, bearer forwarding for AgentGateway-backed MCP probe/tool calls, and traceparent propagation before agent lookup/runtime workai_platform_engineering/dynamic_agents/src/dynamic_agents/auth/openfga_authz.py, ai_platform_engineering/dynamic_agents/src/dynamic_agents/auth/token_context.py, ai_platform_engineering/dynamic_agents/src/dynamic_agents/auth/jwt_middleware.py, ai_platform_engineering/dynamic_agents/src/dynamic_agents/services/mcp_client.py, ai_platform_engineering/dynamic_agents/src/dynamic_agents/routes/chat.py, ai_platform_engineering/dynamic_agents/tests/test_openfga_authz.py, ai_platform_engineering/dynamic_agents/tests/test_chat_pdp_gate.py, ai_platform_engineering/dynamic_agents/tests/test_mcp_client_token_forwarding.py
Per-agent MCP tool restriction and ownership enforcement: Web UI backend reconciles creator owner, organization-admin manager, owner-team user/manager, and agent:<id> caller tool:<server>/<tool> OpenFGA tuples; Dynamic Agents signs agent_id context for AgentGateway, and the bridge checks derived can_call on tools/callui/src/lib/rbac/openfga-agent-tools.ts, ui/src/app/api/dynamic-agents/route.ts, ui/src/app/api/dynamic-agents/teams/route.ts, ui/src/components/dynamic-agents/DynamicAgentEditor.tsx, ui/src/lib/rbac/__tests__/openfga.test.ts, ui/src/app/api/dynamic-agents/__tests__/route-rbac.test.ts, ui/src/components/dynamic-agents/__tests__/DynamicAgentEditor.unsaved.test.tsx, ai_platform_engineering/dynamic_agents/src/dynamic_agents/services/mcp_client.py, ai_platform_engineering/dynamic_agents/tests/test_mcp_client_token_forwarding.py, deploy/openfga/bridge/main.py, deploy/openfga/bridge/tests/test_grpc_bridge.py, scripts/backfill-agent-tool-openfga.ts, scripts/__tests__/backfill-agent-tool-openfga.test.ts, docker-compose.dev.yaml, charts/ai-platform-engineering/charts/openfga-authz-bridge/templates/deployment.yaml, charts/ai-platform-engineering/charts/openfga-authz-bridge/values.yaml
Dynamic agents legacy visibility helpers (not the authoritative execution PDP for start/invoke/resume)ai_platform_engineering/dynamic_agents/src/dynamic_agents/auth/access.py
AgentGateway static ext_authz configuration with one MCP route/backend per targetdeploy/agentgateway/config.yaml
Host-specific caipe.example.com AgentGateway + nginx assets used by an external Docker Compose overlay (the overlay itself lives outside this repo under ~/outshift/)deploy/caipe-rbac-nginx.conf, deploy/agentgateway/config.caipe-rbac.yaml
AgentGateway ext_authz template (no CEL policy rendering)deploy/agentgateway/config.yaml.j2
AgentGateway OpenFGA gRPC extAuthz route gatedeploy/agentgateway/config.yaml and deploy/agentgateway/config.yaml.j2 (extAuthz block)
AgentGateway data-plane ingress (service.port, default 4000; admin port 15000 stays private)charts/ai-platform-engineering/charts/agentgateway/templates/ingress.yaml, charts/ai-platform-engineering/charts/agentgateway/values.yaml
AgentGateway MCP endpoint routing invariant β€” every AgentGateway-routed MCP server must persist a target-qualified endpoint {agentgateway_base}/mcp/<server_id>. A bare {agentgateway_base}/mcp returns HTTP 404 from AgentGateway (no matching path-prefix) and surfaces in the UI as Failed to connect to MCP server: HTTP 404 Not Found from http://agentgateway:4000/mcp. Defence in depth: (a) POST/PUT /api/mcp-servers normalises the endpoint on save via the shared TS helper, (b) MCPServerEditor offers a Pick AgentGateway target row populated by the discovery API so admins don't type endpoints by hand, (c) the dynamic-agents probe path self-heals stale Mongo rows at read time via the mirror Python helper, (d) scripts/fix-mcp-endpoint-routing.ts audits Mongo in dry-run and rewrites mis-shaped rows under --apply. Direct upstream URLs (http://mcp-confluence:8000/mcp) and config-driven rows are intentionally left untouched.ui/src/lib/rbac/mcp-endpoint-normalizer.ts, ui/src/lib/rbac/__tests__/mcp-endpoint-normalizer.test.ts, ui/src/app/api/mcp-servers/__tests__/endpoint-normalization.test.ts, ui/src/components/dynamic-agents/MCPServerEditor.tsx, ai_platform_engineering/dynamic_agents/src/dynamic_agents/services/mcp_endpoint_normalizer.py, ai_platform_engineering/dynamic_agents/tests/test_mcp_endpoint_normalizer.py, ai_platform_engineering/dynamic_agents/tests/test_mcp_client_endpoint_self_heal.py, scripts/fix-mcp-endpoint-routing.ts, scripts/__tests__/fix-mcp-endpoint-routing.test.ts
Web UI backend AgentGateway MCP discovery and one-click onboarding (mcp_server:agentgateway can_discover for discovery, can_manage for sync), concrete MCP server list/probe/update/delete checks for admins and non-admins, organization-member grant repair for existing/synced config-driven MCP servers, in-place migration of matching legacy direct MCP rows to AgentGateway routes, tuple cleanup before MCP server deletion, no session-role bypass, and migration warnings for true duplicate MCP server ID conflictsui/src/lib/rbac/agentgateway-mcp-discovery.ts, ui/src/app/api/mcp-servers/route.ts, ui/src/app/api/mcp-servers/probe/route.ts, ui/src/app/api/mcp-servers/agentgateway/discover/route.ts, ui/src/app/api/mcp-servers/agentgateway/sync/route.ts, ui/src/app/api/mcp-servers/agentgateway/_lib.ts, ui/src/components/dynamic-agents/MCPServersTab.tsx, ui/src/app/api/__tests__/mcp-servers-rbac.test.ts, ui/src/app/api/mcp-servers/agentgateway/__tests__/route.test.ts, ui/src/components/dynamic-agents/__tests__/MCPServersTab.test.tsx
Helm-packaged RBAC runtime services for 0.5.0: AgentGateway standalone proxy, OpenFGA, OpenFGA authz bridge, Keycloak dependency gating, stable admin/IdP/UI-client/platform-client secret enforcement and reconciliation, setup script enablement, and release image workflowscharts/ai-platform-engineering/Chart.yaml, charts/ai-platform-engineering/values.yaml, charts/ai-platform-engineering/charts/keycloak/templates/secret.yaml, charts/ai-platform-engineering/charts/keycloak/templates/ui-client-external-secret.yaml, charts/ai-platform-engineering/charts/keycloak/templates/platform-client-external-secret.yaml, charts/ai-platform-engineering/charts/keycloak/templates/job-init-idp.yaml, charts/ai-platform-engineering/charts/keycloak/templates/job-auth-reconcile.yaml, charts/ai-platform-engineering/charts/keycloak/scripts/init-idp.sh, charts/ai-platform-engineering/charts/agentgateway/, charts/ai-platform-engineering/charts/openfga/, charts/ai-platform-engineering/charts/openfga-authz-bridge/, deploy/openfga/bridge/tests/test_helm_values.py, tests/test_keycloak_ui_client_secret_reconcile.py, tests/test_keycloak_platform_client_secret_reconcile.py, tests/integration/test_keycloak_platform_client_reconcile.sh, setup-caipe.sh, .github/workflows/ci-keycloak-init.yml, .github/workflows/ci-openfga-authz-bridge.yml
OpenFGA dev PDP stack in Docker Compose (openfga, bridge, init, Postgres) with MongoDB-backed authz auditdocker-compose.dev.yaml (rbac profile for OpenFGA/bridge)
OpenFGA authorization model + seed tuple writer, including typed wildcard support for default-agent user:* user agent:<id> grants, organization-member reader llm_model:<id> grants for seeded LLM models, organization-member read/use/invoke grants for seeded MCP servers, emergency tuple seeding via openfga.init.seedTuples, organization-admin manager agent:<id>, manager llm_model:<id>, and admin_surface inheritance, user_profile:<sub> self-read ownership, self-service owner relations for MCP servers/LLM models/Slack channels/Webex spaces, team can_use/can_manage, and agent:<id> caller tool:<server>/<tool> grantsdeploy/openfga/model.fga, deploy/openfga/init/authorization-model.json, deploy/openfga-experiment/model.fga, deploy/openfga-experiment/init/authorization-model.json, charts/ai-platform-engineering/charts/openfga/authorization-model.json, charts/ai-platform-engineering/charts/openfga/templates/configmap-model.yaml, charts/ai-platform-engineering/charts/openfga/templates/job-init.yaml, deploy/openfga/init/seed.py, ui/src/lib/seed-config.ts
BFF bootstrap-admin resolver: reads bootstrap emails, resolves or creates Keycloak users, seeds durable OpenFGA organization/system_config tuples, and reports status in Keycloak health diagnosticsui/src/lib/rbac/keycloak-bootstrap-admins.ts, ui/src/lib/rbac/keycloak-admin.ts, ui/src/lib/rbac/keycloak-rbac-reconciliation.ts, ui/src/lib/rbac/keycloak-migration-health.ts, ui/src/components/admin/KeycloakMigrationHealthPanel.tsx, ui/src/lib/rbac/__tests__/keycloak-bootstrap-admins.test.ts, ui/src/lib/rbac/__tests__/keycloak-admin.test.ts, ui/src/lib/rbac/__tests__/keycloak-rbac-reconciliation.test.ts, ui/src/components/admin/__tests__/KeycloakMigrationHealthPanel.test.tsx
AgentGateway Envoy gRPC ext_authz adapter to OpenFGA Check and durable MongoDB openfga_rebac audit writerdeploy/openfga/bridge/main.py, deploy/openfga/bridge/audit.py, deploy/openfga/bridge/requirements.txt, deploy/openfga/bridge/tests/test_grpc_bridge.py, deploy/openfga/bridge/tests/test_helm_values.py
Admin UI OpenFGA tuple writer for Team Resources saves base relationships (team:<slug>#member user agent:<id>, caller tool:<prefix>)ui/src/lib/rbac/openfga.ts, ui/src/lib/rbac/__tests__/openfga.test.ts, ui/src/app/api/admin/teams/[id]/resources/route.ts, ui/src/app/api/__tests__/admin-team-resources.test.ts
Admin UI: top-level admin IA (Settings, Teams & Users, Integrations, Insights, Metrics & Health, Security & Policy), non-admin settings shell fallback when privileged stats APIs deny, Settings-hosted Credentials tab gated by CAIPE_CREDENTIALS_ENABLED and organization admin access, Settings-hosted Knowledge Bases tab for admin_surface:rag_datasources plus team Knowledge Base read/ingest/admin grants, OpenFGA ReBAC Access Manager with catalog-driven effective-access checks, searchable subject selection, Default FGA Grants editor with realtime all-user reconciliation, custom profile CRUD, team profile override assignment, OpenFGA Store: Catalog & Live Relationships read-only model/resource/tuple visibility, baseline diagnostics, staged grant/revoke remediation, policy graph, tuple inspector, and unified openfga_rebac RBAC Audit entries from Web UI backend, Dynamic Agents, RAG, and the OpenFGA bridgeui/src/app/(app)/admin/page.tsx, ui/src/app/(app)/admin/__tests__/admin-page.test.tsx, ui/src/app/api/rbac/admin-tab-gates/route.ts, ui/src/app/api/rbac/admin-tab-gates/__tests__/route.test.ts, ui/src/components/credentials/AdminCredentialManagementPanel.tsx, ui/src/components/credentials/AdminSecretsManager.tsx, ui/src/components/credentials/OAuthConnectorAdminPanel.tsx, ui/src/components/admin/rebac/RagTeamAccessPanel.tsx, ui/src/components/admin/rebac/__tests__/RagTeamAccessPanel.test.tsx, ui/src/components/admin/rebac/BaselineFgaProfilePanel.tsx, ui/src/components/admin/rebac/__tests__/BaselineFgaProfilePanel.test.tsx, ui/src/components/admin/rebac/UserBaselineDiagnosticsPanel.tsx, ui/src/app/api/admin/openfga/baseline-profile/route.ts, ui/src/app/api/admin/openfga/__tests__/baseline-profile-route.test.ts, ui/src/lib/rbac/baseline-access.ts, ui/src/lib/rbac/__tests__/baseline-access.test.ts, ui/src/app/api/admin/openfga/baseline-diagnostics/route.ts, ui/src/app/api/admin/openfga/__tests__/baseline-diagnostics-route.test.ts, ui/src/app/api/admin/teams/[id]/kb-assignments/route.ts, ui/src/app/api/admin/teams/[id]/kb-assignments/__tests__/route.test.ts, ui/src/components/admin/OpenFgaRebacTab.tsx, ui/src/components/admin/__tests__/OpenFgaRebacTab.test.tsx, ui/src/app/api/admin/openfga/*/route.ts, ui/src/lib/rbac/openfga.ts, ui/src/lib/rbac/audit.ts, ui/src/lib/rbac/types.ts, ui/src/components/admin/UnifiedAuditTab.tsx, ui/src/components/admin/__tests__/UnifiedAuditTab.test.tsx
Admin UI Default Agent setting and chat-available agent picker β€” reads system_config:platform_settings, saves platform_config.default_agent_id, and reconciles the OpenFGA typed-wildcard grant user:* user agent:<id> so authenticated users can see the configured default and enabled global Dynamic Agents through the OpenFGA-only picker. PATCH requires acknowledge_public_access: true whenever a non-null default changes, emits an [AUDIT] platform_default_agent_changed line, and the per-agent PUT/DELETE handlers block demoting visibility: global β†’ team or deleting the current platform default via isPlatformDefaultAgent(id) with 409 / AGENT_IS_PLATFORM_DEFAULTui/src/app/api/admin/platform-config/route.ts, ui/src/app/api/admin/platform-config/__tests__/route.test.ts, ui/src/components/admin/PlatformSettingsTab.tsx, ui/src/components/admin/__tests__/PlatformSettingsTab.test.tsx, ui/src/lib/rbac/platform-default.ts, ui/src/app/api/dynamic-agents/route.ts, ui/src/components/dynamic-agents/DynamicAgentEditor.tsx, ui/src/app/api/dynamic-agents/available/route.ts, ui/src/app/api/dynamic-agents/__tests__/route-rbac.test.ts, ui/src/lib/rbac/login-openfga-bootstrap.ts, ui/src/lib/rbac/__tests__/login-openfga-bootstrap.test.ts
Universal ReBAC resource/action vocabulary, including mcp_gateway:list, llm_model, relationship validation, OpenFGA tuple conversion, and Mongo collection helpers (spec 2026-05-11 identity group ReBAC foundation)ui/src/types/rbac-universal.ts, ui/src/lib/rbac/resource-model.ts, ui/src/lib/rbac/relationship-validator.ts, ui/src/lib/rbac/tuple-builders.ts, ui/src/lib/rbac/mongo-collections.ts, ui/src/lib/rbac/__tests__/rebac/resource-model.test.ts, ui/src/lib/rbac/__tests__/rebac/tuple-builders.test.ts, ui/src/lib/rbac/__tests__/mongo-collections.test.ts
Universal ReBAC resource catalog and enforcement-status transition visibilityui/src/lib/rbac/resource-catalog.ts, ui/src/lib/rbac/enforcement-status.ts, ui/src/app/api/admin/rebac/catalog/route.ts, ui/src/app/api/admin/rebac/enforcement-status/route.ts, ui/src/lib/rbac/__tests__/resource-catalog.test.ts, ui/src/app/api/admin/rebac/__tests__/catalog-route.test.ts, ui/src/app/api/admin/rebac/__tests__/enforcement-status-route.test.ts, tests/rbac/fixtures/rebac_resources.ts, tests/rbac/unit/ts/universal-rebac-matrix.test.ts
Universal ReBAC policy authoring, staged change-set validation/apply, relationship provenance, and guided Access Manager / policy graph UIui/src/lib/rbac/policy-rule-store.ts, ui/src/lib/rbac/policy-change-set-store.ts, ui/src/lib/rbac/policy-change-validator.ts, ui/src/app/api/admin/rebac/change-sets/route.ts, ui/src/app/api/admin/rebac/change-sets/[changeSetId]/validate/route.ts, ui/src/app/api/admin/rebac/change-sets/[changeSetId]/apply/route.ts, ui/src/app/api/admin/rebac/resources/[type]/[id]/relationships/route.ts, ui/src/components/admin/rebac/RebacPolicyBuilder.tsx, ui/src/components/admin/rebac/PolicyChangeSetDiff.tsx, ui/src/components/admin/OpenFgaRebacTab.tsx, ui/src/lib/rbac/__tests__/rebac/policy-change-validator.test.ts, ui/src/app/api/admin/rebac/__tests__/change-sets-route.test.ts, ui/src/app/api/admin/rebac/__tests__/resource-relationships-route.test.ts, tests/rbac/e2e/story-policy-authoring.spec.ts
Universal ReBAC graph filtering, relationship provenance visualization, read-only Slack/Webex team-routing metadata overlays, catalog-driven resource visibility, effective can_* access edges, authorization-model topology layers, and access explanation. Team-scoped graph filters include both team:<slug>#member and team:<slug>#admin usersets, and the policy graph derives supported resource types/actions from the universal model/catalog instead of a graph-local resource list.ui/src/lib/rbac/rebac-graph.ts, ui/src/lib/rbac/access-explainer.ts, ui/src/app/api/admin/rebac/graph/route.ts, ui/src/app/api/admin/rebac/check/route.ts, ui/src/app/api/admin/openfga/graph/route.ts, ui/src/components/admin/rebac/RebacGraphFilters.tsx, ui/src/components/admin/rebac/RebacAccessChecker.tsx, ui/src/components/admin/OpenFgaRebacTab.tsx, ui/src/app/api/admin/rebac/__tests__/graph-route.test.ts, ui/src/app/api/admin/rebac/__tests__/check-route.test.ts, tests/rbac/e2e/story-graph-and-access-checker.spec.ts
Identity Group Sync shared types, deterministic team slug helpers, stores, matcher/planner/reconciler, reviewed team creation, guarded member-only removal, OIDC claim login reconciliation and admin claim-suggestions preview, Okta directory connector, dual session/bearer admin gates, manual team membership provenance, scoped team-admin guards, API routes, UI tab, and fixturesui/src/types/identity-group-sync.ts, ui/src/lib/rbac/team-slugs.ts, ui/src/lib/rbac/identity-provider-store.ts, ui/src/lib/rbac/identity-group-sync-rule-store.ts, ui/src/lib/rbac/external-group-store.ts, ui/src/lib/rbac/team-membership-source-store.ts, ui/src/lib/rbac/team-admin-guards.ts, ui/src/lib/rbac/identity-group-rule-matcher.ts, ui/src/lib/rbac/identity-group-sync-planner.ts, ui/src/lib/rbac/identity-group-sync-reconciler.ts, ui/src/lib/rbac/oidc-claim-reconciler.ts, ui/src/lib/rbac/okta-directory-connector.ts, ui/src/app/api/admin/teams/route.ts, ui/src/app/api/admin/teams/[id]/route.ts, ui/src/app/api/admin/teams/[id]/members/route.ts, ui/src/app/api/admin/identity-group-sync/_lib.ts, ui/src/app/api/admin/identity-group-sync/*/route.ts, ui/src/app/api/admin/identity-group-sync/claim-suggestions/route.ts, ui/src/app/api/admin/identity-group-sync/teams/[teamId]/membership-sources/route.ts, ui/src/components/admin/TeamDetailsDialog.tsx, ui/src/components/admin/identity-group-sync/IdentityGroupSyncTab.tsx, ui/src/components/admin/identity-group-sync/MappingClusterEditor.tsx, ui/src/components/admin/identity-group-sync/DryRunPreview.tsx, ui/src/app/(app)/admin/page.tsx, ui/src/app/api/rbac/admin-tab-gates/route.ts, ui/src/hooks/useAdminTabGates.ts, tests/rbac/fixtures/identity_groups.ts, tests/rbac/fixtures/identity_groups.py, tests/rbac/e2e/identity-group-rebac-fixture.ts, tests/rbac/e2e/story-identity-group-sync.spec.ts, tests/rbac/e2e/story-manual-team-management.spec.ts, ui/src/lib/rbac/__tests__/identity-group-sync/, ui/src/app/api/admin/identity-group-sync/__tests__/, ui/src/components/admin/identity-group-sync/__tests__/, ui/src/app/api/admin/teams/__tests__/, ui/src/app/api/admin/teams/[id]/members/__tests__/
Slack channel ReBAC shared types, workspace alias normalization (SLACK_WORKSPACE_ALIAS), team-owned channel management gates, resource-scoped non-admin channel list/self-service Integrations tab, shared Slack/Webex connector onboarding wizard, OpenFGA source-of-truth channel-agent associations, dependent Mongo listen/priority route metadata, Slack Channel Association Default read/apply/discovered-channel onboarding APIs with per-channel team/agent overrides and Keycloak OBO repair, Web UI backend checks, runtime bot access-check delegation, editable/deleteable admin UI, runtime diagnostics, runtime evaluator, Slack bot admin runtime status/reload/config-sync/config-defaults APIs for legacy Slackbot migration prefill, and fixturesui/src/types/slack-rebac.ts, ui/src/lib/rbac/slack-channel-grant-store.ts, ui/src/lib/rbac/slack-channel-route-store.ts, ui/src/lib/rbac/slack-channel-rebac.ts, ui/src/lib/slack-bot-admin.ts, ui/src/app/api/admin/slack/channels/_lib.ts, ui/src/app/api/admin/slack/channels/route.ts, ui/src/app/api/admin/slack/channels/defaults/route.ts, ui/src/app/api/admin/slack/channels/[workspaceId]/[channelId]/resources/route.ts, ui/src/app/api/admin/slack/channels/[workspaceId]/[channelId]/routes/route.ts, ui/src/app/api/admin/slack/channels/[workspaceId]/[channelId]/diagnostics/route.ts, ui/src/app/api/admin/slack/channels/[workspaceId]/[channelId]/access-check/route.ts, ui/src/app/api/integrations/slack/channels/[workspaceId]/[channelId]/access-check/route.ts, ui/src/app/api/admin/slack/runtime/*/route.ts, ui/src/app/api/admin/slack/runtime/__tests__/config-defaults-route.test.ts, ui/src/components/admin/rebac/ConnectorOnboardingWizard.tsx, ui/src/components/admin/rebac/SlackChannelRebacPanel.tsx, ui/src/components/admin/rebac/__tests__/SlackChannelRebacPanel.test.tsx, ui/src/app/(app)/admin/page.tsx, ui/src/app/api/admin/teams/[id]/slack-channels/route.ts, ui/src/components/admin/TeamDetailsDialog.tsx, ai_platform_engineering/integrations/slack_bot/utils/slack_rebac.py, ai_platform_engineering/integrations/slack_bot/utils/slack_agent_routes.py, ai_platform_engineering/integrations/slack_bot/utils/slack_admin_api.py, ai_platform_engineering/integrations/slack_bot/utils/channel_team_mapper.py, ai_platform_engineering/integrations/slack_bot/utils/rbac_middleware.py, ai_platform_engineering/integrations/slack_bot/app.py, ui/src/app/api/admin/slack/channels/__tests__/channel-resources-route.test.ts, ui/src/app/api/integrations/slack/channels/[workspaceId]/[channelId]/__tests__/access-check-route.test.ts, ai_platform_engineering/integrations/slack_bot/tests/test_slack_channel_rebac.py, ai_platform_engineering/integrations/slack_bot/tests/test_slack_agent_routes.py, ai_platform_engineering/integrations/slack_bot/tests/test_slack_admin_api.py, tests/rbac/e2e/story-slack-channel-rebac.spec.ts, tests/rbac/fixtures/slack_rebac.ts
Slack/Webex bot team membership prechecks: Mongo mapping still resolves channel/space to the owning team, but team#member OpenFGA decisions now take precedence over legacy teams.members fallbackai_platform_engineering/integrations/slack_bot/utils/channel_team_resolver.py, ai_platform_engineering/integrations/slack_bot/tests/test_channel_team_resolver.py, ai_platform_engineering/integrations/webex_bot/utils/space_team_resolver.py, ai_platform_engineering/integrations/webex_bot/tests/test_space_team_resolver.py
Team β†’ Slack channel / Webex space visibility tuples (so previously-onboarded channels/spaces show as "Setup completed" in the admin panels): the admin listing routes (GET /api/admin/slack/channels, GET /api/admin/webex/spaces) filter each row by can_read on the channel/space OpenFGA object. Until this fix the onboarding writers only emitted the outbound slack_channel|webex_space β†’ user β†’ agent:<id> grant tuples; they never wrote any inbound visibility tuple onto the channel/space object, so can_read resolved to false for everyone and the panel silently dropped every configured row, showing it again as "Needs setup". The fix: (1) onboarding writers (defaults/route.ts for Slack, onboardWebexSpace for Webex) now also emit team:<slug>#admin β†’ manage (relation "manager") β†’ slack_channel|webex_space:<workspace>--<id> and team:<slug>#member β†’ use (relation "user") β†’ slack_channel|webex_space:<workspace>--<id> β€” matching the relation pair already written by the team-channels / team-spaces PUT endpoints so admin-PUT and onboarding-defaults converge on identical tuples; (2) the new messaging_team_visibility_v1 migration walks channel_team_mappings + webex_space_team_mappings and backfills those same tuples for previously-onboarded rows. slackChannelTeamVisibilityRelationships / webexSpaceTeamVisibilityRelationships are the shared builders so the writer and the backfill produce identical tuples. The migration is idempotent (OpenFGA no-ops on identical writes). Both user and reader relations grant can_read through the model's union, so any previously-injected reader tuples from manual repair remain effective and do not need to be rewritten.ui/src/lib/rbac/slack-channel-rebac.ts (slackChannelTeamVisibilityRelationships), ui/src/lib/rbac/webex-space-rebac.ts (webexSpaceTeamVisibilityRelationships), ui/src/lib/rbac/__tests__/slack/slack-channel-rebac.test.ts, ui/src/lib/rbac/__tests__/webex-space-rebac.test.ts, ui/src/app/api/admin/slack/channels/defaults/route.ts (emits visibility tuples per onboarded channel), ui/src/lib/rbac/webex-space-onboarding.ts (emits visibility tuples per onboarded space), ui/src/lib/rbac/migrations/registry.ts (MESSAGING_TEAM_VISIBILITY_MIGRATION_ID, deriveMessagingTeamVisibilityPlan), ui/src/lib/rbac/migrations/schema-area-classifications.ts (messaging_team_visibility), ui/src/lib/rbac/migrations/__tests__/messaging-migrations.test.ts
Keycloak-to-ReBAC transition helpers, curated-vs-raw user role display, engineer-facing enforcement comparison API, task/skill role filtering, Keycloak resource sync guardrails, and drift detectionui/src/lib/rbac/keycloak-transition.ts, ui/src/components/admin/UserManagementTab.tsx, ui/src/lib/rbac/enforcement-comparison.ts, ui/src/app/api/rbac/enforcement-comparison/route.ts, ui/src/lib/rbac/task-skill-realm-access.ts, ui/src/lib/rbac/keycloak-resource-sync.ts, ui/src/lib/rbac/drift-detection.ts, ui/src/lib/rbac/__tests__/rebac/keycloak-transition.test.ts, ui/src/lib/rbac/__tests__/rebac/task-skill-realm-access-transition.test.ts, ui/src/lib/rbac/__tests__/rebac/drift-detection.test.ts, ui/src/app/api/rbac/__tests__/enforcement-comparison-route.test.ts, ui/src/lib/rbac/__tests__/keycloak-resource-sync.test.ts
ReBAC admin API gates, audit emitters, RBAC matrix seed entries, Mongo index initialization, and universal ReBAC backfill/rollback scripts; the backfill prefers persisted users.keycloak_sub for OpenFGA subjectsui/src/app/api/admin/rebac/_lib.ts, ui/src/lib/rbac/audit.ts, tests/rbac/rbac-matrix.yaml, scripts/init-rbac-mongo-indexes.ts, scripts/backfill-universal-rebac.ts, scripts/__tests__/backfill-universal-rebac.test.ts, scripts/rollback-universal-rebac-tuples.ts
OpenFGA relationship backfill status and provenance collectionsMongoDB rbac_migrations, team_membership_sources, rebac_relationships
RBAC/ReBAC route drift validator, including OpenFGA Dynamic Agent execution route coveragescripts/validate-rbac-matrix.py, tests/rbac/rbac-matrix.yaml
Admin tab visibility gates (deterministic OpenFGA + feature flag checks, including admin_surface:credentials; repairs baseline member tuples, opens Slack/Webex for concrete resource managers, no CEL editor or admin_tab_policies)ui/src/lib/rbac/types.ts, ui/src/app/api/rbac/admin-tab-gates/route.ts, ui/src/app/api/rbac/admin-tab-gates/__tests__/route.test.ts, ui/src/hooks/useAdminTabGates.ts, ui/src/app/(app)/admin/page.tsx
DB-managed release migration control plane β€” Admin UI seeds/reads migration_manifest, hides completed migrations by default, exposes every MongoDB collection with its current data_schema_versions row plus runtime migration targets, offers metadata-only v1 initialization for unversioned schema areas, requires schema-area classification guardrails for new collections, records schema_migrations, surfaces admin-only migration-required/header version-metadata alerts through GET /api/rbac/migration-status, exposes Keycloak reconciliation diagnostics and one-click Reconcile now repair in the dedicated Security & Policy β†’ Keycloak tab through GET /api/admin/keycloak/migration-health, and keeps the audited super-admin override API at POST /api/admin/rebac/migrations/override; migration handlers remain code-backed for conversation owner normalization, direct organization membership backfill for existing users, universal team-resource OpenFGA backfill, Dynamic Agent tool tuple reconciliation, Dynamic Agent organization-admin inheritance, Slack/Webex messaging ReBAC backfills, messaging team mapping reconciliation, RBAC/messaging index creation, and BFF-owned Keycloak RBAC mapping reconciliation (keycloak_rbac_mapping_reconciliation_v1)ui/src/app/(app)/admin/page.tsx, ui/src/app/(app)/admin/__tests__/admin-page.test.tsx, ui/src/components/admin/MigrationTab.tsx, ui/src/components/admin/KeycloakMigrationHealthPanel.tsx, ui/src/components/admin/__tests__/KeycloakMigrationHealthPanel.test.tsx, ui/src/components/admin/__tests__/MigrationTab.test.tsx, ui/src/components/layout/AppHeader.tsx, ui/src/components/layout/__tests__/AppHeader.test.tsx, ui/src/hooks/use-migration-status.ts, ui/src/app/api/rbac/migration-status/route.ts, ui/src/app/api/rbac/migration-status/__tests__/route.test.ts, ui/src/app/api/admin/keycloak/migration-health/route.ts, ui/src/app/api/admin/rebac/migrations/route.ts, ui/src/app/api/admin/rebac/migrations/status/route.ts, ui/src/app/api/admin/rebac/migrations/override/route.ts, ui/src/app/api/admin/rebac/migrations/version-bootstrap/apply/route.ts, ui/src/app/api/admin/rebac/migrations/[migrationId]/plan/route.ts, ui/src/app/api/admin/rebac/migrations/[migrationId]/apply/route.ts, ui/src/lib/rbac/migrations/registry.ts, ui/src/lib/rbac/migrations/types.ts, ui/src/lib/rbac/migrations/schema-area-classifications.ts, ui/src/lib/rbac/migrations/__tests__/registry-guardrails.test.ts, ui/src/lib/rbac/keycloak-rbac-reconciliation.ts, ui/src/lib/rbac/keycloak-migration-health.ts, ui/src/lib/rbac/migrations/conversation-owner-identity.ts, ui/src/lib/rbac/migrations/__tests__/agent-organization-inheritance.test.ts, ui/src/lib/rbac/migrations/__tests__/messaging-migrations.test.ts, ui/src/lib/rbac/conversation-implicit-authz.ts, ui/src/app/api/admin/rebac/__tests__/migrations-route.test.ts, ui/src/lib/rbac/__tests__/keycloak-rbac-reconciliation.test.ts, ui/src/lib/rbac/migrations/__tests__/conversation-owner-identity.test.ts, ui/src/lib/rbac/__tests__/conversation-implicit-authz.test.ts
AgentGateway policy modelOpenFGA ext_authz only; CEL policy CRUD and Mongo sync bridge are retired
Slack OBO token exchange (RFC 8693)ai_platform_engineering/integrations/slack_bot/utils/obo_exchange.py
Slack identity auto-bootstrap + JIT branch + manual link fallback (spec 103)ai_platform_engineering/integrations/slack_bot/utils/identity_linker.py
Slack bot Keycloak Admin REST client β€” user-by-slack_user_id lookup, set_user_attribute, and JIT create_user_from_slack (spec 103). Uses KEYCLOAK_SLACK_BOT_ADMIN_CLIENT_ID/_SECRET (surface-specific prefix leaves room for future Webex/Teams bots), distinct from UI Web UI backend's KEYCLOAK_ADMIN_*ai_platform_engineering/integrations/slack_bot/utils/keycloak_admin.py
Slack-bot email masking helper (privacy-safe log redaction, spec 103 FR-010)ai_platform_engineering/integrations/slack_bot/utils/email_masking.py
Slack bot Helm wiring: Socket-mode Slack tokens, SLACK_RBAC_ENABLED, JIT flags, Keycloak bot OBO secret (KEYCLOAK_BOT_CLIENT_SECRET), Keycloak Admin lookup secret, and ExternalSecret-backed Vault keys such as projects/caipe/rbac/slackbotcharts/ai-platform-engineering/charts/slack-bot/values.yaml, charts/ai-platform-engineering/charts/slack-bot/templates/deployment.yaml, charts/ai-platform-engineering/charts/slack-bot/templates/external-secret.yaml
JIT feature flags β€” Docker Compose (dev + prod)docker-compose.dev.yaml and docker-compose.yaml (slack-bot service env)
Keycloak realm-management role pinning for service-account-caipe-platform (view-users, query-users, manage-users) β€” bootstrap shell establishes the first-run baseline; BFF TypeScript migrations own ongoing reconciliation/diagnosticscharts/ai-platform-engineering/charts/keycloak/scripts/init-idp.sh (_ensure_caipe_platform_user_roles block), ui/src/lib/rbac/keycloak-rbac-reconciliation.ts, ui/src/lib/rbac/keycloak-migration-health.ts
Keycloak realm-management role pinning β€” declarative source of truthcharts/ai-platform-engineering/charts/keycloak/realm-config.json and deploy/keycloak/realm-config.example.json (clientRoles.realm-management for service-account-caipe-platform)
Slack account linking callback and admin unlink actionui/src/app/api/auth/slack-link/route.ts, ui/src/app/api/admin/slack/users/[id]/route.ts, ui/src/components/admin/UserDetailModal.tsx
Webex account linking callback, nonce store, and 1:1 Adaptive Card bootstrapui/src/app/api/auth/webex-link/route.ts, ui/src/lib/rbac/webex-link-nonce.ts, ai_platform_engineering/integrations/webex_bot/utils/identity_linker.py, ai_platform_engineering/integrations/webex_bot/webex_responder.py
Slack channel β†’ team routing + OpenFGA-backed channel-agent dispatch, setup-mode Slack response suppression, and opt-in first-message auto-assignment for unmapped channelsai_platform_engineering/integrations/slack_bot/utils/channel_team_mapper.py, ai_platform_engineering/integrations/slack_bot/utils/slack_channel_auto_assign.py, ai_platform_engineering/integrations/slack_bot/tests/test_slack_channel_auto_assign.py, ai_platform_engineering/integrations/slack_bot/utils/slack_agent_routes.py, ai_platform_engineering/integrations/slack_bot/utils/slack_rebac.py, ai_platform_engineering/integrations/slack_bot/utils/slack_runtime_policy.py, ai_platform_engineering/integrations/slack_bot/tests/test_slack_route_no_silent_failure.py, ai_platform_engineering/integrations/slack_bot/app.py
Admin API: Keycloak identities and user admin actions (list/stats require admin_surface:users#can_read; user details require user_profile:<id>#can_read; product authorization is exposed through teams/OpenFGA, not role chips, a Roles tab, or a standalone Slack users tab; Users list exposes Slack linked / pending / unlinked filters)ui/src/app/api/admin/users/route.ts, ui/src/app/api/admin/users/[id]/route.ts, ui/src/lib/rbac/require-openfga.ts, ui/src/app/api/admin/users/[id]/teams/route.ts, ui/src/app/api/admin/users/[id]/roles/route.ts, ui/src/app/api/admin/users/[id]/role/route.ts, ui/src/app/api/admin/users/stats/route.ts, ui/src/components/admin/UserManagementTab.tsx, ui/src/components/admin/UserDetailModal.tsx, ui/src/components/admin/__tests__/UserDetailModal.test.tsx, ui/src/app/api/__tests__/admin-users.test.ts, ui/src/app/api/admin/users/__tests__/dual-auth-user-admin-routes.test.ts
Admin API: Prometheus metrics proxy (admin_surface:metrics#can_read instant and batch PromQL for the baseline Metrics & Health dashboard)ui/src/app/api/admin/metrics/route.ts, ui/src/lib/rbac/require-openfga.ts, ui/src/app/api/admin/metrics/__tests__/route.test.ts
Admin API: platform stats dashboards (admin_surface:stats#can_manage for the main stats dashboard; related skills/checkpoint stats routes still require privileged admin view)ui/src/app/api/admin/stats/route.ts, ui/src/app/api/admin/stats/skills/route.ts, ui/src/app/api/admin/stats/checkpoints/route.ts, ui/src/app/api/admin/stats/__tests__/stats-rbac-routes.test.ts
RBAC e2e port band + E2E_COMPOSE_ENV contract (spec 102)Makefile (test-rbac-up target) + spec 102 quickstart β€Ί E2E port band
RBAC e2e env-var substitutions inside the dev compose filedocker-compose.dev.yaml (search for MONGODB_HOST_PORT, SUPERVISOR_HOST_PORT, RBAC_FALLBACK_FILE, E2E_RUN)
Shared custom MCP auth middleware β€” JWT/shared-key validation and localhost dev bypass for embedded MCPsai_platform_engineering/agents/common/mcp-auth/mcp_agent_auth/middleware.py and ai_platform_engineering/agents/common/mcp-auth/mcp_agent_auth/pdp.py
Shared custom MCP auth package docs / operator knobs for local and embedded MCP serversai_platform_engineering/agents/common/mcp-auth/README.md
Spec 104 team/resource grants for gateway authorizationOpenFGA tuples written by Team Resources APIs and enforced through AgentGateway ext_authz
Keycloak seed (identity-only CAIPE realm; does not create CAIPE business/resource roles) and local cleanup helper for stale legacy rolescharts/ai-platform-engineering/charts/keycloak/scripts/init-idp.sh, scripts/cleanup-local-keycloak-legacy-roles.py
Spec 104 spec / acceptance criteriadocs/docs/specs/104-team-scoped-rbac/spec.md
Spec 104 Story 4 β€” Admin UI Team Resources API: GET/PUT /api/admin/teams/[id]/resources, persists team.resources = { agents, agent_admins, tools, tool_wildcard }, writes OpenFGA team-resource tuples, and does not mirror per-resource Keycloak rolesui/src/app/api/admin/teams/[id]/resources/route.ts
Skill Hub skill permission overlay β€” Team Resources exposes hub skills as skill:hub-<hub_id>-<hub_skill_id> grants; locally-created and .zip-imported team skills write skill#user tuples; Skill Hubs persist shared_with_teams, grant those teams on refresh, and backfill already-crawled hub skills via skill_hub_team_grants_backfill_v1. /api/skill-hubs lists hub metadata through admin_surface:skills#can_read, while hub mutations require admin_surface:skills#can_manage. /api/skills filters non-admin catalog/runtime responses through OpenFGA can_read/can_use, while built-in source=default catalog templates remain readable to signed-in users without per-template tuplesui/src/lib/rbac/skill-team-grants.ts, ui/src/app/api/skills/configs/route.ts, ui/src/app/api/skills/configs/import-zip/route.ts, ui/src/components/skills/ImportSkillZipDialog.tsx, ui/src/app/api/skill-hubs/route.ts, ui/src/lib/rbac/require-openfga.ts, ui/src/app/api/skill-hubs/[id]/route.ts, ui/src/app/api/skill-hubs/[id]/refresh/route.ts, ui/src/components/admin/SkillHubsSection.tsx, ui/src/components/admin/__tests__/SkillHubsSection.autoswitch.test.tsx, ui/src/lib/rbac/migrations/registry.ts, ui/src/app/api/admin/teams/[id]/resources/route.ts, ui/src/app/api/skills/route.ts, ui/src/app/api/skills/__tests__/runnable-gate.test.ts, ui/src/lib/rbac/__tests__/skill-team-grants.test.ts, ui/src/app/api/skill-hubs/__tests__/route-team-grants.test.ts, ui/src/app/api/skill-hubs/[id]/refresh/__tests__/route.test.ts
Spec 104 β€” Admin UI Team management dialog: Resources tab (Use+Manage per agent and MCP-server tool prefixes) backed by OpenFGA team/resource relationships; there is no product Roles tabui/src/components/admin/TeamDetailsDialog.tsx
Spec 104 Story 4 β€” Keycloak Admin helpers retained for identity administration and legacy cleanup paths; product access is represented by OpenFGA relationshipsui/src/lib/rbac/keycloak-admin.ts
Spec 104 Story 4 β€” Team.resources = { agents, tools } Mongo schema fieldui/src/types/teams.ts
Spec 098 US9 β€” Team-owned Team Slack Channels API: GET/PUT /api/admin/teams/[id]/slack-channels, idempotent full-replace into channel_team_mappings, denormalises team.slack_channels, checks team#read/manage through OpenFGA, writes channel ownership tuples, and rejects channels already mapped to a different team (409). Agent grants live under Slack channel ReBAC.ui/src/app/api/admin/teams/[id]/slack-channels/route.ts
Spec 098 US9 β€” Admin UI Slack channel discovery (server-side conversations.list, in-process 60s cache, 503 if SLACK_BOT_TOKEN unset so UI falls back to manual ID entry)ui/src/app/api/admin/slack/available-channels/route.ts
Spec 098 US9 — Slack Channels tab inside the team-management dialog (live discovery picker and manual-ID fallback for channel→team binding only)ui/src/components/admin/TeamDetailsDialog.tsx (SlackChannelsPanel)
Spec 098 US9 β€” Team.slack_channels = [{ slack_channel_id, channel_name, slack_workspace_id }] denormalised count for team-card chipui/src/types/teams.ts
Spec 104 Story 4 β€” Admin UI dialog with the Resources tab (checkboxes for agents + per-MCP tool prefixes)ui/src/components/admin/TeamDetailsDialog.tsx
Spec 104 Story 4 β€” Jest coverage for resources API (auth gates, OpenFGA tuple reconciliation, and no Keycloak resource-role mirroring)ui/src/app/api/__tests__/admin-team-resources.test.ts, ui/src/lib/rbac/__tests__/openfga.test.ts
Team Knowledge Bases/Data Sources API β€” writes OpenFGA team:<slug>#member reader/ingestor/manager knowledge_base:<datasource_id> tuples before persisting team_kb_ownership metadataui/src/app/api/admin/teams/[id]/kb-assignments/route.ts, ui/src/app/api/admin/teams/[id]/kb-assignments/__tests__/route.test.ts
Strict UI object-level OpenFGA helper and BFF enforcement pass β€” conversation, skill, task, workflow-config-as-task, knowledge base, agent, LLM model, MCP server, tool, system config, self-service RAG datasource owner/team tuple reconciliation, RAG datasource list filtering, and search constraint injection before bearer-token proxying. The helper does not bypass checks for session role=admin and no longer has a test-only PDP bypass. Built-in tool catalog (GET /api/dynamic-agents/builtin-tools) is intentionally excluded from this pass β€” see the route comment for why (static catalog, not a permissioned object surface).ui/src/lib/rbac/resource-authz.ts, ui/src/lib/rbac/openfga-owned-resources.ts, ui/src/lib/rbac/__tests__/resource-authz.test.ts, ui/src/app/api/chat/**/route.ts, ui/src/app/api/skills/**/route.ts, ui/src/app/api/task-configs/route.ts, ui/src/app/api/workflow-configs/route.ts, ui/src/app/api/__tests__/task-workflow-rbac.test.ts, ui/src/app/api/rag/[...path]/route.ts, ui/src/app/api/rag/kb/[...path]/route.ts, ui/src/app/api/dynamic-agents/route.ts, ui/src/app/api/dynamic-agents/agents/[id]/route.ts, ui/src/app/api/dynamic-agents/builtin-tools/route.ts, ui/src/app/api/dynamic-agents/models/route.ts, ui/src/app/api/llm-models/route.ts, ui/src/app/api/llm-models/__tests__/route.test.ts, ui/src/app/api/dynamic-agents/__tests__/route-rbac.test.ts, ui/src/app/api/admin/platform-config/route.ts, ui/src/app/api/admin/platform-config/__tests__/route.test.ts
RAG server team/OpenFGA Knowledge Base checks (check_kb_datasource_access, inject_kb_filter, OpenFGA check/list-objects) β€” datasource-level RBAC for direct API and MCP requestsai_platform_engineering/knowledge_bases/rag/common/src/common/models/rbac.py, ai_platform_engineering/knowledge_bases/rag/server/src/server/rbac.py, ai_platform_engineering/knowledge_bases/rag/server/src/server/restapi.py, ai_platform_engineering/knowledge_bases/rag/server/src/server/tools.py, ai_platform_engineering/knowledge_bases/rag/server/tests/test_openfga_team_rebac.py
RAG hybrid ACL β€” per-document acl_tags filter (opt-in: RBAC_DOC_ACL_TAGS_ENABLED)ai_platform_engineering/knowledge_bases/rag/server/src/server/doc_acl.py
RAG hybrid ACL backfill β€” assigns acl_tags=["__public__"] to existing Milvus rows before flipping the flagscripts/rag-doc-acl-migration.py
RAG datasource display name (DataSourceInfo.name) β€” human-friendly label only, NEVER an authorization key. datasource_id remains the immutable RBAC/storage key used by Milvus filters, OpenFGA tuples, document metadata, and ingestor jobs.ai_platform_engineering/knowledge_bases/rag/common/src/common/models/rag.py
RAG datasource name derivation helpers (derive_friendly_name, derive_friendly_name_from_url) β€” used by ingestors at creation and by the rag-server for lazy-backfill of legacy rowsai_platform_engineering/knowledge_bases/rag/common/src/common/utils.py
RAG datasource rename API (PATCH /v1/datasource/{datasource_id} β€” display-label only, requires Role.ADMIN and check_kb_datasource_access(scope="admin"))ai_platform_engineering/knowledge_bases/rag/server/src/server/restapi.py
RAG Data Sources card β€” show friendly name with monospace datasource_id as secondary line, inline pencil renameui/src/components/rag/IngestView.tsx
Spec 104 (REMOVED by Phase 3 of spec 2026-05-24-derive-team-from-channel) β€” active_team JWT claim design / sequence diagrams / spike notes (historical)docs/docs/specs/104-team-scoped-rbac/active-team-design.md
Spec 104 (REMOVED) β€” Per-team Keycloak client scope provisioning (team-<slug> + hardcoded active_team mapper) was deleted from Helm/runtime init in Phase 3. The mechanism never shipped to production, so no realm has legacy scopes to clean upcharts/ai-platform-engineering/charts/keycloak/scripts/init-token-exchange.sh, deploy/keycloak/init-token-exchange.sh, charts/ai-platform-engineering/charts/keycloak/scripts/init-idp.sh
Spec 104 (REMOVED) β€” Web UI backend Keycloak Admin helpers for per-team client scopes were deleted; surviving file holds OBO-permission helpers onlyui/src/lib/rbac/keycloak-admin.ts
Spec 104 (REMOVED) β€” Web UI backend startup auto-sync of Keycloak team-* scopes / team-personal scope / orphan-scope deletion / audience-default selection was deleted in Phase 3. The surviving reconciliation migration covers OBO permission strategy, bot service-account impersonation roles, and bootstrap admin tuples onlyui/src/lib/rbac/keycloak-rbac-reconciliation.ts, ui/src/lib/rbac/keycloak-migration-health.ts, ui/src/app/api/admin/keycloak/migration-health/route.ts, ui/src/components/admin/KeycloakMigrationHealthPanel.tsx (startup called from ui/src/instrumentation.ts)
Spec 104 β€” Team slug field (immutable) + create/delete provisioning. (Phase 3 removed the Keycloak side of team CRUD; only Mongo + OpenFGA writes remain.)ui/src/types/teams.ts, ui/src/app/api/admin/teams/route.ts, ui/src/app/api/admin/teams/[id]/route.ts
Spec 104 (REMOVED) β€” Slack-bot OBO impersonate_user(active_team=...) and active_team claim verification were removed in Phase 3. Bot now mints a team-agnostic OBO tokenai_platform_engineering/integrations/slack_bot/utils/obo_exchange.py
Spec 104 β€” Slack channel β†’ team-slug resolver + per-user team membership pre-check with friendly bot denial copyai_platform_engineering/integrations/slack_bot/utils/channel_team_resolver.py, ai_platform_engineering/integrations/slack_bot/utils/user_messages.py
Spec 104 β€” Slack-bot _rbac_enrich_context (DM β†’ personal chain, group β†’ resolver, hard-deny on missing mapping or OBO failure, no SA fallback). Phase 3 dropped the active_team field from the enriched contextai_platform_engineering/integrations/slack_bot/app.py, ai_platform_engineering/integrations/slack_bot/utils/user_messages.py
Spec 104 (REMOVED) β€” RAG server UserContext.active_team claim wiring + extract_active_team_from_claims were deleted in Phase 3ai_platform_engineering/knowledge_bases/rag/common/src/common/models/rbac.py, ai_platform_engineering/knowledge_bases/rag/server/src/server/rbac.py, ai_platform_engineering/knowledge_bases/rag/server/src/server/restapi.py
Spec 104 β€” Dynamic-agents JWT middleware: accept aud=agentgateway + aud=caipe-platform. (Phase 3 stopped reading or logging the legacy active_team claim.)ai_platform_engineering/dynamic_agents/src/dynamic_agents/auth/jwks_validate.py, ai_platform_engineering/dynamic_agents/src/dynamic_agents/auth/jwt_middleware.py
Dynamic-agents vendored JWKS validator env-var contract β€” pins the resolution order for KEYCLOAK_URL (defaults to http://localhost:7080 β€” the documented in-pod trap), OIDC_ISSUER (verbatim if set, derived from KEYCLOAK_URL otherwise β€” the Kevin failure mode when the in-cluster URL differs from the browser-facing issuer), and KEYCLOAK_JWKS_URL / OIDC_JWKS_URL (explicit overrides win). The vendored copy is the runtime path Bearer-token validation actually executes; the shared copy has its own tests under tests/rbac/unit/py/test_jwks_validate.pyai_platform_engineering/dynamic_agents/src/dynamic_agents/auth/jwks_validate.py, ai_platform_engineering/dynamic_agents/tests/test_jwks_validate.py
Dynamic-agents Helm chart Keycloak/OIDC wiring β€” subchart values.yaml exposes config.KEYCLOAK_URL and config.OIDC_ISSUER, templates/configmap.yaml skips empty values so the in-code defaults still apply when an operator hasn't set them, templates/NOTES.txt prints a non-blocking helm install warning whenever either is empty, and the umbrella values.yaml defaults KEYCLOAK_URL to the bundled ai-platform-engineering-keycloak:8080 service so the vanilla install works out of the boxcharts/ai-platform-engineering/charts/dynamic-agents/values.yaml, charts/ai-platform-engineering/charts/dynamic-agents/templates/configmap.yaml, charts/ai-platform-engineering/charts/dynamic-agents/templates/NOTES.txt, charts/ai-platform-engineering/values.yaml, tests/test_dynamic_agents_chart_keycloak_env.py
Spec 104 β€” AGW delegates gateway authorization to OpenFGA through ext_authzdeploy/agentgateway/config.yaml, deploy/openfga/bridge/main.py
Spec 104 β€” Web UI backend writes OpenFGA user:<sub> member team:<slug> tuples when admins add/remove team members; no new team_member:<slug> realm-role assignmentsui/src/app/api/admin/teams/[id]/members/route.ts (writeTeamMembershipTuple)
Spec 104 β€” Slack-bot membership pre-check uses Mongo-backed channel/team membership; it no longer requires team_member:<slug> in the JWTai_platform_engineering/integrations/slack_bot/utils/channel_team_resolver.py, ai_platform_engineering/integrations/slack_bot/utils/rbac_middleware.py
Operational recovery β€” remove stale CAIPE business/resource realm roles from local Keycloak after the identity-only migrationscripts/cleanup-local-keycloak-legacy-roles.py
Roadmap β€” feasibility analysis of remote PDP options (OpenFGA / OPA / Cedar / Cerbos / keep CEL) for AGW external authzdocs/docs/security/rbac/feasibility-pdp-options.md

Webex Bot ReBAC Additions​

What you want to changeFile
Webex integration operator/API guidedocs/docs/api/webex-integration.md
Webex ReBAC shared types, resource/subject vocabulary, and Mongo collection namesui/src/types/webex-rebac.ts, ui/src/types/rbac-universal.ts, ui/src/types/mongodb.ts, ui/src/lib/rbac/mongo-collections.ts
Webex space grant store, route metadata store, OpenFGA subject/tuple-read helpers, and access-check helperui/src/lib/rbac/webex-space-grant-store.ts, ui/src/lib/rbac/webex-space-route-store.ts, ui/src/lib/rbac/webex-space-openfga.ts, ui/src/lib/rbac/webex-space-rebac.ts, ui/src/lib/rbac/__tests__/webex-space-openfga.test.ts, ui/src/lib/rbac/__tests__/webex-space-stores.test.ts
Webex Admin BFF and top-level Integrations UI: team-owned space management gates, resource-scoped non-admin space list/self-service Integrations tab, resources, routes, shared Slack/Webex connector onboarding wizard, diagnostics with one-click zero-agent/stale-metadata/listen-mode repairs, operator access-check, runtime bot access-check delegation, available-spaces discovery, one-shot onboarding/default convergence, Keycloak OBO repair, and runtime admin API proxyui/src/app/api/admin/webex/spaces/_lib.ts, ui/src/app/api/admin/webex/spaces/route.ts, ui/src/app/api/admin/webex/available-spaces/route.ts, ui/src/app/api/admin/webex/spaces/onboard/route.ts, ui/src/app/api/admin/webex/spaces/defaults/route.ts, ui/src/app/api/admin/webex/spaces/[workspaceId]/[spaceId]/resources/route.ts, ui/src/app/api/admin/webex/spaces/[workspaceId]/[spaceId]/routes/route.ts, ui/src/app/api/admin/webex/spaces/[workspaceId]/[spaceId]/diagnostics/route.ts, ui/src/app/api/admin/webex/spaces/[workspaceId]/[spaceId]/access-check/route.ts, ui/src/app/api/integrations/webex/spaces/[workspaceId]/[spaceId]/access-check/route.ts, ui/src/app/api/admin/webex/runtime/status/route.ts, ui/src/app/api/admin/webex/runtime/reload/route.ts, ui/src/app/api/admin/webex/runtime/sync-from-config/route.ts, ui/src/lib/rbac/webex-space-onboarding.ts, ui/src/lib/rbac/keycloak-admin.ts, ui/src/lib/webex-bot-admin.ts, ui/src/components/admin/rebac/ConnectorOnboardingWizard.tsx, ui/src/components/admin/rebac/WebexSpaceRebacPanel.tsx, ui/src/components/admin/rebac/__tests__/WebexSpaceRebacPanel.test.tsx, ui/src/app/api/admin/webex/spaces/__tests__/space-resources-route.test.ts, ui/src/app/api/integrations/webex/spaces/[workspaceId]/[spaceId]/__tests__/access-check-route.test.ts, ui/src/app/(app)/admin/page.tsx
Webex user linking and admin unlink/status displayui/src/app/api/auth/webex-link/route.ts, ui/src/app/api/admin/webex/users/[id]/route.ts, ui/src/app/api/admin/users/route.ts, ui/src/components/admin/UserManagementTab.tsx, ui/src/components/admin/UserDetailModal.tsx
Webex team-space binding UI and API β€” checks team#read/manage through OpenFGA and writes space ownership tuples for team members/adminsui/src/app/api/admin/teams/[id]/webex-spaces/route.ts, ui/src/components/admin/TeamDetailsDialog.tsx, ui/src/components/admin/__tests__/TeamDetailsDialog.webex.test.tsx
Webex OpenFGA ReBAC admin panel and graph supportui/src/components/admin/rebac/WebexSpaceRebacPanel.tsx, ui/src/components/admin/rebac/__tests__/WebexSpaceRebacPanel.test.tsx, ui/src/components/admin/OpenFgaRebacTab.tsx
Webex bot runtime gate, identity linking, OBO exchange, team resolver, friendly denial copy, ReBAC evaluator, audit logging, and Webex ID validationai_platform_engineering/integrations/webex_bot/app.py, ai_platform_engineering/integrations/webex_bot/utils/identity_linker.py, ai_platform_engineering/integrations/webex_bot/utils/obo_exchange.py, ai_platform_engineering/integrations/webex_bot/utils/space_team_resolver.py, ai_platform_engineering/integrations/webex_bot/utils/user_messages.py, ai_platform_engineering/integrations/webex_bot/utils/webex_rebac.py, ai_platform_engineering/integrations/webex_bot/utils/audit.py, ai_platform_engineering/integrations/webex_bot/utils/webex_ids.py
Webex route resolution, auto-assign, admin API, streaming client, threaded responder with friendly reason-code fallback, WDM websocket transport, and process entrypointai_platform_engineering/integrations/webex_bot/utils/webex_agent_routes.py, ai_platform_engineering/integrations/webex_bot/utils/webex_space_auto_assign.py, ai_platform_engineering/integrations/webex_bot/utils/webex_admin_api.py, ai_platform_engineering/integrations/webex_bot/a2a_client.py, ai_platform_engineering/integrations/webex_bot/webex_responder.py, ai_platform_engineering/integrations/webex_bot/webex_websocket.py, ai_platform_engineering/integrations/webex_bot/webex_wdm.py, ai_platform_engineering/integrations/webex_bot/main.py
Webex bot unit testsai_platform_engineering/integrations/webex_bot/tests/
Webex OpenFGA model and Helm model syncdeploy/openfga/model.fga, deploy/openfga/init/authorization-model.json, deploy/openfga-experiment/model.fga, deploy/openfga-experiment/init/authorization-model.json, charts/ai-platform-engineering/charts/openfga/authorization-model.json
Webex Keycloak clients, user profile attribute, OBO/token-exchange policy, and admin audience mappercharts/ai-platform-engineering/charts/keycloak/scripts/init-idp.sh, charts/ai-platform-engineering/charts/keycloak/realm-config.json, charts/ai-platform-engineering/charts/keycloak/values.yaml
Webex bot Helm, compose, CI, Dockerfile, and secrets examplecharts/ai-platform-engineering/charts/webex-bot/, charts/ai-platform-engineering/Chart.yaml, charts/ai-platform-engineering/values.yaml, charts/ai-platform-engineering/charts/caipe-ui/values.yaml, charts/ai-platform-engineering/charts/caipe-ui/values-external-secrets.yaml, docker-compose.dev.yaml, docker-compose.yaml, .github/workflows/ci-webex-bot.yml, build/Dockerfile.webex-bot, deploy/secrets-examples/webex-secret.yaml.example
Webex RBAC matrix and e2e fixturestests/rbac/rbac-matrix.yaml, tests/rbac/fixtures/webex_rebac.ts, tests/rbac/e2e/story-webex-space-rebac.spec.ts

Personal DM Experience (spec 2026-05-24-derive-team-from-channel)​

What you want to changeFile
BFF POST /api/user/check_agent_access β€” bot-only PDP endpoint that runs evaluateAgentAccess(subject, agent_id) (direct user grant β†’ team-union fallback) and returns {allowed, reason, path, matched_team_slug} so bots can decide DM access WITHOUT carrying active_team on the JWT. Used by Slack /caipe-use, Webex use, and the DM dispatch chain in both bots.ui/src/app/api/user/check_agent_access/route.ts, ui/src/app/api/user/check_agent_access/__tests__/route.test.ts
BFF GET /api/user/accessible-agents β€” bot-only list of agents the user can use, drives /caipe-list and Webex list. Returns {data: {agents:[...], total, page, page_size}}.ui/src/app/api/user/accessible-agents/route.ts
BFF GET/PUT /api/user/preferences β€” per-user saved DM-default agent (dm_default_agent_id); null clears the preference (FR-029a).ui/src/app/api/user/preferences/route.ts, ui/src/lib/rbac/user-preferences-store.ts
BFF requireAgentUsePermission team-union fallback (FR-038) — when direct user→agent grants miss, the Web UI now probes the user's team memberships via listUserTeamSlugs and accepts a team:<slug>#member can_use agent:<id> grant. Audit reason code ALLOW_TEAM_UNION.ui/src/lib/rbac/openfga-agent-authz.ts, ui/src/lib/rbac/types.ts, ui/src/lib/rbac/__tests__/openfga-agent-authz.test.ts
Slack bot DM agent resolver + override store + slash commands β€” dm_agent_resolver.py walks override β†’ saved-pref β†’ dm_agent_id β†’ default_agent_id re-checking the PDP at each step; dm_thread_overrides.py is the LRU-bounded in-process store (no TTL β€” explicit /caipe-use default to clear); slash_commands.py holds the pure handlers; command_rate_limiter.py is the per-user sliding-window limiter; app.py wires Bolt @app.command(...) to the pure handlers AND replaces _get_agent_id_for_dm() with the resolver chain in handle_dm_message. The middleware now honors require_mapping=False so commands work in unmapped channels.ai_platform_engineering/integrations/slack_bot/utils/dm_agent_resolver.py, ai_platform_engineering/integrations/slack_bot/utils/dm_thread_overrides.py, ai_platform_engineering/integrations/slack_bot/utils/dm_authz_client.py, ai_platform_engineering/integrations/slack_bot/utils/accessible_agents_client.py, ai_platform_engineering/integrations/slack_bot/utils/user_preferences_client.py, ai_platform_engineering/integrations/slack_bot/utils/slash_commands.py, ai_platform_engineering/integrations/slack_bot/utils/command_rate_limiter.py, ai_platform_engineering/integrations/slack_bot/app.py (slash_caipe_* handlers, _resolve_dm_agent_for_message, _override_key_for_dm), docs/docs/integrations/slack-manifest.md
Webex bot DM agent resolver + command dispatcher β€” webex_command_dispatcher.py parses text via text_commands.parse_command_text and routes to the same pure handlers as Slack (list / use / help / use default); app.py intercepts commands in handle_webex_message BEFORE route resolution so unmapped 1:1 spaces still work, returning the new REASON_COMMAND_HANDLED sentinel. ParsedWebexEvent now carries is_direct (derived from roomType=="direct") so the use <agent> command refuses to run in group spaces.ai_platform_engineering/integrations/webex_bot/app.py (CommandHandlerProtocol, WebexCommandHandled, REASON_COMMAND_HANDLED, ParsedWebexEvent.is_direct), ai_platform_engineering/integrations/webex_bot/utils/webex_command_dispatcher.py, ai_platform_engineering/integrations/webex_bot/utils/dm_agent_resolver.py, ai_platform_engineering/integrations/webex_bot/utils/dm_thread_overrides.py, ai_platform_engineering/integrations/webex_bot/utils/dm_authz_client.py, ai_platform_engineering/integrations/webex_bot/utils/accessible_agents_client.py, ai_platform_engineering/integrations/webex_bot/utils/text_commands.py, ai_platform_engineering/integrations/webex_bot/utils/command_rate_limiter.py, ai_platform_engineering/integrations/webex_bot/tests/test_webex_command_dispatcher.py, ai_platform_engineering/integrations/webex_bot/tests/test_runtime_gate.py
Dynamic Agents active_team log cleanup — jwt_middleware.py no longer logs active_team on the bearer-token-validated line because the claim no longer exists on the JWT. Triage of "wrong team" now uses the BFF-side channel→team mapping audit.ai_platform_engineering/dynamic_agents/src/dynamic_agents/auth/jwt_middleware.py
End-to-end smoke scripts β€” manual-walkthrough scripts that exercise the BFF preferences/check_agent_access endpoints alongside live Slack/Webex DMs and verify the dispatch chain end-to-end. Not wired into CI β€” they require a running dev stack and a real Slack workspace / Webex bot.tests/rbac/end_to_end/test_slack_dm.sh, tests/rbac/end_to_end/test_webex_1to1.sh, tests/rbac/end_to_end/test_webui_team_grant.sh, tests/rbac/end_to_end/_lib.sh, tests/rbac/end_to_end/README.md