Skip to main content
View as Markdown

Console Customization

What you can change about the built-in admin console — whether it is served at all, and how your own design paints it — plus the accessibility floor no configuration can remove.

The admin dashboard is a Sovrium app, embedded in the binary and served from yours. That makes a question inevitable, so this page answers it plainly: you configure whether the console is served, and how it looks. Nothing else.

The operator does not edit the admin config; they edit only their own design. Any other modification is code, and therefore a fork.

That clause is the whole boundary. Everything below is what it means in practice.

What you configure

One key, two states:

app.yaml
admin: false

admin is a boolean, and that is the whole of it. Omit the key, or write admin: true, and the console is served at /_admin; write admin: false and it is served nowhere. The path is fixed, so there is nothing to place — and anything that is not a boolean, whether an object, a path string or null, is refused at boot rather than silently ignored. That refusal is deliberate: a key that looked like it configured the console, and quietly did not, would be worse than no key at all.

The base path /_admin is not configurable either. Making it movable would turn "is this request inside the console?" into a question the public carve-out, the stylesheet cache key and the subtree collision check each had to re-answer from config — and an operator who wants the console on another address already has a reverse proxy, which is where that decision belongs.

SOVRIUM_ADMIN=off in the environment serves it nowhere too, which is the difference between the two: whether the console belongs to this application is an application fact and belongs in the config file, while an emergency kill switch is a deployment fact and belongs to whoever holds the environment — who may not be whoever holds the config. The environment WINS over admin: true, and it never fails boot for disagreeing with it.

What you cannot rename

Nothing the console says. Its headings, its breadcrumbs, its table columns and its empty states are its own, there is no override key for them, and every deployment of a release reads them identically.

That follows from what the console is rather than from a policy laid over it. The console is an ordinary Sovrium app — written in the same declarative config surface yours is, and compiled into the binary as a preset — so the words it publishes belong to that app and not to yours. An operator who wants different words, or a different structure behind them, switches this console off and builds their own; the pieces are the config surface you already write.

What the console looks like

Yours, on Sovrium's floor.

The console is prebuilt. It ships inside the binary, version-locked to the release, and admin: false turns it off so you can build your own instead. What it is never changes. What it looks like is another matter: your design cascades into it, so the console wears your colours, your spacing, your density and your component styling — and keeps Sovrium's reference values for everything you did not declare.

The console's default design system can be 100 % overridden. It can never be removed. Both halves are load-bearing.

What cascades

The keys of your design that cascade, each taken as you wrote it:

Key Reaches
design Your colour roles above all — the console's chrome paints from background, foreground, primary and their siblings in some 350 places. Radii, fonts and spacing follow wherever a step you named matches a utility the console uses.
typeScale Emitted into the console's stylesheet, so your steps are defined there. The console's own pages draw none of them today, so declaring a ladder does not yet change how the console reads.
density Row height, control height, gap and text size — the console's tables and lists read the ladder directly.
components Your classes on every engine component the console draws.
ramps, colorRoles Carried, and read today by the design-system pages rather than by the stylesheet.

Everything else in design stays Sovrium's, and each for its own reason:

Not cascaded Why
zones A map from your route patterns to zones. Against console paths it matches nothing, so importing it would leave the console with no zone at all.
logo Identity, not a token. The console is Sovrium's; putting your mark on it would credit our product to you.
voice, principles, imagery Writing and asset guidance for the surfaces you author. The console's own words are ours.

Where the defaults come from when you declare nothing

Not from a merge, and the distinction matters if you are reasoning about precedence. Sovrium's reference token layer is emitted into every stylesheet before anything you wrote, so an undeclared token resolves to Sovrium's value one token at a time — in the console exactly as on your own pages. Declaring design.colors.primary moves the console's primary and nothing else; every other token keeps the reference. You never have to restate a value to keep it.

What you cannot take away

The accessibility floor. design.components lets you restyle an engine component type across the whole app, and replace: true drops Sovrium's recipe for that type outright — a legitimate thing to want, and the single most damaging class list you could write, because a button with no focus ring loses the page for a keyboard user. So the floor is applied after your classes, in the console and in your own app alike. You can make a console button look like anything. You cannot make it unfocusable.

That is the guarantee behind "never removed": you can replace every value, and the console still starts, still renders, and still works with a keyboard.

What is never yours

