Namespace IAM Policy Flow
Overview
Namespace IAM policy management begins when an authenticated caller uses the OCC API or CLI to list, create, read, or delete a Namespace Role or exact AccessBinding. OCC authorizes the administrator, validates that the policy entry belongs to the requested Namespace, and delegates the policy mutation to the selected IAM Driver. The flow stops after the policy and audit event commit together in the platform state transaction.
Entry Points
- Trigger:
GET,POST, orDELETEunder/namespaces/:namespaceId/iam/* - Source:
packages/contracts/src/api/routes.ts:occApiRoutes - Source:
apps/controller/src/index.ts:requiredPermissions - Source:
apps/controller/src/http/iam.ts:iamHandlers - Assumptions: the caller is admitted to the Installation, the selected IAM Driver implements Namespace policy management, and the requested Namespace already exists.
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| E["Controller validates Role, subject, and exact target"]
E --> F["Selected IAM Driver mutates platform IAM policy"]
F --> G["Controller appends audit event in the same transaction"]
D --> H["API returns policy metadata"]
G --> HExecution Trace
1. Route admission and permission selection
apps/controller/src/index.ts:requiredPermissions
The API route declares IAM operations as Installation administration plus exact
Namespace read. Admission and identity resolution remain in
apps/controller/src/index.ts; they resolve the caller before dispatching to
the IAM handler. The OCC controller uses the selected IAM Driver for both
permission checks. Ordinary access to the target resource does not authorize
policy delegation.
2. Role and AccessBinding commands reach the controller
apps/controller/src/http/iam.ts:iamHandlers
List and read operations call the corresponding OpenClawController IAM method
and return policy metadata. Create and delete operations run inside
controller.transact, append an attributable mutation audit event, and return
only after the transaction commits. The event's authorization records the
Installation administer check. Role events carry the Namespace as resource and
roleId plus permissions in details. AccessBinding create and delete events
carry the bound target as resource (the Namespace for a Namespace binding) and
bindingId, subjectKind, subjectId, and roleId in details. Deletion reads
the removed Role or AccessBinding in the same transaction to record it.
3. OCC validates policy ownership
packages/occ/src/index.ts:createIAMAccessBinding
Role creation accepts only nonempty, duplicate-free permissions for Namespace
resource kinds; namespace permissions support only read. 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.
4. The IAM Driver persists or reads policy
packages/iam/src/index.ts:NativeIAMDriver
The native IAM Driver implements Namespace policy methods against the platform-provided policy repository. It rejects missing Roles, cross-Namespace targets, unsupported subjects, duplicate IDs, referenced Role deletion, and unknown exact bindings without weakening authorization. Existing human Principals can receive bindings without a Namespace service identity. ServicePrincipal subjects must belong to that exact Namespace.
When the selected native Driver reloads policy during a PostgreSQL State callback,
PostgresPlatformState.loadNativeIAMState reads through that State instance's
original transaction. Authorization therefore sees that unit's pending grants
and removals. Outside a callback, the loader opens its ordinary read transaction.
Work that escapes the callback retains its original closed lifetime and fails;
it cannot obtain another client after commit, rollback, or an unknown outcome.
This transaction binding supplies neither authenticated session custody nor a
fence against concurrent policy invalidation.
5. Platform state commits policy and audit together
packages/occ/src/state/postgres-state.ts:PostgresPlatformState
The PostgreSQL state implementation writes Roles and AccessBindings through the
same unit of work used by the API audit append. If commit outcome is unknown,
State discards the connection without another query. OCC reports dependency
failure; a caller must not infer rollback or replay the mutation from that
result. Later authorization requests read the current policy through the IAM
Driver. Namespace locking serializes grant creation with Namespace deletion;
exact resource targets retain their existing deletion locks, and deleting a
target resource deletes the bindings on it in the same transaction. Identity foreign
keys protect persisted bindings without expanding application-role privileges.
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.
Debugging and Verification
node --test tests/integration/occ-api.test.mjschecks the HTTP contract and API admission behavior for Namespace IAM policy.node --test tests/conformance/occ-api-security.test.mjschecks that Agent responses exposeservicePrincipalIdwithout accepting caller-supplied values.node --test tests/integration/postgres-namespace-iam-policy.test.mjschecks PostgreSQL persistence, audit atomicity, and deletion behavior with real state.node --test tests/conformance/postgres-transaction-unknown-commit.test.mjschecks that an unknown commit does not wait for a later rollback query.- A
403means the caller lacks Installation administration, exact Namespace read, or target read for binding creation. A409on Role deletion means a binding still references the Role.
