# Permissions & Groups Armature uses group-based RBAC. Permissions are granted to groups, and users inherit the union of permissions from all groups they belong to. There are no per-user permission grants — all access flows through group membership. ## Groups ### System groups | Group | ID | Purpose | |-------|----|---------| | Everyone | `00000000-...0001` | Implicit membership for every authenticated user. Default permissions: `extension.use`, `workflow.submit`. | | Admins | `00000000-...0002` | Full platform access. Members receive all seven permission slugs. Replaces the legacy `role = admin` check. | Every new user is automatically added to **Everyone** on registration. Admin status is granted by adding a user to the **Admins** group in **Admin > People > Groups**. ### Custom groups Administrators can create additional groups under **Admin > People > Groups**. Each custom group has: - **Name** — display label - **Description** — purpose (shown in admin UI) - **Scope** — always `global` (team-scoped groups reserved for future use) - **Permissions** — zero or more permission slugs from the table below ## Permission slugs Seven platform permissions control access to kernel features: | Slug | Description | |------|-------------| | `surface.admin.access` | Full admin panel access (tabs, settings, package management) | | `admin.view` | Read-only admin panel access (monitoring, health, audit log) | | `extension.use` | Use installed extension surfaces and libraries | | `extension.install` | Install, update, enable, and disable packages | | `workflow.create` | Create and edit workflow definitions | | `workflow.submit` | Submit instances to public-link workflows | | `token.unlimited` | Bypass per-user token budgets (API rate limiting) | Permissions follow a `domain.action` naming convention. ## Permission resolution When a request arrives, the kernel resolves the effective permission set: 1. Fetch all groups the user belongs to (including Everyone). 2. Union all permission arrays across those groups. 3. Cache the result for the duration of the request. A user has a permission if **any** of their groups grants it. Frontend code checks permissions via `sw.can('slug')` — see the [Frontend JS Guide](FRONTEND-JS-GUIDE) for details. ## Extension permissions Separate from user permissions, each **package** can request sandbox capabilities. These are granted per-package in **Admin > Packages**: | Permission | Grants | |------------|--------| | `db.read` | Query `ext_data` tables (read-only) | | `db.write` | Insert, update, and delete rows in `ext_data` tables | | `api.http` | Make outbound HTTP requests from Starlark | | `notifications.send` | Send in-app notifications to users | | `secrets.read` | Read admin-configured extension secrets | | `realtime.publish` | Publish WebSocket events to subscribed clients | | `connections.read` | Read external connection configs (decrypted) | | `workflow.access` | Read workflow definitions and instances | See the [Starlark Reference](STARLARK-REFERENCE) for how these map to sandbox modules. ## Settings cascade Package settings use a three-tier resolution model: ``` user override → team override → global default ``` At each tier: - **Global** — set by admins in **Admin > Packages > Settings** - **Team** — set by team admins in **Team Admin > Settings** - **User** — set by users in **Settings > Extensions** ### The `user_overridable` flag Each setting key in a package manifest can declare `user_overridable`: ```json { "settings": [ { "key": "theme", "user_overridable": true }, { "key": "api_endpoint", "user_overridable": false } ] } ``` - `true` (default) — team and user scopes can override the global value. - `false` — only the global (admin) value is used. Team and user values are silently ignored during resolution. This gives administrators a lock mechanism: set `user_overridable: false` on security-sensitive keys to prevent lower scopes from changing them, while allowing cosmetic preferences to flow freely. ### Resolution algorithm 1. Start with the global value for each key. 2. For each key where `user_overridable` is true (or undeclared): - If a team-scoped value exists, it overrides global. - If a user-scoped value exists, it overrides team. 3. For keys where `user_overridable` is false: - Team and user values are discarded. 4. Unknown keys (not in schema) default to overridable.