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
DJ already has a working RBAC engine, wired into the request path and switched on in config. The default policy is permissive, so it allows everything today. The work below turns it into enforced write-governance, one namespace at a time.
What exists today
The data model: roles, role_scopes, and role_assignments tables. Principals are rows in users tagged by kind (user, service account, group).
Five actions (read, write, execute, delete, manage) with a hierarchy and node/namespace scopes matched by cascading wildcard.
Resolution that folds in group membership and skips expired assignments, behind pluggable group-membership and authorization services.
An AccessChecker that endpoints call to register a request and check it.
Service accounts, role/scope/assignment and group APIs, History events, and soft-delete on roles.
#2230 merged: MANAGE checks on role/group administration.
Any actual denial. With permissive defaults a missing grant still allows the action, and there is no per-namespace, per-action restrictive scoping in production yet. (steps 4 and 7)
Auto-ownership on create. Creating a node or namespace assigns no role. node_owners is display metadata the RBAC service never reads. (step 5)
Full enforcement coverage. Some mutating endpoints never call check(), use the wrong action/resource, or mutate through deployment/background paths. (step 0)
An ownership backfill from current owners. (step 6)
A management UI for roles, scopes, assignments, or groups. (not in the MVP)
Read and execute gating. (not in the MVP)
The sequence below is the plan to turn it on safely.
Problem - metadata and ownership governance
Right now anyone can edit or delete anyone else's nodes, and nothing marks who owns a production node. Confidentiality is already handled downstream, so this is about protecting node definitions and giving them owners. Reads and queries stay as they are. The query engine still governs data access.
The plan is to enforce write/delete/manage first, leave read/execute permissive, and turn governance on one namespace at a time. A request is only protected if every path to the mutation performs a correct authorization check.
How a request is decided
Owning a namespace is a role. Creating or provisioning a namespace gives its owner principal a role with MANAGE on the namespace boundary.
RBAC/group administration is fail-closed:
admins are allowed through the logged break-glass path
otherwise the caller needs an explicit MANAGE grant containing every scope they create, assign, change, or revoke
permissive fallback and the default-access role cannot authorize grant administration
For an ordinary metadata request:
admins are allowed, and the bypass is logged
an explicit principal/group/service-account grant allows it
with no explicit grant, a matching restrictive scope denies it
with no restrictive match, the default-access role can grant fallback access
with no fallback grant, the permissive default allows it
Reads and queries stay permissive. For the MVP, restrictive scopes remain config-backed via RESTRICTIVE_SCOPES. A DB-backed registry can follow if self-service governance is needed.
WRITE/DELETE/MANAGE can be restrictive while READ/EXECUTE stay permissive.
Restrictive matching remains exact by action.
Validate all rules at startup.
Define one supported scope grammar and reject ambiguous patterns such as finance*, .*, and **.
Resolve the root mismatch: finance.* does not cover the finance namespace object, and namespace:* does not currently cover nodes.
Rebase and fix the current server CI failure before review.
Auto-ownership / namespace provisioning.
Use one owner role per namespace boundary.
Use project/repo owner groups for repo-backed namespaces.
Give deploy identities WRITE/DELETE, without MANAGE.
Reserve creator auto-ownership for personal or ad hoc namespaces.
Do not upgrade parent WRITE into child MANAGE merely because a caller creates a resource.
Grant roles in the same transaction as provisioning.
Keep node_owners as display/contact metadata.
Backfill ownership.
Use team/repo owner groups instead of node_owners or created_by_id.
Run a repeatable sync with a dry-run artifact.
Record which assignments are sync-managed so stale groups and renamed principals can be reconciled without deleting manual exceptions.
Include broad grants, observed writers, deploy identities, conflicts, and missing groups in the artifact.
Turn on write-governance per namespace.
Start with a non-overlapping Git-backed namespace.
Scope owner and deploy principals before enforcement.
Run audit mode first and log actor, route, action, resource, matched policy, matched grant, decision, and reason.
Require expected owner/deploy writes to succeed and synthetic non-owner writes to deny.
Require zero unclassified mutation paths and zero unexpected denials.
Test rollback from enforcement to audit before expanding.
Open decisions (need sign-off)
Route-coverage guard shape (step 0)
Restrictive scopes only protect mutations that reach a correct authorization check. Route enumeration catches missing checks, but it cannot prove that the action/resource is correct or cover deployment and background paths. Decision needed: choose the initial route guard and the long-term internal mutation invariant. Current lean: begin with route enumeration and a reviewed allowlist, then require authorization context in the internal mutation path.
Audit-mode contract (step 7)
Audit mode must resolve access as if enforcement were active, retain the matched grant/policy and reason, and still allow the request. Decision needed: define log fields, metrics, and the go/no-go threshold. Current lean: require one representative audit window, zero unexpected owner/deploy denials, zero unclassified mutation paths, and successful negative canaries.
Owner-role granularity (step 5)
Per-node roles are precise but create large role/assignment churn. Namespace roles align with rollout and team ownership. Decision needed: choose per-node roles or one role per namespace boundary. Current lean: use one role per namespace boundary, with per-node grants as explicit exceptions.
Owner-role namespace level (step 5)
Wildcard grants cascade, so choosing an ancestor can hand the first creator an entire subtree. Decision needed: grant at the leaf, first existing ancestor, or an explicit governance boundary. Current lean: require an explicit boundary for team/repo namespaces and use the leaf only for approved personal/ad hoc cases.
Governed-namespace cold start (step 5)
Once a parent is governed, creating its first child requires an existing WRITE/MANAGE grant. Decision needed: define who provisions the first governed namespace and owner assignment. Current lean: use admin-seeded roots plus pre-registered owner groups. Avoid permissive-until-first-owner.
Admin seeding per deployment (step 2)
Break-glass is the recovery path if grants or policy configuration are wrong. Decision needed: define how each deployment seeds and verifies its admin set. Current lean: make admin seeding and a tested bypass an activation prerequisite, with bypass use logged separately.
Ownership backfill source of truth (step 6) node_owners and created_by_id contain historical individuals and deploy bots, so they are not reliable grant sources. Decision needed: choose the authoritative mapping from namespaces to owner groups and deploy identities. Current lean: use team/repo ownership groups and deploy identities, reconciled by a repeatable sync with a dry-run artifact.
Day-2 grant workflow before UI
Owners need a supported way to grant and revoke access before a management UI exists. Decision needed: choose raw APIs, a CLI, desired-state sync, or a combination. Current lean: provide a supported CLI or desired-state sync backed by the same APIs and History trail.
Scope and boundary semantics (steps 0, 4, and 5)
Today finance.* excludes the finance namespace object, namespace:* does not cover nodes, and ambiguous patterns can produce surprising grants. Decision needed: change matching so a namespace boundary covers its root, descendants, and contained nodes, or require exact plus wildcard scopes everywhere. Current lean: define boundary semantics directly, accept one documented pattern grammar, and reject overlapping governed boundaries for the first rollout.
Not in the MVP
A roles/assignments management UI.
Read-side restriction.
Nested governed boundaries.
Day-2 grants still need a supported CLI or sync while the UI remains out of scope.
This builds on @shangyian's proposal in #1575. The engine and data model are already there. The work here is enforcement and rollout.
This plan gates writes first, keeps reads with the query engine, defaults ownership to namespace boundaries, and adds coverage audit, audit mode, backfill, and gradual rollout.
Current state
DJ already has a working RBAC engine, wired into the request path and switched on in config. The default policy is
permissive, so it allows everything today. The work below turns it into enforced write-governance, one namespace at a time.What exists today
roles,role_scopes, androle_assignmentstables. Principals are rows inuserstagged bykind(user, service account, group).AccessCheckerthat endpoints call to register a request and check it.MANAGEchecks on role/group administration.What doesn't exist yet
Each gap notes the sequence step that closes it.
permissivedefaults a missing grant still allows the action, and there is no per-namespace, per-action restrictive scoping in production yet. (steps 4 and 7)MANAGEchecks, but the permissive fallback can still allow a broad symbolic scope such asnode:*, which can then override a governed namespace. (steps 0 and 1)node_ownersis display metadata the RBAC service never reads. (step 5)check(), use the wrong action/resource, or mutate through deployment/background paths. (step 0)The sequence below is the plan to turn it on safely.
Problem - metadata and ownership governance
Right now anyone can edit or delete anyone else's nodes, and nothing marks who owns a production node. Confidentiality is already handled downstream, so this is about protecting node definitions and giving them owners. Reads and queries stay as they are. The query engine still governs data access.
The plan is to enforce write/delete/manage first, leave read/execute permissive, and turn governance on one namespace at a time. A request is only protected if every path to the mutation performs a correct authorization check.
How a request is decided
Owning a namespace is a role. Creating or provisioning a namespace gives its owner principal a role with
MANAGEon the namespace boundary.RBAC/group administration is fail-closed:
MANAGEgrant containing every scope they create, assign, change, or revokeFor an ordinary metadata request:
Reads and queries stay permissive. For the MVP, restrictive scopes remain config-backed via
RESTRICTIVE_SCOPES. A DB-backed registry can follow if self-service governance is needed.Sequence
MANAGE; never use permissive fallback.MANAGEscopes.MANAGEfor Git configuration and owner changes.remove_complex_dimension_link, materialization, preagg, and cube writes.WRITEand node copy checking the full node name as a namespace.MANAGEon role/group admin APIs.node:*,namespace:*, and malformed broad patterns.WRITE/DELETE/MANAGEcan be restrictive whileREAD/EXECUTEstay permissive.finance*,.*, and**.finance.*does not cover thefinancenamespace object, andnamespace:*does not currently cover nodes.WRITE/DELETE, withoutMANAGE.WRITEinto childMANAGEmerely because a caller creates a resource.node_ownersas display/contact metadata.node_ownersorcreated_by_id.Open decisions (need sign-off)
Route-coverage guard shape (step 0)
Restrictive scopes only protect mutations that reach a correct authorization check. Route enumeration catches missing checks, but it cannot prove that the action/resource is correct or cover deployment and background paths.
Decision needed: choose the initial route guard and the long-term internal mutation invariant.
Current lean: begin with route enumeration and a reviewed allowlist, then require authorization context in the internal mutation path.
Audit-mode contract (step 7)
Audit mode must resolve access as if enforcement were active, retain the matched grant/policy and reason, and still allow the request.
Decision needed: define log fields, metrics, and the go/no-go threshold.
Current lean: require one representative audit window, zero unexpected owner/deploy denials, zero unclassified mutation paths, and successful negative canaries.
Owner-role granularity (step 5)
Per-node roles are precise but create large role/assignment churn. Namespace roles align with rollout and team ownership.
Decision needed: choose per-node roles or one role per namespace boundary.
Current lean: use one role per namespace boundary, with per-node grants as explicit exceptions.
Owner-role namespace level (step 5)
Wildcard grants cascade, so choosing an ancestor can hand the first creator an entire subtree.
Decision needed: grant at the leaf, first existing ancestor, or an explicit governance boundary.
Current lean: require an explicit boundary for team/repo namespaces and use the leaf only for approved personal/ad hoc cases.
Governed-namespace cold start (step 5)
Once a parent is governed, creating its first child requires an existing WRITE/MANAGE grant.
Decision needed: define who provisions the first governed namespace and owner assignment.
Current lean: use admin-seeded roots plus pre-registered owner groups. Avoid permissive-until-first-owner.
Admin seeding per deployment (step 2)
Break-glass is the recovery path if grants or policy configuration are wrong.
Decision needed: define how each deployment seeds and verifies its admin set.
Current lean: make admin seeding and a tested bypass an activation prerequisite, with bypass use logged separately.
Ownership backfill source of truth (step 6)
node_ownersandcreated_by_idcontain historical individuals and deploy bots, so they are not reliable grant sources.Decision needed: choose the authoritative mapping from namespaces to owner groups and deploy identities.
Current lean: use team/repo ownership groups and deploy identities, reconciled by a repeatable sync with a dry-run artifact.
Day-2 grant workflow before UI
Owners need a supported way to grant and revoke access before a management UI exists.
Decision needed: choose raw APIs, a CLI, desired-state sync, or a combination.
Current lean: provide a supported CLI or desired-state sync backed by the same APIs and History trail.
Scope and boundary semantics (steps 0, 4, and 5)
Today
finance.*excludes thefinancenamespace object,namespace:*does not cover nodes, and ambiguous patterns can produce surprising grants.Decision needed: change matching so a namespace boundary covers its root, descendants, and contained nodes, or require exact plus wildcard scopes everywhere.
Current lean: define boundary semantics directly, accept one documented pattern grammar, and reject overlapping governed boundaries for the first rollout.
Not in the MVP
Day-2 grants still need a supported CLI or sync while the UI remains out of scope.
Relation to #1575
This builds on @shangyian's proposal in #1575. The engine and data model are already there. The work here is enforcement and rollout.
This plan gates writes first, keeps reads with the query engine, defaults ownership to namespace boundaries, and adds coverage audit, audit mode, backfill, and gradual rollout.