diff --git a/docs/roles-and-teams.md b/docs/roles-and-teams.md index 44b44bed..343c26a8 100644 --- a/docs/roles-and-teams.md +++ b/docs/roles-and-teams.md @@ -214,13 +214,69 @@ written once and the code moves every day. - `bun run docs:check` — the permission count claim in `CLAUDE.md` moves whenever the registry does. - Any schema phase: verify by query per host, never by `db:push`'s exit code. +## Teams — decided 2026-08-23 + +The four open questions were answered by the owner. Recorded here so phase 4 is implementation rather +than another round of design. + +**A team owns a shared folder.** Not only an authorization grouping — storage is the part that was +actually wanted. Team *dashboards* are a probable later addition and were explicitly deferred, because +they need a permission shape nothing here has: people who can edit a dashboard's STRUCTURE versus people +who only see what it renders. Every grant today is read-or-write over a whole capability, so that is a new +model rather than a new level. Build the folder; leave room for the dashboards. + +**Any Admin may create a team**, not the owner alone. Teams are self-service for people already trusted to +administer the platform. This is what makes the quota section below necessary rather than optional: an +Admin creating a team is an Admin allocating disk. + +**A team may grant a capability the member's own role lacks** — the union, per D6 — with one rule on top: +**whoever creates the team can only attach capabilities they hold themselves.** Escalation is capped at the +creator's own reach rather than the whole registry. `execution` and `admin` stay ungrantable regardless, +and `confined` still resolves to nothing for an account with no Linux user. + +**Deleting a role is refused while anyone holds it, in a team or globally.** The same rule D4 already +applies to global holders, extended rather than special-cased; the refusal should name the teams. + +### Two things phase 4 must not get wrong + +**The shared folder is enforced by the kernel, not by the grant.** Every role already holds `files` at +`write` — they are the seeded `confined` defaults — so the capability is not what keeps one team out of +another's folder. A Linux group per team is. The grant says whether someone can use the files surface at +all; group membership says which folders they reach. Both are required and they are not substitutes. + +**The grant cache is keyed on ROLE today**, deliberately: "at most one entry per role, and a new account +needs no warm-up" (`permissions/authorize.ts`). Teams end that — effective permissions become role plus +memberships, which is per-user. That property is easy to lose without noticing and awkward to recover, so +it should be a decision in the implementation rather than a side effect. + +## Quotas — the model, and the prerequisite nobody can skip + +The owner declares platform-level defaults and maxima: per user, and per team. Admins allocate inside +them. A change re-applies at the Linux level through `POST /users/:id/provision-linux`, which already +exists as a retry path and becomes "re-apply current policy to this account". + +**Enforcement is the kernel's job, not the platform's.** Members hold `terminal` at `write` and have their +own Linux accounts, so a quota the platform tracks by counting bytes through the file browser is +decorative — a member opens a terminal or SSHes in and writes past it. Only filesystem quotas bind. + +**Which means a filesystem that can carry them.** Checked on 2026-08-23: neither `officer-dev` nor `alpha` +can. Both are ext4 with homes on the root filesystem, no `usrquota`/`grpquota`/`prjquota` mount options, +and neither has the ext4 `quota` or `project` feature. Enabling `project` needs `tune2fs -O project` on an +UNMOUNTED filesystem, and `/` cannot be unmounted on a running server — so it is a rescue boot, not a +config change. + +The cheap moment is when a host is built, by giving the Linux homes their own filesystem with quota +features from the start. Retrofitting a live host is expensive and gets more so with every account on it. + +**Where this actually matters is `edge-pertento`** — the first real multi-user deployment, and the only +one that will have accounts that are not the owner's. `officer-dev` is a development machine and will not +be multi-user; alpha is the owner's own. Check edge-pertento's disk before it gains more accounts. + +**On a host that cannot enforce, the platform says so** — "policy recorded, not enforceable here" — rather +than storing numbers that do nothing. A feature that silently does nothing is worse than one that admits +it cannot. + ## Open questions -1. **Does a team have its own capabilities, or only its own levels?** Whether `team_permissions` can grant a - capability the account's global role does not have at all. Union says yes; that may be more than wanted. -2. **Who may create a team?** Owner only, or any Admin. Bears on whether a team is an administrative object - or a self-service one. -3. **Does a team own anything?** A team folder, a team dashboard. The answer decides whether teams are - purely an authorization grouping or become a storage boundary — and the second is a much larger feature. -4. **What happens to a user's team memberships when a role is deleted?** D4 refuses to delete a role anyone +None outstanding for phase 4. The four that were here are answered above. holds globally; the same question exists per team and is not answered here.