Operations · 2. The console tour
The console is a single HTML page, embedded in the daemon binary, that reads the daemon over its own origin and shows you what your agents have been doing. This chapter is explanation: it draws the shell once, names each part, and tells you what changes when you touch it. Chapters 3 to 6 then walk the individual screens.
2.1 What you are opening
Point a browser at the daemon's HTTP port and go to /console. / redirects there (console.rs:300).
There is no build step, no CDN and no external request of any kind. The page loads four same-origin scripts and nothing else:
| File | What it is |
|---|---|
api.js | The generated network client. 173 GET methods, 40 gated mutations, 7 curated read-POSTs. Nothing else in the console is allowed to call fetch (api.js:18) |
pages.js | The page registry: which screens exist, which destination each belongs to, and what controls each declares (pages.js:1384) |
render.js | The renderer, plus every hand-built surface (the ring, the browsers, the Studio) |
shell.html | The shell itself: routing, the rail, themes, posture, boot |
If you are iterating on the console, CORECRUXD_CONSOLE_DEV_PATH makes the daemon serve those files from disk instead of from the binary, and stamps a flag that tears down the service worker so a plain refresh always shows your edit (console.rs:47, shell.html:5683).
2.2 The layout, in three parts
┌────────────┬───────────────────────────────────────────────┐
│ RAIL │ TOPBAR heading · sub [ search ⌘K ] │
│ ├───────────────────────────────────────────────┤
│ brand │ │
│ workspace │ │
│ chips │ CONTENT │
│ status │ (the current page) │
│ theme │ │
│ │ │
│ Rings 1 │ │
│ Work 2 │ │
│ Memory 3 │ │
│ Trust 4 │ │
│ Meters 5 │ │
│ Site map 7│ │
│ │ │
│ [ badge ] │ ← account + System pages, rolls upward │
└────────────┴───────────────────────────────────────────────┘
The rail is the only navigation. There is no second menu. Destinations are the top-level rows; each one opens an accordion of its sub-pages beneath it, and exactly one accordion is open at a time, the one you are in (shell.html:4717).
The topbar is a heading and a search field. It goes glass when the page scrolls under it (shell.html:4035). On the Rings page it gives up its heading to host the playback bar.
The content region is the page. Every route replaces it wholesale.
2.3 The rail, control by control
2.3.1 Collapse and expand
The rail starts collapsed on every fresh load. Only an explicit, persisted preference of open keeps it expanded (shell.html:4932). The state is written to crux.console.rail and re-applied before first paint so the layout does not jump (shell.html:39).
| Control | Where | What it does |
|---|---|---|
| Chevron toggle | Top of the rail, expanded state only | Collapses the rail |
| The brand logo | Top of the rail, collapsed state only | Expands the rail. The chevron is deliberately absent when collapsed (shell.html:4960) |
Collapsed, every row is an icon square with no visible label. Hovering one steadily for 900 ms eases a glass label out from under the rail; it leaves on mouse-out, on any scroll, on a click, on Escape and on every route change (shell.html:4610). This replaced the native browser tooltip, so if you are looking for a title on a compact rail row, there is deliberately none.
2.3.2 The destination rows
Six destinations appear in the rail, in this order, with these keyboard shortcuts (pages.js:1450):
| Row | Key | What it is for |
|---|---|---|
| Rings | 1 | The console index: the live work board, facts and daemon glance drawn as an animated ring, plus nine sub-views |
| Work | 2 | ExecPlans, the activity log, projects and sessions |
| Memory | 3 | Facts, tenants, documents and retrieval tuning |
| Trust | 4 | Receipts, gates, identity and posture |
| Meters | 5 | Cost and usage |
| Site map | 7 | Every surface, one click away |
Pressing a number key navigates to that destination, unless focus is in a text field (shell.html:5534).
Four further destinations exist but are deliberately not in the rail:
| Hidden destination | How you reach it | Why it is hidden |
|---|---|---|
| System | The account badge at the bottom of the rail | Its pages are settings, not places you work |
Studio (#/canvas/studio) | The account menu, or the Studio button beside the theme control | It is a builder, not a destination |
| Explorer | The top-right search field, or the top of the Explore menu | It is a search box; giving it a rail row would be redundant |
| Link graph | Appears in the rail only when the daemon reports the console_link_graph capability as available | Gated on a configured upstream proxy (shell.html:4118) |
Overwatch is a fifth entry in the registry, retired: every #/overwatch/... URL redirects to its Rings equivalent so no bookmark dead-ends (shell.html:5460, pages.js:1536).
Clicking a destination row does two things: it navigates to that destination's default page, and it opens that row's accordion. Clicking the row you are already on fires no navigation, so the accordion is opened directly instead (shell.html:4822).
2.3.3 Keyboard walking the accordion
The sub-pages live inside the rail in both rail states, so the keyboard model is a list, not a menu.
| Key | From a destination row | From a sub-page row |
|---|---|---|
| Down | Steps into the open group's first row | Next row |
| Up | - | Previous row; from the first row, back to the destination |
| Escape | - | Returns focus to the destination row |
| Enter / Space | Navigates and opens the group | Navigates |
The account group at the bottom of the rail grows upward, so its two vertical keys are mirrored: visual order always matches key order (shell.html:4404).
2.3.4 The meta cluster (expanded rail only)
Between the brand and the destinations sits a compact cluster. It is hidden when the rail is collapsed; §2.3.5 covers what replaces it.
| Control | What it shows or does |
|---|---|
| Workspace switcher | One button showing the active workspace's icon and name. Clicking it opens a rightward menu listing every workspace (shell.html:3852) |
| Auth chip | A padlock, tinted when auth_mode is anything but off. The exact mode is in its tooltip. Omitted entirely if the summary did not report one (shell.html:4901) |
| Dataplane chip | Tinted when the daemon reports a dataplane. On a stock Community Edition build this is off |
| Status pill | See §2.5 |
| Theme cycle button | Advances to the next theme in your allocated cycle (shell.html:3645) |
| Studio button | Opens #/canvas/studio (shell.html:3658) |
A chip is never fabricated. If the boot summary carried no value for it, no chip is drawn.
2.3.5 The collapsed rail's two icons
When the rail is collapsed the meta cluster disappears and two icon buttons take its place (shell.html:4542):
| Icon | What it opens |
|---|---|
| The active workspace's glyph | The same rightward workspace menu the expanded button opens |
| A sliders glyph, "Operator options" | A glass popover carrying: an Options row (jumps to System › Settings), a Theme picker (all three themes, as buttons), and a Connection readout mirroring the status pill (shell.html:4468) |
Only one popover is open at a time; opening either closes the other, and a click anywhere else closes both.
2.3.6 The account badge and the System group
The badge at the very bottom of the rail is not decoration and it is not a link; it is the trigger for the footer accordion (shell.html:4777).
Clicking it rolls a single merged list upward:
| Section | Rows |
|---|---|
| System pages | Settings · Integrations · Extensions · Workbench · Raw JSON-RPC, plus Infra and Docs in Professional mode, plus Raw only in operator posture |
| hairline separator | - |
| Account actions | Studio · Language · Log out (shell.html:3730) |
Three notes on those account rows:
- Studio goes to
#/canvas/studio. - Language is a placeholder. It shows an
ENchip and does nothing; there is a single locale. - Log out is
STUBBEDon a local daemon and hidden there. It appears only when a probe of/api/auth/sessionfinds a hosted session surface; on a local daemon that path 404s and the row stays hidden (api.js:927, shell.html:3758). When it does appear it POSTs to/api/auth/logoutwith a CSRF double-submit header and, on success, navigates to/.
The badge itself shows your posture: Operator with a sub-line of operator · local or operator · admin, or Customer with customer view (shell.html:5602).
2.4 The topbar
| Element | Behaviour |
|---|---|
| Heading and sub-line | The current page's title and strapline, or the destination's when a destination has no sub-pages |
| Search field | Type and press Enter to carry the query into Explorer. ⌘K / Ctrl+K focuses it from anywhere (shell.html:4048) |
| Glass on scroll | The bar is transparent until the page scrolls more than 4px under it |
On the Rings destination the topbar enters tab mode: the heading is hidden by CSS and the Rings play bar is mounted into its space, beside the search field (shell.html:5216). Leaving Rings restores it on every route (shell.html:4565).
2.5 The status pill: the four things it can say
This is the single most useful widget in the shell, so it is worth knowing exactly what each state means (shell.html:4880).
| Pill | Colour | Means | What to do |
|---|---|---|---|
Connected · Local | ok | Reachable, summary loaded, auth_mode is off | Nothing. You are on an unauthenticated local daemon |
Connected · Platform | ok | Reachable, summary loaded, any other auth mode | Nothing |
Degraded | warn | The daemon answered, but GET /v1/console/summary returned no usable data | Usually a scope problem. See chapter 8 |
Offline | crit | Neither /v1/version nor /v1/console/summary was reachable at all | The daemon is down, or you are on the wrong origin |
Signed out | crit | A session gate bounced the read to a sign-in page, or returned 401/403 | Sign in again |
A · read-only suffix rides on any of them whenever your posture is customer.
Signed out also raises a fixed red banner across the top with a Sign in link that carries a return URL back to the exact view you were on (shell.html:4864). The detection happens once, at boot: the origin-wide gate makes the boot summary read a faithful oracle. A session that expires mid-visit is not caught until the next load.
The brand text also changes with the same signal: Crux Daemon when auth_mode is off, Crux Console otherwise, with the version as its sub-line (shell.html:4914).
2.6 How the console authenticates
There are three separate things here and conflating them is the usual source of confusion.
1. The browser never holds a bearer token. Every call api.js makes is same-origin with credentials: 'same-origin' (api.js:16). The daemon authenticates the session at its own origin. There is no token in localStorage for an XSS to steal.
2. Posture is derived from the server, every boot, and never persisted. The rule is exactly two clauses (render.js:62):
operator ⟺ daemon reports auth_mode === 'off'
OR GET /v1/admin/version returned 200
customer ⟸ anything else, including a failed or blocked probe
It fails closed. The console holds the result in memory only, and re-derives it on the next load (shell.html:4090).
3. A gated write additionally requires a bound passport. Posture decides whether a write control is visible and enabled. Before it will fire, the console also requires a passport id bound in localStorage under crux-console-bound-passport, and refuses with an Article 14 message if there is none (render.js:88, render.js:648):
Bind a passport first, Art.14 requires an attributed approver before any gated write.
You bind one on Trust › Pending mints, in the Bound approver panel. See chapter 4 §4.9.
Two layers back that up. Every mutation goes through one function that refuses in customer posture regardless of what the UI shows (render.js:74), and the daemon independently enforces the admin or facts scope on each route.
2.7 Themes
Three themes ship, defined in one registry (shell.html:3592):
| Theme | Character | Background |
|---|---|---|
| Glass (default) | Dark, translucent surfaces, 18px backdrop blur | #060A12 |
| Dark | Flat dark, no blur | #0D1117 |
| Light | Flat light, no blur | #F4F6F8 |
The chosen theme is written to crux.console.theme and re-applied by a script in <head> before first paint, so there is no flash (shell.html:24). An unknown or absent value falls back to Glass.
Two controls set it, and they are not the same control:
| Control | Where | What it does |
|---|---|---|
| Theme cycle button | Rail meta cluster (expanded), or the operator-options popover (collapsed) | Advances to the next theme in your cycle |
| Theme card | System › Settings, injected above the page (shell.html:3663) | Picks a theme directly, and sets which themes the cycle button rotates through |
The Theme card gives each theme a button and an in cycle checkbox. Unchecking a theme removes it from the quick-cycle rotation, stored in crux.console.theme.cycle. The cycle can never be emptied: unchecking the last one re-checks it (shell.html:3678). Default: all three are in the cycle.
The Rings canvas reads its palette from the same CSS custom properties and re-reads them live when you switch theme, so the ring recolours without a reload (render.js:13398).
2.8 Workspaces: Command, Explore, and your own
A workspace is a named set of destinations and pages. Two are built in (shell.html:3788):
| Workspace | What it is |
|---|---|
| Command | The ops console; everything chapters 3 to 6 describe |
| Explore | The reader: a document tree in the rail, a ~72-character reading column, and an evidence panel |
Switching to Explore drives the hash to #/documents/explorer and lands you on the Explorer search (shell.html:4002). Switching back to Command returns you to the default destination, and specifically drives the hash there, so a switch out of a user workspace cannot leave you looking at the same page (shell.html:3990).
You can define your own. A user workspace is a fact under the console:workspace: entity prefix, with its pages under console:page:; the console loads them asynchronously at boot and appends them to the switcher (shell.html:3940). They live at #/w/<uid>[/<pageUid>]. Built-ins render fully with zero configuration, so a slow or absent load costs nothing.
Two behaviours worth knowing:
- New workspaces appear without a refresh. The console opens an
EventSourceon/v1/events/stream?types=fact.stored, filters for the two entity prefixes, and reloads the model 600 ms after one lands (shell.html:3961). - An unknown workspace uid never dead-ends. If the model has loaded and the uid is not in it, you are redirected to Command (shell.html:5349).
Building and editing workspaces is Studio › Pages, chapter 6 §6.3.
2.9 Presentation modes and density
Mode is presentation. It never changes what you can access. A customer sees the same gated surface in every mode (shell.html:3767).
Underneath there are three modes, standard, professional, documents, but only two visible controls:
| Control | Where | Values | Effect |
|---|---|---|---|
| Workspace switcher | Rail | Command / Explore / yours | Command uses your density preference; Explore is the documents reader |
| Command density | System › Settings, top card (shell.html:4070) | Standard (default) / Professional | Professional raises density, reveals mono metadata rows on expanders, and unhides the four Pro-only pages |
Professional mode adds four pages that Standard hides entirely: Infra, Docs, Global and Agent (pages.js:1447). A deep link to a Pro-only page while in Standard redirects to the destination's default page rather than showing an error (shell.html:5101).
Density is stored in crux.console.density; mode in crux.console.mode. Both are applied before first paint (shell.html:47).
2.10 Demo mode
Append ?demo=1 to the console URL. This sets a flag, persists it to crux.console.demo, and lets labelled fixtures fill panels whose real feed came back empty or flag-off. ?demo=0 clears it (shell.html:4977).
Three rules make this safe to leave on while evaluating:
- Real data always wins. A fixture is only consulted when the real endpoint answered empty or 404.
- Every demo panel is chipped. A small
demolabel is appended to any tile, card or panel a fixture filled. - Fixtures are read through one choke point.
demoData()returnsnullwhen the flag is off, so nothing can render a fixture by accident.
Use it to see what a populated console looks like before you have populated one. Do not use it to evaluate whether a number is real; that is what the chip is for.
2.11 Phone and tablet
Below 720px the rail is hidden and a fixed bottom tab bar takes over (shell.html:4994):
| Tab | Goes to |
|---|---|
| Rings | #/rings |
| Work | #/work |
| Trust | #/trust |
| More | Opens a bottom sheet listing Memory, Meters and Site map, plus the relocated account badge |
The "More" tab shows as current whenever the active destination lives in the sheet. Escape closes the sheet, as does tapping the scrim. The account badge and its whole accordion are physically moved between the rail footer and the sheet as the viewport crosses 720px, one live node, not two copies (shell.html:5073).
2.12 Installing it as an app
The console ships a web manifest and a service worker, registered with scope /console (shell.html:5698). The worker caches the app shell and never caches anything under /v1/*, control-plane reads always hit the daemon.
Registration is skipped entirely in three cases: no serviceWorker support, a non-secure context, or the dev-path flag. In the dev case it also unregisters any existing worker and deletes its caches.
2.13 What to do when the console looks wrong
Three failure shapes, and how to tell them apart quickly. Full procedures are in chapter 8.
| What you see | Almost always |
|---|---|
| Page renders, panels say "unavailable, HTTP 403" | Scope problem. Your token lacks console:read or admin:read |
Page renders, panels say "disabled, set CORECRUXD_..." | A feature flag is off. This is the console telling you the truth |
| Whole page blank, no rail | The scripts did not load. Check /console-v2/render.js returns 200 |
| Controls you expected are absent | Customer posture. Check the account badge and the status pill's read-only suffix |
Sources
- crates/corecruxd/src/console.rs:294, the console router
- crates/corecruxd/console/v2/shell.html:5543,
boot() - crates/corecruxd/console/v2/shell.html:5440,
route() - crates/corecruxd/console/v2/shell.html:4796,
buildRail - crates/corecruxd/console/v2/render.js:62,
derivePosture - crates/corecruxd/console/v2/render.js:74,
operatorGatedCall, the single mutation choke point - crates/corecruxd/console/v2/api.js:706, the 40 gated mutations
- crates/corecruxd/console/v2/pages.js:1450, the destination registry

