2026-05-29

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 role of admin or viewer, stored in profiles.role. Admins see and write everything; viewers are limited to rows whose device owner_user_id = auth.uid() (additive *_read_owner_or_admin RLS policies on devices, device_status, entries, settings_device, device_jwts, breakpoint_events). Admins can promote/demote from a user's row.
  • Device ownerdevices.owner_user_id (set when a human pairs a device via device-pair) is the anchor for viewer-scoped RLS. Role promote/demote fans out live over the profiles:all Realtime 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 userInvitations request 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.
  • 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