The owner console
The owner console at /admin is the instance-wide administration surface. It
is one shell with a tab rail; every tab is a query parameter on the same route
(/admin/?tab=<key>), which the console echoes as a locator chip under its
title. In the sidebar it is labelled Owner, and it sits in an admin-gated
section together with the separate Operator console. Nothing on this page is
workspace-scoped except where a tab is explicitly called out as such — an edit
here changes the deployment for every organization on it.


Three consoles, three scopes
Section titled “Three consoles, three scopes”These three surfaces are routinely confused. They are distinct routes with distinct authorization.
| Surface | Route | Scope | Who may open it |
|---|---|---|---|
| Owner console | /admin |
The whole instance — users, billing plans, entitlements, marketplace governance | An instance admin only |
| Operator console | /operator |
The whole instance — cross-tenant operability plus the global kill-switch | An instance admin session, or a headless deployment operator token |
| Operations report | /reports/operations |
One workspace — that workspace’s dispatch, routing and circuit breakers | That workspace’s owner or admin |
/reports/operations is a tab of the Reports hub, not a console: it is the
read-only, tenant-scoped view of the same operability data, and a workspace owner
reaches it without any instance privilege. See
Reports. The cross-tenant reads and the
global global_dispatch switch live on /operator only — see
Operate the delivery engine.
Who counts as an instance admin
Section titled “Who counts as an instance admin”A user is an instance admin when their user_profiles.is_admin column is true
or they hold the system_admin row in user_roles. That flag is set at
profile creation and only then: an email that case-insensitively matches
SUPACLOUD_INITIAL_ADMIN_EMAILS is created both approved and admin; every other
signup is created neither. The console offers no control to promote a user, so
promoting one is a database or bootstrap-configuration act, not a UI act.
Authorization is enforced in two independent places. The whole /api/admin/*
sub-router is wrapped in a require_system_admin middleware layer, and each
handler additionally calls require_admin_user — which first requires an
approved account, then the admin flag. The page itself redirects a
non-admin to /settings after its profile load resolves, and the sidebar entry
is hidden without the flag; neither of those is the security boundary.
The thirteen tabs
Section titled “The thirteen tabs”| Tab | Route | What it governs | Most consequential change |
|---|---|---|---|
| Users | ?tab=users |
Approval of every account on the instance | Reject — it deletes the pending account row outright |
| Features | ?tab=features |
Nothing — it reports which optional features are wired | Read-only |
| SEO | ?tab=seo |
Nothing — it reports the marketing site’s metadata health | Read-only |
| Plans | ?tab=plans |
Every billing plan’s price, interval, included seats and visibility | Editing amount_cents rotates the plan’s Stripe Price |
| Templates | ?tab=templates |
The transactional email the instance sends | Saving overwrites the live subject and body for all recipients |
| Customers | ?tab=customers |
Nothing — it lists every workspace with its plan and subscription status | Read-only |
| Overrides | ?tab=overrides |
Entitlement grants that sit above the plan | A global (unscoped) grant changes entitlements for every tenant |
| Queues | ?tab=queues |
Nothing — it reports the Stripe event backlog and reconciliation status | Read-only |
| Audit | ?tab=audit |
Nothing — it is the append-only record of admin and billing changes | Read-only |
| Marketplace Payouts | ?tab=payouts |
Seller payouts and the platform fee percentages | Submit — it sends a SEPA batch to the bank; there is no un-submit |
| Error page | ?tab=error-page |
The instance-default App error page | Saving replaces the whole JSON bag; there is no merge |
| Marketplace review | ?tab=marketplace-review |
The install gate on catalog items | Approve — it flips an item to verified and thus installable |
| Marketplace sources | ?tab=marketplace-sources |
Source registry of the active workspace, not the instance | Deleting a source (refused while it has published items) |
An unknown or absent tab parameter falls back to Users.
Backed by GET /api/admin/users/pending and GET /api/admin/users, with
POST /api/admin/users/{user_id}/approve and
DELETE /api/admin/users/{user_id}/reject.
The tab has two blocks: Pending Users — a warning-edged action list with Approve / Reject per row — above All Users, the full roster with an admin badge and an Approved / Pending status badge per account.
Reject is irreversible. It is not a status change: the server hard-deletes
the user_profiles row (WHERE approved = false). There is no undo and no
rejected-user list; the person must register again. Approve, by contrast, is a
one-way UPDATE ... SET approved = true and is safe to re-issue.
This tab shows the admin flag but never sets it.
Features
Section titled “Features”Backed by GET /api/admin/feature-availability. Read-only.
The feature-availability report answers one question: which optional,
secret-backed features does this running process actually have configured. A KPI
strip carries the resolved Secret backend (env or openbao), an
available-of-total count, a Boot-critical health badge, the deployment
Edition (Community or Enterprise) and the unavailable count. Below it, one
row per feature — category, name, the env-var name chips, and an
Available / Unavailable badge — with a drawer per row.
Nothing on this tab writes anything. To change what it reports, set the named
variable in the environment or in the supacloud/app vault map and redeploy —
see Environment variables, the
SECRET_BACKEND matrix and
Secret provisioning. The reported
edition comes from the license token, not from this tab; see
Apply an edition license.
Backed by GET /api/admin/seo. Read-only.
A diagnostic of the public marketing site’s metadata: a score, the number of indexed routes, the locales served, the count of public plans, then the search and social assets — canonical base URL, Open Graph image with its dimensions and a live preview, and the Twitter card type — and finally one row per check with its status. Changing any of it is a marketing-site configuration or build change; the tab has no controls.
Backed by GET /api/admin/plans, PUT /api/admin/plans/{plan_id} and
POST /api/admin/plans/{plan_id}/stripe-sync.
Every billing plan on the instance, including inactive and non-public ones. Each
row is an editable draft — display name, amount in cents, Included seats,
interval (month, year, one_time, custom) and currency (EUR, USD, GBP) —
committed per row with Save, plus Active and Public toggles and a
per-plan Stripe sync.
- A plan is a shared instance object. Editing one changes it for every organization subscribed to it, immediately.
- An
amount_centschange rotates the plan’s Stripe Price. The Stripe sync action is a preview of that reconciliation, not the money movement itself. - Included seats is the plan’s
users.maxentitlement: a non-negative whole number, or the literalunlimited. Anything else is rejected. For how the pool and the overage bill, see Manage seats and billing — this page does not restate it. - Every save writes an audit entry with before/after, visible on the Audit tab.
Templates
Section titled “Templates”Backed by GET /api/admin/templates and PUT /api/admin/templates/{key}.
The transactional-email templates the instance sends, stored in the database
rather than in the image. Three columns: the list of template keys, an editor
(Subject, Body, optional Change reason) and a live preview rendering
the body against a sample context. Detected {{variable}} placeholders are
listed as chips under the editor, and the row header shows whether the draft is
unsaved.
Saving replaces the stored subject and body for the whole instance — the next email of that kind uses the new text. The change reason is written to the audit log, but the tab offers no revert and no version list; recovering an earlier wording means reading it back out of the audit entry.
Customers
Section titled “Customers”Backed by GET /api/admin/customers. Read-only.
Every workspace on the instance grouped under its owning organization: an organization header (name, a Personal / Organization kind badge, workspace count) over one row per workspace showing its plan id and a subscription-status badge. A workspace whose organization the payload does not carry falls into a single ungrouped bucket rather than disappearing. There are no controls.
Overrides
Section titled “Overrides”Backed by GET /api/admin/overrides/board (server-paginated, with feature,
scope and status facets plus free-text search), POST /api/admin/overrides and
POST /api/admin/overrides/{override_id}/revoke.
An entitlement override grants a feature entitlement outside the plan. The
grant form takes a feature key, a JSON value (a number, a string such as
"unlimited", or an object), a reason, an optional workspace and/or user scope,
and an optional expiry. The table lists Feature, Scope, Value, Expires and
Status; an unscoped row shows scope Global.
Revoke is a stamp, not a delete: the row stays in the table with status revoked, which is what makes the grant history auditable. Granting and revoking both write audit entries.
Queues
Section titled “Queues”Backed by GET /api/admin/queues. Read-only.
Three figures on one card: Stripe events (total received), Pending (received but not yet processed) and a Reconciliation status badge. It answers whether the Stripe webhook backlog is draining. There is no requeue or replay control here.
Backed by GET /api/admin/audit/board.
The admin and billing audit log — plan edits, override grants and revokes, template changes — each entry carrying before/after snapshots. The board is server-paginated with facet filters on action and target kind, free-text search and header sort on action and target.
This log is append-only from the console: there is no delete, no edit and no retention control on this tab.
Marketplace Payouts
Section titled “Marketplace Payouts”Backed by GET /api/admin/marketplace/payouts/queue,
GET|POST /api/admin/marketplace/payouts/batches, the per-batch actions
approve, submit, tan, cancel, reconcile and sepa_xml, and
GET|PUT /api/admin/marketplace/fee-settings. Three sub-tabs, routed on a
second query parameter (&subtab=):
| Sub-tab | Route | Contents |
|---|---|---|
| Queue | &subtab=queue |
Sellers with a payable ledger balance; creating a batch from them |
| Batches | &subtab=batches |
The batch lifecycle — approve, submit, TAN, cancel, reconcile, SEPA XML |
| Fee Settings | &subtab=fees |
The platform package and runner fee percentages |
Fee Settings are a singleton, instance-wide row. package_fee_percent and
runner_fee_percent must each be between 0 and 100; the server rejects anything
outside that range. A change applies to future settlements — it does not
retroactively re-price a batch already in the queue.
Error page
Section titled “Error page”Backed by GET /api/admin/error-page and PUT /api/admin/error-page.
A raw JSON editor for the singleton instance-default error page
(error_page_config) — the fallback rendered for a deployed App when neither the
per-App nor the per-workspace error page is set. The configuration is an opaque
bag, so the tab is a textarea rather than a form; invalid JSON is refused in the
browser before the request is sent.
Saving replaces the whole bag rather than merging into it, so an edit that drops a key drops that key. The value is the last fallback for every hosted App on the instance; see Separate-origin App hosting.
Marketplace review
Section titled “Marketplace review”Backed by POST /api/admin/marketplace/items/{id}/review/approve and
.../review/reject, each taking an optional reason.
The review gate for catalog items. Each row shows the item name, its slug, its current verdict pill and a reason field, with Approve and Reject.
- Approve flips
marketplace_items.verified— the install gate — so a paid or central item becomes installable across the instance. That is the consequential act on this tab. - Reject records the rejection and leaves
verifiedfalse, so the item stays uninstallable for the paid/central path. - A decided item drops out of the queue; the tab lists only undecided ones.
For what publishing looks like from the other side, see Publish a marketplace bundle.
Marketplace sources
Section titled “Marketplace sources”Backed by GET|POST /api/marketplace/sources,
PATCH|DELETE /api/marketplace/sources/{id} and
POST /api/marketplace/sources/{id}/sync.
The source registry: list, create, edit, delete and per-source Sync, with a kind (Git or Workspace) and an Enabled / Disabled / Sync error status badge per row.
Two server-side guards apply regardless of role:
- The curated official source (kind
official, owned by no workspace) is read-only: it shows its status and a Sync button, and the server refuses an edit or a delete. - Delete is refused with
409while the source still has published items, so removing a source can never silently cascade its catalog away. Retire the items first.
To install from a source once it is synced, see Install a marketplace connector.
See also: Reports (the tenant-scoped
operability view), Operate the delivery engine
(the /operator console), Manage seats and billing,
Editions and entitlements,
Environment variables, and
Navigation and hubs.