ACL System¶
Klangk uses an Access Control List (ACL) system to manage permissions. Instead of simple admin/non-admin roles, permissions are defined as ACL entries (ACEs) attached to resources in a tree hierarchy. This allows fine-grained control over who can do what, without code changes.
Core Concepts¶
- Resources: paths in a tree that mirror the URL structure (
/,/workspaces,/workspaces/{id},/admin,/admin/users, etc.) - Principals: who the ACE applies to — a specific user, a group, or a system principal (
EveryoneorAuthenticated) - Permissions: what action is allowed or denied (e.g.,
view,create,edit,delete,terminal,files,chat,share,*) - ACEs:
(Allow/Deny, principal, permission)entries ordered by position on a resource - ACL walk: when checking permission, the system walks from the target resource up to
/, checking each node's ACEs in order. First match wins. If no match after reaching root, access is denied.
Resource Tree¶
/ (root)
├── /workspaces (workspace collection)
│ └── /workspaces/{id} (specific workspace)
├── /admin
│ ├── /admin/users
│ ├── /admin/invitations
│ └── /admin/groups
└── /auth (public — no ACL checks)
Default ACEs (seeded on first startup)¶
| Resource | Action | Principal | Permission |
|---|---|---|---|
/ |
Allow | Authenticated | view |
/ |
Deny | Everyone | * |
/workspaces |
Allow | Authenticated | create |
/admin |
Allow | group:admin | * |
/admin |
Deny | Everyone | * |
These defaults mean: any logged-in user can view pages and create workspaces; only members of the admin group can access admin functions; unauthenticated users are denied everything.
Groups¶
Groups replace the old role system. A group is a named collection of users. The built-in admin group is created automatically on first startup and the default admin user is added to it.
Admin UI: Admin > Groups tab — create/delete groups, add/remove members.
API endpoints:
GET /admin/groups— list all groupsPOST /admin/groups— create group{"name": "...", "description": "..."}DELETE /admin/groups/{id}— delete group (cascades: removes all ACEs referencing it)POST /admin/groups/{id}/members— add user{"user_id": "..."}DELETE /admin/groups/{id}/members/{user_id}— remove user
Workspace Permissions¶
When a workspace is created, the owner gets a (Allow, user:{id}, *) ACE on /workspaces/{id}. This grants full access: view, edit, delete, share, terminal, files, chat.
Sharing: the owner can share a workspace with users or groups. The simple sharing UI (Sharing tab) grants view, terminal, files, and chat. For finer control, the Advanced ACL editor lets you add/remove/reorder individual ACEs.
Tab visibility: workspace tabs (Terminal, Files, Chat, Sharing, Settings) are gated by per-resource permissions. A shared user without chat permission won't see the Chat tab.
Permissions checked on workspace resources:
| Permission | Controls |
|---|---|
view |
Can see the workspace exists |
terminal |
Can open a terminal / exec commands |
files |
Can browse/upload/download files |
chat |
Can see the Chat tab |
edit |
Can change workspace settings (name, image, command, mounts, env) |
share |
Can manage who has access (Sharing tab) |
delete |
Can delete the workspace |
* |
All of the above |
Checking Your Permissions¶
Web UI: the UI automatically shows/hides elements based on your permissions (admin button, workspace tabs, create button, etc.).
API: GET /api/my-permissions returns your effective permissions on all static resources. Add ?resource=/workspaces/{id} to check a specific resource.
CLI: klangkc ls --shared shows workspaces shared with you.
Troubleshooting: "Why can't I access this workspace?"¶
- Check your permissions:
GET /api/my-permissions?resource=/workspaces/{id}— does it include the permission you need? - Check the workspace ACL: in the Sharing tab, expand "Advanced: Access Control" to see the ACE list.
- Check group membership: are you in the right group? Admin > Groups tab shows group members.
- Check the ACL walk: permissions are inherited from parent resources. An ACE on
/applies to everything below it unless overridden. ADenyACE at a higher level blocks access even if a lower-levelAllowexists, if theDenyhas a lower position number. - Position matters: ACEs are checked in position order (lowest first). If a
Denyon position 0 matches before anAllowon position 1, access is denied. Use the ACL editor to reorder entries.