OpenClaw EnterpriseDOCSGitHub

Namespace IAM Policy Flow

Overview

Authenticated callers use the OCC API or CLI to list, read, or create Namespace Roles, exact AccessBindings, and ServicePrincipals, and to delete Roles and AccessBindings. OCC checks authority and Namespace ownership before the selected IAM Driver writes policy. Policy and audit commit in the same State transaction. Phase 6 covers configured policy, Group membership, Owner bindings, and deploy-time grants.

Entry Points

Flow

graph TD
  A["Caller invokes Namespace IAM route"] --> B["OCC admits identity and required permissions"]
  B --> C{"Read or mutation?"}
  C -->|read| D["Controller asks selected IAM Driver to read Namespace policy"]
  C -->|create/delete| S{"Human-session caller?"}
  S -->|yes| L{"Assigning session current?"}
  L -->|yes, held through transaction| CFI["Hold shared IAM configuration lock"]
  L -->|revoked or expired| X["Roll back and deny"]
  S -->|no| CFI
  CFI --> E["Controller validates Role, subject, and exact target"]
  E --> T{"IAM Driver joins transaction?"}
  T -->|yes| F["Selected IAM Driver mutates platform IAM policy"]
  T -->|no| U["Reject with 503 before policy write"]
  F --> G["Controller appends audit event in the same transaction"]
  D --> H["API returns policy metadata"]
  G -->|COMMIT validates held session| H
  G -->|held session expired| X
  M["Startup applies the iam configuration"] --> P["IAM Driver writes configured Roles, Groups, and Group bindings"]
  N["Caller creates a resource in an existing Namespace or deploys an Agent"] --> AC["OCC authorizes the exact operation under the Namespace lock"]
  AC --> O["OCC writes the Owner binding or missing Agent operate grants"]
  P --> Q["Policy and audit commit in the same transaction"]
  O --> Q

Execution Trace

1. Route admission and permission selection

apps/controller/src/index.ts:requiredPermissions

apps/controller/src/index.ts admits and resolves the caller before dispatch. OCC uses the selected IAM Driver to check Installation administer and exact Namespace read. Delegation requires both checks even when the caller can access the target.

2. Role and AccessBinding commands reach the controller

apps/controller/src/http/iam.ts:iamHandlers

List and read call OpenClawController IAM methods and return policy metadata. Create and delete use transactIAM and return after policy and attributable audit commit. Authentication wraps controller.transact for human sessions. It holds the session before the command and checks expiry after the command and audit append. For guarded profiles, packages/occ/src/state/human-authentication.ts:PostgresHumanAuthentication.holdSession validates the bound account and sign-in method through the actor guard. PostgreSQL locks the user in shared mode before locking the session. OCC then takes the shared IAM configuration lock before Namespace and account locks. Session holds preserve policy transaction timeouts. Account-management operations keep exclusive locks, a five-second lock limit, and a ten-second statement limit. Independent sessions can write different Namespaces concurrently. Logout and account or method revocation wait for protected writes, and a revocation that finishes first prevents the write. Expiry during the write or at server-side COMMIT validation rolls back policy and its success audit. The memory adapter serializes writes with local logout and rechecks the session inside the transaction. Non-session credentials use their usual admission path.

Mutation events record Installation administer in authorization. Role events name the Namespace and include roleId and permissions. AccessBinding events name the bound target, including the Namespace for a Namespace binding, and include bindingId, subjectKind, subjectId, and roleId. Deletion reads the removed row in the same transaction for audit. ServicePrincipal creation takes an empty body and audits the Namespace and servicePrincipalId. The non-Agent identity belongs to and appears only in its Namespace, starts without grants, and gains access through an AccessBinding. The service key flow issues and revokes its keys. The identity has no delete route.

3. OCC validates policy ownership

packages/occ/src/index.ts:holdIAMPolicyAuthority packages/occ/src/index.ts:createIAMAccessBinding

Each Role and AccessBinding mutation calls holdIAMPolicyAuthority, which takes the shared configuration fence before the Namespace lock and final checks. A configured-read withdrawal therefore commits before authorization or after the policy write. A denied write saves no policy change or success audit. Configured Roles still cannot grant Installation permissions.

