Update to Control Plane
Snapshot Oct 30, 2026 · 00:05 UTC · version 1.2.0
Collection source: downloaded plugin package.
Instructions updated for access-control
Skill instructions — Now says: `get_permissions`, `create_group`, `edit_group`, `create_service_account`, `update_service_account`, `add_key_to_service_account`, and `invite_user_to_org`...
Observed in instructions or declared skills. Runtime behavior has not been tested.
Up to 10 plugins free · no card required.
All fields and original values.
Skill instructions
Oct 28, 2026 · 18:05 UTC
`create_group`, `edit_group`, `create_service_account`, `update_service_account`, `add_key_to_service_account`, and `invite_user_to_org` need `?toolsets=full`; reconnect with it or use the CLI. - `create_policy` builds **one** binding fr...
Oct 30, 2026 · 00:05 UTC
`get_permissions`, `create_group`, `edit_group`, `create_service_account`, `update_service_account`, `add_key_to_service_account`, and `invite_user_to_org` need `?toolsets=full`; reconnect with it or use the CLI. On core, `grant_workload...
Compare saved observations
Download comparison JSONSKILL.md text changes
− removed · + added
--- before +++ after @@ -5,7 +5,7 @@ # Access Control & Policies — Primary Skill -> **Tool availability:** `create_group`, `edit_group`, `create_service_account`, `update_service_account`, `add_key_to_service_account`, and `invite_user_to_org` need `?toolsets=full`; reconnect with it or use the CLI. +> **Tool availability:** `get_permissions`, `create_group`, `edit_group`, `create_service_account`, `update_service_account`, `add_key_to_service_account`, and `invite_user_to_org` need `?toolsets=full`; reconnect with it or use the CLI. On core, `grant_workload_secret_access` (`remove: true` revokes) grants a workload secret access through its identity and a policy, creating either when missing; `grant_cloud_access` creates no policy: it makes sure the workload has an identity and sets that identity's cloud provider access. A **policy** targets one resource kind and binds **permissions** to **principals** (users, groups, service accounts, workload identities). The common failure is a policy that **exists but grants nothing** — a wrong `targetKind`, permission name, or principal link fails with no error — so read the policy back after writing. @@ -41,7 +41,7 @@ **Valid target kinds:** the `targetKind` enum accepts all **33** resource kinds, but only the **20 kinds with permission schemas** (the ones `get_permissions` accepts) are meaningful targets: `workload, secret, gvc, identity, image, org, policy, group, serviceaccount, user, volumeset, domain, location, ipset, mk8s, cloudaccount, agent, auditctx, quota, task`. **Create vs update:** -- `create_policy` builds **one** binding from `addPermissions` × (`addUsers`/`addGroups`/`addServiceAccounts`/`addIdentities`) — providing only one side is an **error** (a binding needs both). It also requires **exactly one** target scope (`targetAll` / `targetLinks` / `targetQuery`). For several distinct bindings, use `update_policy` `addBindings`. +- `create_policy` builds **one** binding from `addPermissions` × (`addUsers`/`addGroups`/`addServiceAccounts`/`addIdentities`) — providing only one side is an **error** (a binding needs both). It takes the target scope as `targetAll`, `targetLinks`, or `targetQuery`; they may be combined, and `targetAll` wins. For several distinct bindings, use `update_policy` `addBindings`. - `update_policy` **merges** bindings (matched by exact permission set — you can't extend a set) but **replaces** targets (`targetLinks` wholesale, `removeTargetLinks` incremental, `targetAll` — mutually exclusive). ## Permissions @@ -77,7 +77,7 @@ ### Users (IDP-backed) - **Invite:** `invite_user_to_org` (`email`, optional `groupName`). -- **Read / remove:** `get_resource` (kind="user") / `delete_resource` (kind="user") take **`identifier`** (id or email); `list_resources` (kind="user") has an `email` filter. +- **Read / remove:** `get_resource` (kind="user") / `delete_resource` (kind="user") take the user's `name` exactly as `list_resources` (kind="user") shows it; `list_resources` has no filter argument, so find the user in its table. ## Built-ins (seeded per org)
Full technical diff · 1 changed fields
changed /skill_md_contents
"---\nname: access-control\ndescription: \"Primary skill for access control, policies, and RBAC on Control Plane. Use when the user asks about permissions, policies, service accounts, user access, group membership, bindings, who can do what, least-privilege, or IAM.\"\n---\n\n# Access Control & Policies — Primary Skill\n\n> **Tool availability:** `create_group`, `edit_group`, `create_service_account`, `update_service_account`, `add_key_to_service_account`, and `invite_user_to_org` need `?toolsets=full`; reconnect with it or use the CLI.\n\nA **policy** targets one resource kind and binds **permissions** to **principals** (users, groups, service accounts, workload identities). The common failure is a policy that **exists but grants nothing** — a wrong `targetKind`, permission name, or principal link fails with no error — so read the policy back after writing.\n\n## The model\n\n| Layer | Scope | Controls |\n|---|---|---|\n| **Billing-account roles** | account-wide (set on the billing account, separate from org policies) | `billing_admin`, `billing_viewer`, `org_creator` — none grant org-resource access |\n| **Org policies** | per resource kind | all day-to-day access |\n\nA caller is allowed an action when some policy on the resource's kind has a binding that lists that action (or a permission implying it) **and** names the caller — or a group the caller is in.\n\n## Policy anatomy\n\nA policy = one **`targetKind`** + a **target scope** + **bindings**.\n\n```yaml\nkind: policy\nname: app-secret-access\ntargetKind: secret # exactly one resource kind\ntargetLinks: # OR `target: all` OR `targetQuery:` (pick one)\n - //secret/database-url\nbindings: # ≤ 50\n - permissions: [reveal, use]\n principalLinks: # 1–200\n - //gvc/production/identity/app-identity\n```\n\n**Limits:** `bindings` ≤ 50; `principalLinks` 1–200 per binding; `targetLinks` ≤ 200. `origin` is read-only — `default` (yours) or `builtin` (locked; see Built-ins).\n\n**Target scope (pick one):** `target: all` (org-wide roles) · `targetLinks` (specific resources) · `targetQuery` (tag query — see **query-spec**).\n\n**Valid target kinds:** the `targetKind` enum accepts all **33** resource kinds, but only the **20 kinds with permission schemas** (the ones `get_permissions` accepts) are meaningful targets: `workload, secret, gvc, identity, image, org, policy, group, serviceaccount, user, volumeset, domain, location, ipset, mk8s, cloudaccount, agent, auditctx, quota, task`.\n\n**Create vs update:**\n- `create_policy` builds **one** binding from `addPermissions` × (`addUsers`/`addGroups`/`addServiceAccounts`/`addIdentities`) — providing only one side is an **error** (a binding needs both). It also requires **exactly one** target scope (`targetAll` / `targetLinks` / `targetQuery`). For several distinct bindings, use `update_policy` `addBindings`.\n- `update_policy` **merges** bindings (matched by exact permission set — you can't extend a set) but **replaces** targets (`targetLinks` wholesale, `removeTargetLinks` incremental, `targetAll` — mutually exclusive).\n\n## Permissions\n\n**`get_permissions` (`kind`) is the source of truth** — it returns the kind's exact permission list and its implication map. Confirm names with it before writing a policy; never hand-write them, and don't assume a kind only has `create` / `delete` / `edit` / `view` / `manage`.\n\nTwo traps that don't need a lookup:\n- **`manage` implies every permission** for a kind — grant it only to true admins.\n- **Secret values need `reveal`, not `read`** — the most common mistake.\n\nMany kinds add non-obvious permissions beyond CRUD — e.g. secret `reveal`/`use`, image `pull`, workload `connect`/`exec.*`, serviceaccount `addKey`, user `invite`/`impersonate`, mk8s `clusterAdmin` — so pull the real set with `get_permissions`.\n\n## Principals\n\n| Type | Link |\n|---|---|\n| User | `//user/EMAIL` |\n| Group (preferred) | `//group/NAME` |\n| Service account | `//serviceaccount/NAME` |\n| Workload identity | `//gvc/GVC/identity/NAME` (**GVC-scoped** — never `//identity/NAME`) |\n\nAn **identity never belongs to a group**, so authorize it only with a binding that names its exact link. At runtime the attached identity is the workload's API credential: calls from inside the workload with the injected `CPLN_TOKEN` against `CPLN_ENDPOINT` carry exactly the permissions policies grant that identity — nothing more.\n\n### Groups\nMembers are **users and service accounts only** (≤ 200). Bind policies to groups, not individuals.\n- **Create / edit:** `create_group` (`name`, `memberLinks`, `memberQuery`, `identityMatcher`); `edit_group` (`addMemberLinks` / `removeMemberLinks` — read first with `get_resource` (kind=\"group\")).\n- **Dynamic:** `memberQuery` matches users by tag query; `identityMatcher` matches identities by a `jmespath`/`javascript` expression.\n\n### Service accounts (non-human / CI/CD)\n- **Key:** `add_key_to_service_account` (`serviceAccountName`, optional `keyDescription` and `groupName`) creates the SA if missing and returns the Console keys page, where the user adds the key and sees it once; the key never passes through the chat (lost = revoke + remint). `create_service_account` makes one with no key.\n- **Revoke:** `update_service_account` `removeKeys: [NAME]` (immediate). `delete_resource` (kind=\"serviceaccount\") revokes all keys.\n- **CI/CD auth:** store the key as the `CPLN_TOKEN` secret/env var — the CLI uses it ahead of any profile (and works without one); don't pass `--token` on the command line (it leaks into logs). Full setup: **gitops-cicd**.\n\n### Users (IDP-backed)\n- **Invite:** `invite_user_to_org` (`email`, optional `groupName`).\n- **Read / remove:** `get_resource` (kind=\"user\") / `delete_resource` (kind=\"user\") take **`identifier`** (id or email); `list_resources` (kind=\"user\") has an `email` filter.\n\n## Built-ins (seeded per org)\n\n| Resource | Name | Grants |\n|---|---|---|\n| Group | `superusers` | `manage` on every kind (creator auto-added) |\n| Group | `viewers` | `view` on every kind |\n| Service account | `controlplane` | platform-internal — off-limits |\n| Policies | `superusers-KIND` / `viewers-KIND` | `origin: builtin`, `target: all` |\n\n- **Grant org-admin by adding the principal to `superusers`** (or `viewers` for read-only) — don't recreate admin policies.\n- Built-in **policies** can't be created/edited/deleted; built-in **groups** can't be deleted, but their membership is editable (you can't remove yourself from `superusers`).\n- Any resource tagged **`cpln/protected=true` can't be deleted** until untagged.\n\n## Common RBAC patterns\n\n`create_policy` builds these; the `cpln apply -f` manifest is the policy-as-code equivalent for CI/CD.\n\n- **Org admin** — add to `superusers`. Scoped admin — one policy per `targetKind` with `[manage]`.\n- **GVC developer** — `workload` `[connect, create, delete, edit, exec, view]` + `secret` `[create, delete, edit, reveal, use, view]` on a `developers` group.\n- **Read-only** — add to `viewers`.\n- **CI/CD SA** — `workload` `[create, delete, edit, view]` + `image` `[create, pull, view]` + `secret` `[use, view]` on `//serviceaccount/cicd-deployer`.\n- **Workload identity secret access** — the anatomy example above; full flow in **setup-secret** (create identity, bind, attach via `spec.identityLink`).\n- **Auditor** — `policy` `[view]` + `auditctx` `[view]` on an `auditors` group.\n\n## Standard flow\n\n1. Confirm the **org** (and **gvc** for identities); never guess.\n2. `get_permissions` for the kind: confirm permission names.\n3. Read current state: `list_resources` (kind=\"policy\") / `get_resource` (kind=\"policy\") (+ `get_resource` kind=\"group\" / kind=\"serviceaccount\").\n4. Smallest change; if destructive, confirm blast radius.\n5. `create_policy` / `update_policy` (+ group / SA / user tools).\n6. **Verify:** read the policy back; confirm the bindings and target resolved.\n"
"---\nname: access-control\ndescription: \"Primary skill for access control, policies, and RBAC on Control Plane. Use when the user asks about permissions, policies, service accounts, user access, group membership, bindings, who can do what, least-privilege, or IAM.\"\n---\n\n# Access Control & Policies — Primary Skill\n\n> **Tool availability:** `get_permissions`, `create_group`, `edit_group`, `create_service_account`, `update_service_account`, `add_key_to_service_account`, and `invite_user_to_org` need `?toolsets=full`; reconnect with it or use the CLI. On core, `grant_workload_secret_access` (`remove: true` revokes) grants a workload secret access through its identity and a policy, creating either when missing; `grant_cloud_access` creates no policy: it makes sure the workload has an identity and sets that identity's cloud provider access.\n\nA **policy** targets one resource kind and binds **permissions** to **principals** (users, groups, service accounts, workload identities). The common failure is a policy that **exists but grants nothing** — a wrong `targetKind`, permission name, or principal link fails with no error — so read the policy back after writing.\n\n## The model\n\n| Layer | Scope | Controls |\n|---|---|---|\n| **Billing-account roles** | account-wide (set on the billing account, separate from org policies) | `billing_admin`, `billing_viewer`, `org_creator` — none grant org-resource access |\n| **Org policies** | per resource kind | all day-to-day access |\n\nA caller is allowed an action when some policy on the resource's kind has a binding that lists that action (or a permission implying it) **and** names the caller — or a group the caller is in.\n\n## Policy anatomy\n\nA policy = one **`targetKind`** + a **target scope** + **bindings**.\n\n```yaml\nkind: policy\nname: app-secret-access\ntargetKind: secret # exactly one resource kind\ntargetLinks: # OR `target: all` OR `targetQuery:` (pick one)\n - //secret/database-url\nbindings: # ≤ 50\n - permissions: [reveal, use]\n principalLinks: # 1–200\n - //gvc/production/identity/app-identity\n```\n\n**Limits:** `bindings` ≤ 50; `principalLinks` 1–200 per binding; `targetLinks` ≤ 200. `origin` is read-only — `default` (yours) or `builtin` (locked; see Built-ins).\n\n**Target scope (pick one):** `target: all` (org-wide roles) · `targetLinks` (specific resources) · `targetQuery` (tag query — see **query-spec**).\n\n**Valid target kinds:** the `targetKind` enum accepts all **33** resource kinds, but only the **20 kinds with permission schemas** (the ones `get_permissions` accepts) are meaningful targets: `workload, secret, gvc, identity, image, org, policy, group, serviceaccount, user, volumeset, domain, location, ipset, mk8s, cloudaccount, agent, auditctx, quota, task`.\n\n**Create vs update:**\n- `create_policy` builds **one** binding from `addPermissions` × (`addUsers`/`addGroups`/`addServiceAccounts`/`addIdentities`) — providing only one side is an **error** (a binding needs both). It takes the target scope as `targetAll`, `targetLinks`, or `targetQuery`; they may be combined, and `targetAll` wins. For several distinct bindings, use `update_policy` `addBindings`.\n- `update_policy` **merges** bindings (matched by exact permission set — you can't extend a set) but **replaces** targets (`targetLinks` wholesale, `removeTargetLinks` incremental, `targetAll` — mutually exclusive).\n\n## Permissions\n\n**`get_permissions` (`kind`) is the source of truth** — it returns the kind's exact permission list and its implication map. Confirm names with it before writing a policy; never hand-write them, and don't assume a kind only has `create` / `delete` / `edit` / `view` / `manage`.\n\nTwo traps that don't need a lookup:\n- **`manage` implies every permission** for a kind — grant it only to true admins.\n- **Secret values need `reveal`, not `read`** — the most common mistake.\n\nMany kinds add non-obvious permissions beyond CRUD — e.g. secret `reveal`/`use`, image `pull`, workload `connect`/`exec.*`, serviceaccount `addKey`, user `invite`/`impersonate`, mk8s `clusterAdmin` — so pull the real set with `get_permissions`.\n\n## Principals\n\n| Type | Link |\n|---|---|\n| User | `//user/EMAIL` |\n| Group (preferred) | `//group/NAME` |\n| Service account | `//serviceaccount/NAME` |\n| Workload identity | `//gvc/GVC/identity/NAME` (**GVC-scoped** — never `//identity/NAME`) |\n\nAn **identity never belongs to a group**, so authorize it only with a binding that names its exact link. At runtime the attached identity is the workload's API credential: calls from inside the workload with the injected `CPLN_TOKEN` against `CPLN_ENDPOINT` carry exactly the permissions policies grant that identity — nothing more.\n\n### Groups\nMembers are **users and service accounts only** (≤ 200). Bind policies to groups, not individuals.\n- **Create / edit:** `create_group` (`name`, `memberLinks`, `memberQuery`, `identityMatcher`); `edit_group` (`addMemberLinks` / `removeMemberLinks` — read first with `get_resource` (kind=\"group\")).\n- **Dynamic:** `memberQuery` matches users by tag query; `identityMatcher` matches identities by a `jmespath`/`javascript` expression.\n\n### Service accounts (non-human / CI/CD)\n- **Key:** `add_key_to_service_account` (`serviceAccountName`, optional `keyDescription` and `groupName`) creates the SA if missing and returns the Console keys page, where the user adds the key and sees it once; the key never passes through the chat (lost = revoke + remint). `create_service_account` makes one with no key.\n- **Revoke:** `update_service_account` `removeKeys: [NAME]` (immediate). `delete_resource` (kind=\"serviceaccount\") revokes all keys.\n- **CI/CD auth:** store the key as the `CPLN_TOKEN` secret/env var — the CLI uses it ahead of any profile (and works without one); don't pass `--token` on the command line (it leaks into logs). Full setup: **gitops-cicd**.\n\n### Users (IDP-backed)\n- **Invite:** `invite_user_to_org` (`email`, optional `groupName`).\n- **Read / remove:** `get_resource` (kind=\"user\") / `delete_resource` (kind=\"user\") take the user's `name` exactly as `list_resources` (kind=\"user\") shows it; `list_resources` has no filter argument, so find the user in its table.\n\n## Built-ins (seeded per org)\n\n| Resource | Name | Grants |\n|---|---|---|\n| Group | `superusers` | `manage` on every kind (creator auto-added) |\n| Group | `viewers` | `view` on every kind |\n| Service account | `controlplane` | platform-internal — off-limits |\n| Policies | `superusers-KIND` / `viewers-KIND` | `origin: builtin`, `target: all` |\n\n- **Grant org-admin by adding the principal to `superusers`** (or `viewers` for read-only) — don't recreate admin policies.\n- Built-in **policies** can't be created/edited/deleted; built-in **groups** can't be deleted, but their membership is editable (you can't remove yourself from `superusers`).\n- Any resource tagged **`cpln/protected=true` can't be deleted** until untagged.\n\n## Common RBAC patterns\n\n`create_policy` builds these; the `cpln apply -f` manifest is the policy-as-code equivalent for CI/CD.\n\n- **Org admin** — add to `superusers`. Scoped admin — one policy per `targetKind` with `[manage]`.\n- **GVC developer** — `workload` `[connect, create, delete, edit, exec, view]` + `secret` `[create, delete, edit, reveal, use, view]` on a `developers` group.\n- **Read-only** — add to `viewers`.\n- **CI/CD SA** — `workload` `[create, delete, edit, view]` + `image` `[create, pull, view]` + `secret` `[use, view]` on `//serviceaccount/cicd-deployer`.\n- **Workload identity secret access** — the anatomy example above; full flow in **setup-secret** (create identity, bind, attach via `spec.identityLink`).\n- **Auditor** — `policy` `[view]` + `auditctx` `[view]` on an `auditors` group.\n\n## Standard flow\n\n1. Confirm the **org** (and **gvc** for identities); never guess.\n2. `get_permissions` for the kind: confirm permission names.\n3. Read current state: `list_resources` (kind=\"policy\") / `get_resource` (kind=\"policy\") (+ `get_resource` kind=\"group\" / kind=\"serviceaccount\").\n4. Smallest change; if destructive, confirm blast radius.\n5. `create_policy` / `update_policy` (+ group / SA / user tools).\n6. **Verify:** read the policy back; confirm the bindings and target resolved.\n"
Full snapshot data
{
"description": "Primary skill for access control, policies, and RBAC on Control Plane. Use when the user asks about permissions, policies, service accounts, user access, group membership, bindings, who can do what, least-privilege, or IAM.",
"included_files": [],
"name": "access-control",
"skill_md_contents": "---\nname: access-control\ndescription: \"Primary skill for access control, policies, and RBAC on Control Plane. Use when the user asks about permissions, policies, service accounts, user access, group membership, bindings, who can do what, least-privilege, or IAM.\"\n---\n\n# Access Control & Policies — Primary Skill\n\n> **Tool availability:** `get_permissions`, `create_group`, `edit_group`, `create_service_account`, `update_service_account`, `add_key_to_service_account`, and `invite_user_to_org` need `?toolsets=full`; reconnect with it or use the CLI. On core, `grant_workload_secret_access` (`remove: true` revokes) grants a workload secret access through its identity and a policy, creating either when missing; `grant_cloud_access` creates no policy: it makes sure the workload has an identity and sets that identity's cloud provider access.\n\nA **policy** targets one resource kind and binds **permissions** to **principals** (users, groups, service accounts, workload identities). The common failure is a policy that **exists but grants nothing** — a wrong `targetKind`, permission name, or principal link fails with no error — so read the policy back after writing.\n\n## The model\n\n| Layer | Scope | Controls |\n|---|---|---|\n| **Billing-account roles** | account-wide (set on the billing account, separate from org policies) | `billing_admin`, `billing_viewer`, `org_creator` — none grant org-resource access |\n| **Org policies** | per resource kind | all day-to-day access |\n\nA caller is allowed an action when some policy on the resource's kind has a binding that lists that action (or a permission implying it) **and** names the caller — or a group the caller is in.\n\n## Policy anatomy\n\nA policy = one **`targetKind`** + a **target scope** + **bindings**.\n\n```yaml\nkind: policy\nname: app-secret-access\ntargetKind: secret # exactly one resource kind\ntargetLinks: # OR `target: all` OR `targetQuery:` (pick one)\n - //secret/database-url\nbindings: # ≤ 50\n - permissions: [reveal, use]\n principalLinks: # 1–200\n - //gvc/production/identity/app-identity\n```\n\n**Limits:** `bindings` ≤ 50; `principalLinks` 1–200 per binding; `targetLinks` ≤ 200. `origin` is read-only — `default` (yours) or `builtin` (locked; see Built-ins).\n\n**Target scope (pick one):** `target: all` (org-wide roles) · `targetLinks` (specific resources) · `targetQuery` (tag query — see **query-spec**).\n\n**Valid target kinds:** the `targetKind` enum accepts all **33** resource kinds, but only the **20 kinds with permission schemas** (the ones `get_permissions` accepts) are meaningful targets: `workload, secret, gvc, identity, image, org, policy, group, serviceaccount, user, volumeset, domain, location, ipset, mk8s, cloudaccount, agent, auditctx, quota, task`.\n\n**Create vs update:**\n- `create_policy` builds **one** binding from `addPermissions` × (`addUsers`/`addGroups`/`addServiceAccounts`/`addIdentities`) — providing only one side is an **error** (a binding needs both). It takes the target scope as `targetAll`, `targetLinks`, or `targetQuery`; they may be combined, and `targetAll` wins. For several distinct bindings, use `update_policy` `addBindings`.\n- `update_policy` **merges** bindings (matched by exact permission set — you can't extend a set) but **replaces** targets (`targetLinks` wholesale, `removeTargetLinks` incremental, `targetAll` — mutually exclusive).\n\n## Permissions\n\n**`get_permissions` (`kind`) is the source of truth** — it returns the kind's exact permission list and its implication map. Confirm names with it before writing a policy; never hand-write them, and don't assume a kind only has `create` / `delete` / `edit` / `view` / `manage`.\n\nTwo traps that don't need a lookup:\n- **`manage` implies every permission** for a kind — grant it only to true admins.\n- **Secret values need `reveal`, not `read`** — the most common mistake.\n\nMany kinds add non-obvious permissions beyond CRUD — e.g. secret `reveal`/`use`, image `pull`, workload `connect`/`exec.*`, serviceaccount `addKey`, user `invite`/`impersonate`, mk8s `clusterAdmin` — so pull the real set with `get_permissions`.\n\n## Principals\n\n| Type | Link |\n|---|---|\n| User | `//user/EMAIL` |\n| Group (preferred) | `//group/NAME` |\n| Service account | `//serviceaccount/NAME` |\n| Workload identity | `//gvc/GVC/identity/NAME` (**GVC-scoped** — never `//identity/NAME`) |\n\nAn **identity never belongs to a group**, so authorize it only with a binding that names its exact link. At runtime the attached identity is the workload's API credential: calls from inside the workload with the injected `CPLN_TOKEN` against `CPLN_ENDPOINT` carry exactly the permissions policies grant that identity — nothing more.\n\n### Groups\nMembers are **users and service accounts only** (≤ 200). Bind policies to groups, not individuals.\n- **Create / edit:** `create_group` (`name`, `memberLinks`, `memberQuery`, `identityMatcher`); `edit_group` (`addMemberLinks` / `removeMemberLinks` — read first with `get_resource` (kind=\"group\")).\n- **Dynamic:** `memberQuery` matches users by tag query; `identityMatcher` matches identities by a `jmespath`/`javascript` expression.\n\n### Service accounts (non-human / CI/CD)\n- **Key:** `add_key_to_service_account` (`serviceAccountName`, optional `keyDescription` and `groupName`) creates the SA if missing and returns the Console keys page, where the user adds the key and sees it once; the key never passes through the chat (lost = revoke + remint). `create_service_account` makes one with no key.\n- **Revoke:** `update_service_account` `removeKeys: [NAME]` (immediate). `delete_resource` (kind=\"serviceaccount\") revokes all keys.\n- **CI/CD auth:** store the key as the `CPLN_TOKEN` secret/env var — the CLI uses it ahead of any profile (and works without one); don't pass `--token` on the command line (it leaks into logs). Full setup: **gitops-cicd**.\n\n### Users (IDP-backed)\n- **Invite:** `invite_user_to_org` (`email`, optional `groupName`).\n- **Read / remove:** `get_resource` (kind=\"user\") / `delete_resource` (kind=\"user\") take the user's `name` exactly as `list_resources` (kind=\"user\") shows it; `list_resources` has no filter argument, so find the user in its table.\n\n## Built-ins (seeded per org)\n\n| Resource | Name | Grants |\n|---|---|---|\n| Group | `superusers` | `manage` on every kind (creator auto-added) |\n| Group | `viewers` | `view` on every kind |\n| Service account | `controlplane` | platform-internal — off-limits |\n| Policies | `superusers-KIND` / `viewers-KIND` | `origin: builtin`, `target: all` |\n\n- **Grant org-admin by adding the principal to `superusers`** (or `viewers` for read-only) — don't recreate admin policies.\n- Built-in **policies** can't be created/edited/deleted; built-in **groups** can't be deleted, but their membership is editable (you can't remove yourself from `superusers`).\n- Any resource tagged **`cpln/protected=true` can't be deleted** until untagged.\n\n## Common RBAC patterns\n\n`create_policy` builds these; the `cpln apply -f` manifest is the policy-as-code equivalent for CI/CD.\n\n- **Org admin** — add to `superusers`. Scoped admin — one policy per `targetKind` with `[manage]`.\n- **GVC developer** — `workload` `[connect, create, delete, edit, exec, view]` + `secret` `[create, delete, edit, reveal, use, view]` on a `developers` group.\n- **Read-only** — add to `viewers`.\n- **CI/CD SA** — `workload` `[create, delete, edit, view]` + `image` `[create, pull, view]` + `secret` `[use, view]` on `//serviceaccount/cicd-deployer`.\n- **Workload identity secret access** — the anatomy example above; full flow in **setup-secret** (create identity, bind, attach via `spec.identityLink`).\n- **Auditor** — `policy` `[view]` + `auditctx` `[view]` on an `auditors` group.\n\n## Standard flow\n\n1. Confirm the **org** (and **gvc** for identities); never guess.\n2. `get_permissions` for the kind: confirm permission names.\n3. Read current state: `list_resources` (kind=\"policy\") / `get_resource` (kind=\"policy\") (+ `get_resource` kind=\"group\" / kind=\"serviceaccount\").\n4. Smallest change; if destructive, confirm blast radius.\n5. `create_policy` / `update_policy` (+ group / SA / user tools).\n6. **Verify:** read the policy back; confirm the bindings and target resolved.\n"
}SHA-256 of public snapshot: 4d545aa272551d048ed29ed3259566ef23224477af4dadedf9216092f79e60e6