Permissions and the Public Role
Loom's permission model is deliberately small: a Thread declares which roles can do which CRUD actions, and the runtime enforces those declarations on every request.
Declaring permissions
Each Thread has a permissions: block:
permissions:
- role: Admin
can: all
- role: Sales
can: [read, create, update]
- role: Public
can: [read]
can: accepts either the literal all (every CRUD action) or a list of [read, create, update, delete]. Anything else is rejected by loom check.
Roles come from the JWT
Loom's runtime expects a JWT in the Authorization: Bearer … header. The token's roles claim is parsed as a string array; that becomes the user's role set for the request.
{
"sub": "5e2b7d4f-8c1a-4b6f-9c3d-2a1b3c4d5e6f",
"email": "alice@example.com",
"roles": ["Admin"],
"iat": 1714670400,
"exp": 1714756800
}
How tokens get issued is up to you. Loom v0.1.0 does not ship a /auth/login endpoint; you write it yourself or integrate an external IdP.
The Public role
Public is special: when a Thread grants the Public role any action, every caller — authenticated or not, regardless of their actual roles — can perform it.
permissions:
- role: Public
can: [read]
- role: Admin
can: all
Anyone can list and read. Only Admin can create/update/delete. This is the standard "public catalogue, private mutations" pattern.
The behaviour is intentionally locked by a test (TestCan_PublicGrantAppliesToEveryone) — it can't drift accidentally.
Deny by default
If a Thread has no permissions block at all, nobody can touch it through the API. This is the safe default. Add at least one permission entry per Thread you want exposed.
What gets checked, when
| Step | Check |
|---|---|
| HTTP middleware | JWT signature + expiry |
| Per-handler entry | Can(user, thread, action) for the requested action |
| If denied | 403 Forbidden returned before any SQL runs |
Permission checks happen before the handler builds SQL, so a denied request never reaches the database — no risk of timing attacks based on row existence.
Field-level permissions
Loom v0.1.0 does not support field-level access control. If you need "users can read their own email but not anyone else's", that's application-level filtering in your handler logic; it isn't expressible in the Thread declaration.
A possible v0.2.0 feature is per-field role gating; if you need it, file an issue.