Skip to content

Super Admin Console

The super-admin console is the operator’s view of a SaaS cluster. It is the only place where you see across every tenant at once — listing them, suspending them, watching system health, reading billing, and triggering migrations. Tenants never see it. It lives at the apex domain (https://saas.proxrad.com/admin/), not at any tenant subdomain, and authenticates against a separate user table with a separate JWT signing key.

This page covers how the console is structured, what each section does, and the deliberate separation between the super-admin realm and the per-tenant realm.

The super-admin console runs in its own authentication world:

AspectTenant adminSuper-admin
URL<slug>.saas.proxrad.com/loginsaas.proxrad.com/admin/login
User tabletenant_<slug>.userspublic.super_admins
JWT signing keySAAS_TENANT_JWT_SECRET (shared across tenants, scoped by tenant_id claim)SAAS_SUPERADMIN_JWT_SECRET (separate env var, separate signing key)
JWT claim shape{user_id, tenant_id, user_type, exp}{super_admin_id, role, exp}
Brute-force lockout5 fails → 15 min, 10 → 1 hour, 20 → 24 hourSame progression, separate counter, never shares state with tenant counters
MFAOptional per tenantRequired — TOTP enrolment is mandatory at first login

Even if a tenant’s database is compromised and an attacker pulls every JWT secret known to the tenant code path, the super-admin secret is not reachable from any tenant-scoped query. The keys live in the cluster’s .env and only the super-admin handler reads them.

A super-admin cannot log in to a tenant panel by using their super-admin credentials. They can only impersonate a tenant admin from the console — and impersonation creates a new short-lived JWT for that specific tenant, leaving an audit trail.

SectionWhat it showsPermission
DashboardCluster-wide counters: active tenants, total subscribers, today’s signups, today’s revenue, today’s churn, host CPU/memory/disksaas.dashboard.view
TenantsSearchable table of every tenant — slug, plan, status, subscriber count, last login, MRRsaas.tenants.view
BillingPer-tenant subscription state, invoices, failed-payment list, upcoming renewalssaas.billing.view
System HealthPostgres connection-pool utilisation, Redis memory, backup-queue depth, RADIUS auth rate, WireGuard peer countsaas.system.view
Audit LogEvery super-admin action: tenant created, suspended, restored, JWT key rotated, etc.saas.audit.view
SettingsCluster config — JWT secrets, SaaS email relay credentials, default plans, signup togglesaas.settings.edit

The Tenants table is the centre of the console. Each row is one tenant.

ActionEffectReversible?
CreateRuns the onboarding pipeline (Tenant Onboarding) — schema, tables, seed permissions, first admin userYes — delete the tenant
EditChange plan, quota, status, billing email. Does not touch tenant dataYes
SuspendSets tenants.status = 'suspended'. Tenant cannot log in; resolver returns 403. Subscribers stay online — RADIUS continues serving them.Yes — Activate
ActivateReverse of suspendYes
ImpersonateIssues a 1-hour JWT scoped to that tenant with user_type = admin. Used for support.n/a — token expires
DeleteDrops the tenant row, drops tenant_<slug> schema, deletes uploads, deletes cloud backupsNo — coordinate with the tenant first

Plans determine quotas (subscriber count, cloud backup size, scheduled-backup frequency, custom domains allowed). Changing a tenant’s plan is immediate:

  • Subscriber-count quota tightens — the tenant cannot add new subscribers but existing ones keep working.
  • Subscriber-count quota loosens — the tenant can immediately add subscribers up to the new ceiling.
  • Cloud-backup quota tightens — existing backups stay; new ones are rejected if they push over.
  • Custom-domain allowance revoked — existing custom domains keep working; new ones can’t be added.

Billing-wise, the proration is handled by the billing integration (Stripe by default). The console writes a plan_changed_at timestamp on the tenant for proration calculations.

Every impersonation is logged with:

  • Super-admin user ID
  • Target tenant ID
  • Target user inside the tenant (always the first admin)
  • Timestamp + duration of the impersonation session
  • IP address

The impersonated session also writes into the tenant’s own audit log so the tenant can see when support staff accessed their panel. The super-admin console cannot suppress this — it is a non-negotiable transparency feature.

The Billing page integrates with the payment provider (Stripe by default; the integration is pluggable). It surfaces:

  • Active subscriptions — count by plan.
  • MRR / ARR — monthly and annualised recurring revenue.
  • Churn this month — tenants who cancelled or downgraded.
  • Failed payments — tenants whose last invoice failed and are now in a 7-day dunning window.
  • Upcoming renewals — tenants whose next invoice is in the next 14 days.

Each tenant row is clickable, showing their invoice history, payment method on file, and a “trigger manual charge” button for support cases (e.g. a customer who paid out-of-band and wants their invoice marked paid).

The billing data lives in the public.saas_billing and public.saas_invoices tables. It is replicated from Stripe webhooks; if Stripe is the source of truth, the console is the read-side projection.

The system-health panel is the operator’s “is the cluster OK?” view:

MetricWhat healthy looks likeWhat to do otherwise
Postgres poolBelow 70% utilisation under normal loadAbove 90% sustained → bump max_connections or split the cluster
Redis memoryBelow 60% of allocatedAbove 80% → bump Redis memory or shorten cache TTLs
Backup queue depthDrains within 5 minutesSustained >50 jobs → license-server upload bottleneck; investigate
RADIUS auth rateStable per tenantSustained spikes → a tenant’s NAS is flapping
Cluster CPUBelow 70%Above 85% → split tenants across clusters
WireGuard peer countMatches sum(tenant.peers)Mismatch → wg state out of sync; restart wg0

The page polls every 10 seconds and shows trend lines for the last hour. Click-through to a more detailed Grafana dashboard if the cluster has the LGTM observability stack deployed (optional).

Cluster-level settings the super-admin can change:

SettingEffectRestart required
Disable public signupHides the /signup page on the apex domainNo
Default planPlan assigned to self-service signupsNo
SaaS email relay credentialsSMTP for outgoing welcome / invoice emailsNo
License keyThe cluster’s license, registered with the license serverAPI restart
Cloudflare API tokenUsed for DNS-01 cert renewalsNo
Rotate super-admin JWT secretInvalidates every super-admin session immediatelyNo, but every super-admin must re-login
Rotate tenant JWT secretInvalidates every tenant session immediatelyNo, but every tenant user must re-login

JWT-secret rotation is the nuclear option for credential compromise. Use it if a super-admin laptop is stolen or if .env is exposed.

The console is powerful but deliberately limited:

  • Cannot read tenant data without impersonating. There is no “show me subscriber alice@acme from tenant Acme” query at the super-admin level. To see that, you impersonate (logged), and then look it up inside Acme’s panel.
  • Cannot edit tenant data directly. Same reason. Every data change is attributable to a real user inside a real tenant — even if that user is “support impersonating the admin”.
  • Cannot bypass MFA. TOTP is enforced; “I forgot my TOTP” requires a second super-admin to reset, with full audit trail.
  • Cannot decrypt a tenant’s backups without the cluster license key. Same encryption rules apply to super-admins as to tenants.

These constraints make audit easier: the worst-case bad-actor super-admin must impersonate to do damage, and every impersonation leaves a trail in both the cluster audit log and the tenant’s own audit log.

The first super-admin is created during cluster install. Subsequent super-admins are invited from Settings → Super Admins:

  1. Existing super-admin clicks Invite Super-Admin.
  2. Enters the new admin’s email.
  3. The new admin gets an invitation email with a magic-link signup.
  4. They set password + enrol TOTP at first login.
  5. Their role (owner, admin, support) is set by the inviter and can be changed later.
RolePermissions
ownerEverything, including super-admin user CRUD and JWT rotation
adminTenant CRUD, billing, system health. Cannot manage other super-admins.
supportRead-only across tenants, can impersonate, cannot suspend/delete

Super-admin permissions are flat (assigned per super-admin role, not via a permission-group system like the tenant side). The permissions checked by the API are:

PermissionWhat it gates
saas.dashboard.viewThe cluster Dashboard page
saas.tenants.viewThe tenant list
saas.tenants.create / saas.tenants.edit / saas.tenants.deleteTenant CRUD
saas.tenants.impersonateThe Impersonate button
saas.billing.view / saas.billing.editBilling page + manual-charge button
saas.system.viewSystem health page
saas.audit.viewSuper-admin audit log
saas.settings.editCluster settings, JWT rotation
saas.super_admins.manageInvite / remove other super-admins. Owner-only.