Not configurable Why
The console's pages Its structure ships in the binary, version-locked to the release. Upgrading Sovrium upgrades the console.
Its navigation Same. A page you cannot reach is a page that is not there.
Its backend The /api/admin/* routes are admin-gated reads. No config key reaches them, and none adds a write path.
Its identity Its logo, its voice, its zone map. You restyle the console; you do not rebrand it.
Configuration editing There is none, here or anywhere in the self-hosted product. Config is code; you change an app by editing a file.

The last row is the one worth stating twice, because the console being written as a config file makes the temptation structural rather than hypothetical. It does not make configuration editable. A visual config-editing plane exists — it is part of hosted Sovrium Cloud, and it is deliberately not in the binary you self-host.

Which pages the console serves

The console drops pages your instance has no use for. An API keys page on an instance where auth.apiKeys is off is a nav entry leading somewhere empty, so it is not served at all: it 404s and appears in no listing.

That is the one way your config changes the console's shape, and it works in one direction only — a page can be absent because a capability is, never present because you asked for it.

The mount owns its whole subtree

/_admin answers /_admin and every /_admin/** path. An operator page, form or redirect underneath it is a NAMED boot failure rather than a silently shadowed route, because shadowing quietly is the worse failure: you would ship a page that never renders, or displace part of the console you need in order to diagnose it.

Restyling the console itself

Only by working in the Sovrium repository, on the release rather than on an instance — which is exactly what "any other modification is code, and therefore a fork" means.

The console is src/admin/, and its design is src/admin/config/design.ts. Contributors preview it with:

>_ terminal
bun run app:admin

That boots the console standalone with hot reload, so a token edit is visible on the next save. One detail worth knowing before you conclude something is broken: it boots src/admin/preview.ts rather than src/admin/app.ts, because app.ts deliberately declares no auth block and a console nobody can sign into answers 404 on every data page. The preview adds that one block, so sign in at /login with the AUTH_ADMIN_* credentials from src/admin/.env.example.

  • Admin Dashboard — the console itself, its surfaces and its read API.
  • Design System Console — the console section that documents your system at its rendered values.
  • Design System — the design key that governs your own pages.
  • Component Styles — restyling an engine component type across your app.
  • Density — the row, control, gap and text ladder.
  • Environment VariablesSOVRIUM_ADMIN and its siblings.

Behaviour

The Console Wears the Operator's Design and Keeps Sovrium's Floor

  • The console shell resolves and paints the operator's colour, spacing and density tokens
  • The anonymous console login page carries the operator's design before any session exists
  • An inner Data surface carries the operator's design
  • An operator component class reaches a console button WITHOUT removing the accessibility floor
  • An app declaring no design renders the console in the Sovrium reference design
  • The console chrome GEOMETRY is the console's own — a 48px bar and 16px gutters — whatever spacing step the operator declares. The ruling says the design key CASCADES, not that the operator lays the console out: an operator's spacing.gutter: 3.5rem describes THEIR surfaces, and reaching the chrome would take 112px off every grid in a console they do not own. Non-vacuous because the geometry is read in the SAME DOCUMENT where --spacing-gutter resolves to the loud value — either reading alone is satisfied by a cascade that never ran
  • The operator page and the console agree on the resolved tokens, whatever their stylesheet URLs
  • The dark surface is reachable on every operational console family — the records grid, a detail list, a developer reference page and the shell root — on a host declaring no colour scheme. The light reading is the control: a console stuck in dark satisfies "dark is reachable" while being as broken as one stuck in light
  • With nothing stored, the console follows the operator's system preference; a stored choice overrides it. Read in the one direction where the two disagree — stored light against an OS set to dark — because agreement passes on a build that never read storage at all
  • The scheme is settled by a head script that precedes the stylesheet, so there is no flash of the wrong surface — and the host's own page, same renderer and same head builder, declares no scheme and carries no such script. That control is what attributes the script to the console rather than to a build that emits it unconditionally
  • The chrome’s own scheme toggle is the reader’s way to write the key -008..-010 only ever read: one click repaints the console (the resolved --sv-bg AND a genuinely painted element, never body, which measures rgba(0,0,0,0) in both schemes), records the choice under theme, and the choice survives a FULL document load — not an SPA nav, which never re-runs the no-FOUC head script that is the thing under test. Asserted as a PAIR, both ways, since a control that only ever adds dark is one the reader cannot undo
  • An operator design paints the whole console without removing its accessibility floor (regression)

Last updated September 23, 2026

This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.

Built with Sovrium