Organization writes were superadmin-only, so standing up a tenant needed
an out-of-band superadmin. Creating one is now self-service, and an admin
manages the org they belong to.
- POST /api/orgs is open to any authenticated user. A creator who isn't a
superadmin must have no organization yet (a single-valued membership
relation means a second one would abandon the first), and is promoted to
the new org's admin and first member in the same request. If that
promotion fails the org is rolled back, so it is never left stranded
with nobody able to administer it. Superadmins still create tenants
without joining them.
- PATCH/DELETE are manager-gated and scope an admin to their own org. An
admin deletes theirs only as its sole member: they are detached and
demoted to a plain user before the record goes, so the org is empty when
it is removed. Other members still block deletion with a 409.
- /api/me now carries organization + organizationName, which the clients
need to tell "no org yet" from "org you administer".
The panel, Web App (new OrgManager.vue in Settings) and Phone App (new
_OrganizationSection) all mirror the server's gates rather than
re-deciding them. The Phone App cached its role at login and gates the
Users tab on it, so AuthService.adoptRole refreshes that from the profile
instead of making a freshly promoted admin sign in again.
Covered by orgs_test.go, which drives the real handler + middleware chain
against a stand-in PocketBase: promotion, the already-a-member refusal,
superadmin staying unattached, the rollback, own-org scoping, the
detach-and-demote, and the blocking-member 409.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>