The dashboard grew an admin surface for people and the TestFlight beta, not just devices. Dashboard builds 241 → 246; Supabase migrations 178 → 180.
What changed
Users & roles + device owner (dashboard 242 / supabase 178)
/users— a new admin-only page listing every signed-in profile (email, display name,role, linked providers, devices). Click a user for detail tabs: Devices · TestFlight · Password · API tokens.- Roles — each profile carries a
roleofadminorviewer, stored inprofiles.role. Admins see and write everything; viewers are limited to rows whose deviceowner_user_id = auth.uid()(additive*_read_owner_or_adminRLS policies ondevices,device_status,entries,settings_device,device_jwts,breakpoint_events). Admins can promote/demote from a user's row. - Device owner —
devices.owner_user_id(set when a human pairs a device viadevice-pair) is the anchor for viewer-scoped RLS. Role promote/demote fans out live over theprofiles:allRealtime channel (useCurrentRole()re-reads on the spot).
TestFlight automation (dashboard 243 → 246 / supabase 178)
A /settings TestFlight page (admin-only) manages the BusymateHelper beta through App Store Connect, all live over Realtime:
- Testers tab — invite a tester by email (+ optional name) into one or more beta groups (at least one group is required so the invite email actually sends). Search/list testers (flat or grouped); testers matching a real signed-in user are flagged. Per-row Remove deletes the tester from TestFlight (confirm-gated).
- Internal tab (243 → 246) — internal testers must be App Store Connect team members:
- Invite internal tester sends a
userInvitationsrequest with the least-privilege Customer Support role. - Pending team invites lists each unaccepted invite with per-row Resend (cancels + re-creates the invitation, since ASC has no resend endpoint — re-sends the email, resets expiry, keeps the role) and Cancel (instant, optimistic).
- Team members shows accepted members with their roles. Members not yet in any internal beta group are flagged "needs group" (sorted to top with a count badge) and get a per-row Add to <group> button plus an Add all. Invite/resend/cancel/add-all update optimistically — no hard refresh.
- Invite internal tester sends a
- Groups tab — read-only list of beta groups with member counts.
ios.busymate.net (302 → the current TF deep-link) is the install entry point for the beta.
Last-admin safety net (supabase 179 / dashboard 245-246)
- An admin's own role row is locked in the UI (no self-demotion), and a database-level guard prevents removing the last admin — so the project can never be left with zero admins and lock everyone out of the admin surfaces.
Docs accuracy + public docs (docs 139 → 143 / supabase 180)
- Public docs — the docs site is reachable without a dashboard session for the published content.
- Section-index pages — nav folders (e.g.
/changelog,/how-to,/architecture) now render a list of their entries when you land on the folder root, instead of 404-ing. - Inline-SVG diagrams — every architecture/illustration diagram is now inline SVG (no ASCII box-art); one broken cross-repo link was repointed at its GitHub
blob/main/...URL.
Where it's documented
- Users & TestFlight: the Admin pages section of Dashboard — How-to.
- Roles,
owner_user_id, and the viewer-scoped RLS: Supabase backend — How-to.