Role creation accepts only nonempty, duplicate-free permissions for Namespace resource kinds; namespace permissions support only read. iamRolePermissions also refuses action/kind pairs outside SUPPORTED_PERMISSION_ACTIONS (contracts), because no operation checks them, and then any create Permission, because create is checked on the Namespace and this API binds only exact resources (NAMESPACE_POLICY_CREATE_REASON). AccessBinding creation accepts identity subjects and exact targets in the same Namespace, including the Namespace itself when the target ID matches the path Namespace. OCC verifies the target resource exists and that the caller can read it before asking the IAM Driver to create the binding. assertAccessBindingRoleApplies then refuses, with 400, a Role that has no Permission for the target's kind, or a create Permission stored before Role creation refused them, because evaluation would drop those grants.

4. The IAM Driver persists or reads policy

packages/iam/src/index.ts:NativeIAMDriver

The native IAM Driver uses the platform policy repository. Before calling a mutation method, OCC returns 503 if the selected Driver lacks namespacePolicyTransaction: "platform-unit-of-work". Reads need no declaration. The native Driver rejects missing Roles, cross-Namespace targets, unsupported subjects, duplicate IDs, referenced Role deletion, and unknown exact bindings. HTTP maps IAMPolicyValidationError to 400 INVALID_REQUEST with the offending field as the detail path for invalid Role or binding input. Deleting a referenced Role raises IAMRoleInUseError (409 RESOURCE_CONFLICT). Human Principals can receive bindings directly. ServicePrincipal subjects must belong to the exact Namespace.

PostgresPlatformState.loadNativeIAMState reuses the original transaction inside a State callback, so authorization sees pending grants and removals. Outside a callback it opens a read transaction. Escaped work fails on the closed transaction after commit, rollback, or an unknown outcome, without obtaining a new client. Phases 2 and 3 describe write ordering. Human HTTP writes use holdAuthenticationSession in the original PostgreSQL unit.

readNativeIAMState caches a policy snapshot per transaction. A read-only REPEATABLE READ unit reuses it for every decision and lookup, including every item in a list. A writing unit reuses it until the next statement, so IAM writes and locks precede the next read. Decisions sharing a snapshot miss intervening commits. freezeSnapshot makes it immutable. The native Driver validates and indexes it once by ID and issuer/subject for identities, by subject for bindings, and by action and kind for Restrictions. The next unit reads fresh policy.

packages/occ/src/index.ts:OpenClawController.updateAgent checks Agent update again after locking the Namespace. Namespace IAM writes take the same lock. If a binding withdrawal commits first and the caller has no other applicable update grant, the Agent save returns 403 and leaves the draft unchanged.

5. Platform state commits policy and audit together

packages/occ/src/state/postgres-state.ts:PostgresPlatformState

PostgreSQL commits Role and AccessBinding writes with API audit. An unchanged-row UPDATE locks a held session, preserving its lifetime and authentication metadata. It queues the deferred occ.require_held_authentication_session constraint to check expiry with the database clock at COMMIT, including when the client delays COMMIT after its final authentication check. Expiry raises held_authentication_session_is_current. State maps it to 403, rolls back policy and success audit, and the API audits the denial separately. On unknown commit, State discards the connection without another query. OCC reports dependency failure. The caller must neither assume rollback nor replay the mutation. Later requests read current policy through the IAM Driver. The Namespace lock serializes grant creation with Namespace deletion. Exact targets retain their deletion locks. Deleting a target removes its bindings in the same transaction. Identity foreign keys protect bindings without expanding application-role privileges. packages/occ/src/iam-policy-cleanup.ts uses the caller's transaction for deletion audit projections and Namespace policy removal. Both adapters apply one subject rule on every AccessBinding write: a human without a Namespace, a non-Agent ServicePrincipal of the exact Namespace, or the ServicePrincipal of a live Agent there. PostgreSQL checks the owning Agent in the same query because its Agent owner key is deferred to commit. The in-memory adapter resolves subjects live through its resolveIAMIdentity lookup, so humans enrolled after construction can be bound, then falls back to identities provisioned at construction. Agent-owned ServicePrincipals resolve only through its current Agent state.

State also provides an opt-in Installation authority and native-IAM barrier for an original transaction. Its SQL supplier is unregistered, and the Namespace routes above do not use it. It does not protect these routes until the selected account, session, and policy writers join the same protocol.

6. Default users, owners, and deploy-time grants

packages/occ/src/index.ts:OpenClawController.bindCreator

Default users defines the behavior.

Debugging and Verification

Search documentation