Owner asked for role/user management to be UI-configurable by an admin. Platform-role management already is — verified in source, so this issue is only the gap that remains.
What already works (do not rebuild)
/admin Users tab, backed by drb-c2-core/app/routers/users.py, is complete for platform roles:
Action
Backend
UI
List users
GET /admin/users (users.py:85)
Users tab
Create + invite
POST /admin/users (users.py:97)
app/admin/page.tsx:583+
Change role
PATCH /admin/users/{uid} (users.py:175)
role dropdown, page.tsx:462-474
Set owned nodes
same PATCH
shown when role is operator, page.tsx:474
Disable / enable
POST .../disable, .../enable (users.py:229,253)
Users tab
Delete
DELETE /admin/users/{uid} (users.py:273)
Users tab
Roles are admin / operator / viewer, all editable from the dropdown.
The actual gap: orgs are invisible to it
_format_user() (users.py:55-70) returns uid, email, display_name, role, owned_node_ids, disabled, timestamps and Discord link — no org_id, no org_role, no org name. Confirmed by grep: zero references to org_id / orgId / org_role anywhere under drb-frontend/app/admin/.
Consequences for a platform operator today:
Cannot tell which org any user belongs to, from any screen.
Cannot move a user between orgs.
Cannot set org role (owner vs member) from /admin — that lives on the separate /settings/members surface (org.py PATCH "", org.py:48), which is scoped to the caller's own org. So a platform admin managing someone else's org has no route at all.
Cannot answer "who is in this org" without reading org_members in the Firestore console.
This was flagged as an observation in ADMIN_BILLING_AUDIT.md section 1 on 2026-08-18 and has not changed since.
Why it matters now
Board minutes #54 ruled the binding constraint is support hours, not sales — at 5h/week any customer needing the owner personally is one the company cannot afford. Org administration done by hand in the Firestore console is exactly that kind of unaffordable per-customer time, and it scales linearly with customers.
Suggested scope
Add org_id, org_name and org_role to _format_user() and render them as a column plus a detail field.
Allow a platform admin to set a user's org and org role via the existing PATCH /admin/users/{uid}.
Add an org filter to the Users tab.
An organizations list is the natural companion but is genuinely separate — there is still no GET /admin/organizations and no route reads the organizations collection except org.py:38 (single org, caller-scoped).
Related, and deliberately not duplicated
#4 — /admin reads are not org-scoped (a data-leak problem; this issue is the inverse, org data being absent where it is wanted)
#9 — member/node writes are platform-admin-only, blocking org owners (the same seam from the org owner side)
Owner asked for role/user management to be UI-configurable by an admin. **Platform-role management already is** — verified in source, so this issue is only the gap that remains.
## What already works (do not rebuild)
`/admin` Users tab, backed by `drb-c2-core/app/routers/users.py`, is complete for platform roles:
| Action | Backend | UI |
|---|---|---|
| List users | `GET /admin/users` (`users.py:85`) | Users tab |
| Create + invite | `POST /admin/users` (`users.py:97`) | `app/admin/page.tsx:583+` |
| Change role | `PATCH /admin/users/{uid}` (`users.py:175`) | role dropdown, `page.tsx:462-474` |
| Set owned nodes | same PATCH | shown when role is `operator`, `page.tsx:474` |
| Disable / enable | `POST .../disable`, `.../enable` (`users.py:229,253`) | Users tab |
| Delete | `DELETE /admin/users/{uid}` (`users.py:273`) | Users tab |
Roles are `admin` / `operator` / `viewer`, all editable from the dropdown.
## The actual gap: orgs are invisible to it
`_format_user()` (`users.py:55-70`) returns `uid`, `email`, `display_name`, `role`, `owned_node_ids`, `disabled`, timestamps and Discord link — **no `org_id`, no `org_role`, no org name**. Confirmed by grep: **zero** references to `org_id` / `orgId` / `org_role` anywhere under `drb-frontend/app/admin/`.
Consequences for a platform operator today:
- Cannot tell which org any user belongs to, from any screen.
- Cannot move a user between orgs.
- Cannot set org role (`owner` vs `member`) from `/admin` — that lives on the separate `/settings/members` surface (`org.py PATCH ""`, `org.py:48`), which is scoped to the caller's own org. So a platform admin managing someone else's org has no route at all.
- Cannot answer "who is in this org" without reading `org_members` in the Firestore console.
This was flagged as an observation in `ADMIN_BILLING_AUDIT.md` section 1 on 2026-08-18 and has not changed since.
## Why it matters now
Board minutes #54 ruled the binding constraint is **support hours, not sales** — at 5h/week any customer needing the owner personally is one the company cannot afford. Org administration done by hand in the Firestore console is exactly that kind of unaffordable per-customer time, and it scales linearly with customers.
## Suggested scope
1. Add `org_id`, `org_name` and `org_role` to `_format_user()` and render them as a column plus a detail field.
2. Allow a platform admin to set a user's org and org role via the existing `PATCH /admin/users/{uid}`.
3. Add an org filter to the Users tab.
4. An organizations list is the natural companion but is genuinely separate — there is still no `GET /admin/organizations` and no route reads the `organizations` collection except `org.py:38` (single org, caller-scoped).
## Related, and deliberately not duplicated
- #4 — `/admin` reads are not org-scoped (a data-leak problem; this issue is the inverse, org data being absent where it is wanted)
- #9 — member/node writes are platform-admin-only, blocking org owners (the same seam from the org owner side)
- #3 — billing UI is mock data
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Owner asked for role/user management to be UI-configurable by an admin. Platform-role management already is — verified in source, so this issue is only the gap that remains.
What already works (do not rebuild)
/adminUsers tab, backed bydrb-c2-core/app/routers/users.py, is complete for platform roles:GET /admin/users(users.py:85)POST /admin/users(users.py:97)app/admin/page.tsx:583+PATCH /admin/users/{uid}(users.py:175)page.tsx:462-474operator,page.tsx:474POST .../disable,.../enable(users.py:229,253)DELETE /admin/users/{uid}(users.py:273)Roles are
admin/operator/viewer, all editable from the dropdown.The actual gap: orgs are invisible to it
_format_user()(users.py:55-70) returnsuid,email,display_name,role,owned_node_ids,disabled, timestamps and Discord link — noorg_id, noorg_role, no org name. Confirmed by grep: zero references toorg_id/orgId/org_roleanywhere underdrb-frontend/app/admin/.Consequences for a platform operator today:
ownervsmember) from/admin— that lives on the separate/settings/memberssurface (org.py PATCH "",org.py:48), which is scoped to the caller's own org. So a platform admin managing someone else's org has no route at all.org_membersin the Firestore console.This was flagged as an observation in
ADMIN_BILLING_AUDIT.mdsection 1 on 2026-08-18 and has not changed since.Why it matters now
Board minutes #54 ruled the binding constraint is support hours, not sales — at 5h/week any customer needing the owner personally is one the company cannot afford. Org administration done by hand in the Firestore console is exactly that kind of unaffordable per-customer time, and it scales linearly with customers.
Suggested scope
org_id,org_nameandorg_roleto_format_user()and render them as a column plus a detail field.PATCH /admin/users/{uid}.GET /admin/organizationsand no route reads theorganizationscollection exceptorg.py:38(single org, caller-scoped).Related, and deliberately not duplicated
/adminreads are not org-scoped (a data-leak problem; this issue is the inverse, org data being absent where it is wanted)