Docs
Platform admin — Tenantx kernel

Platform admin — Tenantx kernel

**Platform admin** is the **operator console** for the whole product: all organizations, plans, billing operations, cross-tenant support tools, and global content (e.g. help center). It is **not** a tenant inside a customer org.

Platform admin — Tenantx kernel

Platform admin is the operator console for the whole product: all organizations, plans, billing operations, cross-tenant support tools, and global content (e.g. help center). It is not a tenant inside a customer org.


Identity and permission

  • Permission: global subscription.admin (stored as a permission with organization_id = NULL; assigned to the user with Spatie’s team context matching the platform team UUID used in your app — see middleware below).
  • Middleware: platform.adminEnsurePlatformAdmin (backend/app/Http/Middleware/EnsurePlatformAdmin.php).
  • Flow: clear stray team id, then set team id to the platform org UUID 00000000-0000-0000-0000-000000000000, then hasPermissionTo('subscription.admin'). On success the request gets is_platform_admin merged for downstream use.

Platform admins may still have a profile linked to a demo org (e.g. admin@tenantx.dev) so they can use both /platform and the tenant app; the important part is that platform routes never use tenant organization middleware the same way tenant data routes do.


Frontend

  • Routes: /platform/* wrapped in PlatformAdminRoute (checks platform permission client-side; server must still enforce).
  • Layout: PlatformAdminLayout — separate nav, no tenant SmartSidebar.
  • API client: platformApi — calls /api/platform/... only; must not send tenant workspace_id query params (tenant ApiClient attaches workspace for non-platform requests).
  • Permissions hook: usePlatformAdminPermissions — not useHasPermission (org-scoped).

Backend API prefix

All routes in Route::middleware(['auth:sanctum', 'platform.admin'])->prefix('platform') in backend/routes/api.php, including (non-exhaustive):

AreaExamples
Dashboard / analyticsGET /api/platform/dashboard, limits overview
Plans & subscriptionsPlans CRUD, subscriptions list, org subscription detail, renewals, payments confirm/reject
OrganizationsList/create/update/delete any org (storePlatformAdmin, destroyPlatformAdmin, etc.)
Per-org subscription opsActivate, suspend, limit overrides, feature add-ons, usage snapshots, recalculate usage
FilesPlatform file storage listing/upload
Discount codesOperator discount-code management via platform subscription endpoints
Maintenance / license feesOperator views and confirm flows
UsersPlatform user list CRUD, password resets
Login audit /api/platform/login-audit/...
Help center /api/platform/help-center/categories and .../articles (full CRUD)
Maintenance modeEnable/disable/status for the app

Tenant-scoped POST/DELETE /api/organizations are disabled for normal tenant users in the kernel; creating orgs as an operator uses POST /api/platform/organizations (storePlatformAdmin).


Mental model: three shells

flowchart TB
  subgraph tenant [Tenant app same origin]
    WS[Workspace tenant UI SmartSidebar]
    OA[Org admin OrganizationAdminSidebar /org-admin]
  end
  subgraph platform [Platform /platform]
    PA[PlatformAdminLayout + platformApi]
  end
  U[User session Sanctum]
  U --> WS
  U --> OA
  U --> PA
  • Workspace UI: one org, usually one workspace context for data.
  • Org admin: one org, many workspaces, org-wide admin screens.
  • Platform: many orgs, global configuration and support.

Promoting an operator

php artisan platform:assign-admin you@example.com --organization=acme-corp

--organization is optional (slug); use it when the user should also have a normal tenant profile in that org.


Security checklist

  1. Never reuse tenant PermissionRoute or useHasPermission alone to guard /platform routes.
  2. Never trust client org or workspace ids for authorization on platform endpoints — use path/query ids with server-side validation.
  3. Keep subscription.admin off bulk customer roles; it is super-operator level in the kernel.

Related docs