You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Decision (Jente, 2026-08-18) — refines the 2026-06-29 per-silo management-credential decision. The silo's Zitadel management authority is mode-scoped:
Standalone mode: the silo holds full access to its own Zitadel org (it is the whole deployment — org lifecycle, users, roles, grants).
Fleet mode: the silo holds a fine-grained service-user PAT that can only read and write claims (project role grants / user authorizations) for its own org. User add/remove stays fleet-owned — the fleet adds/removes silo users.
Today the silo holds no management credential at all (apps/_infra/deploy-k8s/values.yaml: the per-org login client "never holds an identity-provider management credential"), and silo-side Zitadel role-grant writes are explicitly deferred in libs/backend/server/iam/identity/main/src/mirror-groups.ts (referenced #126, now closed — this issue re-tracks that half). Consequence: org admins manage group roles in the Zitadel Console by hand, and claims are login-time-stale.
Design (from the micro-app identity discussion):
New port + adapter, e.g. libs/backend/server/iam/idp-management (domain-lib /main sub-package convention), consumed by the groups routes and later by micro-app access grants.
Zitadel is the source of truth for the access graph; enforcement stays local per-request. Every grant/revoke writes Zitadel through the port AND mirrors local rows in one $transaction (external call last), so revocation closes access immediately without waiting for re-login. mirror-groups.ts remains the inbound login-time sync.
Todo
Define the port with a mode-scoped capability surface keyed on OPENCRANE_MEMBERSHIP_MODE: fleet = { createProjectRole, grantAuthorization, revokeAuthorization, listAuthorizations } only; standalone = additionally full org/user management. Fail-closed Unavailable adapter when the credential Secret is absent (Obot-custody pattern).
Zitadel adapter: PAT (or JWT-profile key) auth, x-zitadel-orgid pinned to the silo's own org so cross-org calls are IdP-rejected, bounded failure taxonomy, release-local config only.
Verify and document the narrowest Zitadel manager role that permits authorization read/write WITHOUT user management (PROJECT_OWNER is broader than needed — check fine-grained/manager-role options against the live Zitadel version); record in the adapter README.
Wire the groups routes (libs/backend/server/iam/groups/main): platform-UI group mutations write Zitadel role grants through the port; Prisma rows + Zitadel call in one $transaction, external call last.
Keep per-request enforcement on local rows (guards/gateway); claims stay a login-time sync channel.
Helm: Secret mount contract (name agreed with the fleet-side issue), values wiring, k8s-deploy plumbing; docs update: website/security/identity.md + fleet-silo-model (supersedes "silo makes no Zitadel Management API calls").
Negative tests per the trust-boundary checklist: missing credential fails closed, cross-org write rejected, revoked grant closes access immediately, standalone-only ops rejected in fleet mode.
Depends on / paired with
Fleet-side provisioning of the per-silo claims-scoped service user + PAT (WeOwnAI repo issue).
Context
Decision (Jente, 2026-08-18) — refines the 2026-06-29 per-silo management-credential decision. The silo's Zitadel management authority is mode-scoped:
Today the silo holds no management credential at all (
apps/_infra/deploy-k8s/values.yaml: the per-org login client "never holds an identity-provider management credential"), and silo-side Zitadel role-grant writes are explicitly deferred inlibs/backend/server/iam/identity/main/src/mirror-groups.ts(referenced #126, now closed — this issue re-tracks that half). Consequence: org admins manage group roles in the Zitadel Console by hand, and claims are login-time-stale.Design (from the micro-app identity discussion):
libs/backend/server/iam/idp-management(domain-lib/mainsub-package convention), consumed by the groups routes and later by micro-app access grants.$transaction(external call last), so revocation closes access immediately without waiting for re-login.mirror-groups.tsremains the inbound login-time sync.Todo
OPENCRANE_MEMBERSHIP_MODE: fleet = { createProjectRole, grantAuthorization, revokeAuthorization, listAuthorizations } only; standalone = additionally full org/user management. Fail-closedUnavailableadapter when the credential Secret is absent (Obot-custody pattern).x-zitadel-orgidpinned to the silo's own org so cross-org calls are IdP-rejected, bounded failure taxonomy, release-local config only.libs/backend/server/iam/groups/main): platform-UI group mutations write Zitadel role grants through the port; Prisma rows + Zitadel call in one$transaction, external call last.website/security/identity.md+ fleet-silo-model (supersedes "silo makes no Zitadel Management API calls").Depends on / paired with