diff --git a/content/docs/permissions/administrator-guide.mdx b/content/docs/permissions/administrator-guide.mdx index d273a20b426..dfbda774294 100644 --- a/content/docs/permissions/administrator-guide.mdx +++ b/content/docs/permissions/administrator-guide.mdx @@ -159,10 +159,13 @@ is a structured spreadsheet — you never hand-write JSON: - **Capabilities** — named grants like `manage_users` or `studio.access`. Prefer the smallest change that works, in this order: **bind an existing set -to a position** → **clone and adjust** → **author a new set**. Editing a set -that shipped with an app creates an *environment overlay* — your change wins, -survives upgrades, and can be reset back to the vendor baseline; the API name -is immutable after creation. And keep the additive model in mind: author +to a position** → **clone and adjust** → **author a new set**. A set shipped +in code — by an app you installed, or as a platform default such as +`member_default` — can't be edited in the environment (a save answers +`403 NOT_OVERRIDABLE`): clone it and adjust the clone, which is your own set +while upgrades keep reaching the original. Sets created here, clones, and sets +saved into a writable runtime package are edited in place. The API name is +immutable after creation. And keep the additive model in mind: author coherent capability bundles, never "subtraction sets". Full model: [Permission Sets](/docs/permissions/permission-sets). @@ -193,7 +196,8 @@ org structure, it's a business unit. **Can I delete the built-in permission sets?** No — sets like `member_default` and `organization_admin` are the platform baseline. Shipped -sets can be overlaid (Step 5) or simply left unassigned. +sets can't be edited in place either, but they can be cloned and adjusted +(Step 5) or simply left unassigned. ## Where is everything diff --git a/content/docs/permissions/permission-sets.mdx b/content/docs/permissions/permission-sets.mdx index bfd9350dc7c..e8c62feb8d0 100644 --- a/content/docs/permissions/permission-sets.mdx +++ b/content/docs/permissions/permission-sets.mdx @@ -309,7 +309,7 @@ A package ships its own sets (`managedBy: 'package'` + `packageId`), seeded idempotently at boot and re-seeded on upgrade; environment-authored sets (`platform`/`user`) are never clobbered. The data layer refuses forging package provenance through the admin door (two-doors separation, evolved by ADR-0094 — -ordinary edits of a packaged set become environment overlays, see below), which +an edit of a set shipped in code is refused; clone it instead, see below), which is what makes package uninstall well-defined — and enforced: uninstalling a package (`DELETE /api/v1/packages/:id`) revokes its own sets, their position/user bindings, and its pending audience-binding suggestions in the @@ -329,14 +329,16 @@ goes through the data engine) is redirected into a metadata write and the record is re-derived by an awaited projector, so the two can never drift. This has three author-visible consequences: -- **Editing any declared set through Setup — packaged sets included** becomes - an environment overlay of that definition (the standard metadata - customization): it genuinely takes effect, where a record-only edit - previously displayed but never enforced. The row keeps its package - provenance; the Studio layered view diffs the shipped baseline against your - customization, and removing the overlay resets to the baseline. Note the - trade every overlay makes: while an overlay pins a set, the vendor's later - baseline changes don't take effect for it until you reset or re-author. +- **A set shipped in code cannot be edited in the environment.** That is a set + an installed package declares (a `*.permission.ts` file, a stack's + `permissionSets`) or a platform default such as `member_default`. Editing it + through Setup or the metadata API answers `403 NOT_OVERRIDABLE` and stores + nothing. Clone it to a new name instead (the **Clone** action on the set, or + `POST /api/v1/data/sys_permission_set` with a new name) and edit the clone: + the clone is your organization's own set, and package upgrades keep reaching + the original. Every other set (one created in this environment, a clone, or + one saved into a writable runtime package) is edited in place: the edit + lands on its one stored definition, and the record follows it. - **The API name is immutable after creation.** It is the definition's metadata identity, so the create form accepts it but the edit form locks it, and a rename through the data door is rejected. Clone the set to a new name instead. @@ -348,8 +350,11 @@ has three author-visible consequences: packaged set entirely. A cross-package job function needs no hand-authored cross-package set: bind -each package's own sets to one **position** (the union model adds), and use an -overlay where a packaged set must be narrowed. +each package's own sets to one **position** (the union model adds). Where a +packaged set grants too much, clone it, narrow the clone, and bind the clone +in its place: restriction is done by not granting. Narrowing the packaged set +itself is the package author's change: they edit the declaration in the +package and re-publish it (ADR-0086 two-doors). ### Declared ≠ enforced — diagnosing a frozen package set