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:

FileWhat it is
api.jsThe 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.jsThe page registry: which screens exist, which destination each belongs to, and what controls each declares (pages.js:1384)
render.jsThe renderer, plus every hand-built surface (the ring, the browsers, the Studio)
shell.htmlThe 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).

ControlWhereWhat it does
Chevron toggleTop of the rail, expanded state onlyCollapses the rail
The brand logoTop of the rail, collapsed state onlyExpands 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):

RowKeyWhat it is for
Rings1The console index: the live work board, facts and daemon glance drawn as an animated ring, plus nine sub-views
Work2ExecPlans, the activity log, projects and sessions
Memory3Facts, tenants, documents and retrieval tuning
Trust4Receipts, gates, identity and posture
Meters5Cost and usage
Site map7Every 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 destinationHow you reach itWhy it is hidden
SystemThe account badge at the bottom of the railIts pages are settings, not places you work
Studio (#/canvas/studio)The account menu, or the Studio button beside the theme controlIt is a builder, not a destination
ExplorerThe top-right search field, or the top of the Explore menuIt is a search box; giving it a rail row would be redundant
Link graphAppears in the rail only when the daemon reports the console_link_graph capability as availableGated 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.

KeyFrom a destination rowFrom a sub-page row
DownSteps into the open group's first rowNext row
Up-Previous row; from the first row, back to the destination
Escape-Returns focus to the destination row
Enter / SpaceNavigates and opens the groupNavigates

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.

ControlWhat it shows or does
Workspace switcherOne button showing the active workspace's icon and name. Clicking it opens a rightward menu listing every workspace (shell.html:3852)
Auth chipA 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 chipTinted when the daemon reports a dataplane. On a stock Community Edition build this is off
Status pillSee §2.5
Theme cycle buttonAdvances to the next theme in your allocated cycle (shell.html:3645)
Studio buttonOpens #/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):

IconWhat it opens
The active workspace's glyphThe 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:

SectionRows
System pagesSettings · Integrations · Extensions · Workbench · Raw JSON-RPC, plus Infra and Docs in Professional mode, plus Raw only in operator posture
hairline separator-
Account actionsStudio · Language · Log out (shell.html:3730)

Three notes on those account rows:

  • Studio goes to #/canvas/studio.
  • Language is a placeholder. It shows an EN chip and does nothing; there is a single locale.
  • Log out is STUBBED on a local daemon and hidden there. It appears only when a probe of /api/auth/session finds 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/logout with 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

ElementBehaviour
Heading and sub-lineThe current page's title and strapline, or the destination's when a destination has no sub-pages
Search fieldType and press Enter to carry the query into Explorer. ⌘K / Ctrl+K focuses it from anywhere (shell.html:4048)
Glass on scrollThe 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).

PillColourMeansWhat to do
Connected · LocalokReachable, summary loaded, auth_mode is offNothing. You are on an unauthenticated local daemon
Connected · PlatformokReachable, summary loaded, any other auth modeNothing
DegradedwarnThe daemon answered, but GET /v1/console/summary returned no usable dataUsually a scope problem. See chapter 8
OfflinecritNeither /v1/version nor /v1/console/summary was reachable at allThe daemon is down, or you are on the wrong origin
Signed outcritA session gate bounced the read to a sign-in page, or returned 401/403Sign 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):

ThemeCharacterBackground
Glass (default)Dark, translucent surfaces, 18px backdrop blur#060A12
DarkFlat dark, no blur#0D1117
LightFlat 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:

ControlWhereWhat it does
Theme cycle buttonRail meta cluster (expanded), or the operator-options popover (collapsed)Advances to the next theme in your cycle
Theme cardSystem › 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):

WorkspaceWhat it is
CommandThe ops console; everything chapters 3 to 6 describe
ExploreThe 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 EventSource on /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:

ControlWhereValuesEffect
Workspace switcherRailCommand / Explore / yoursCommand uses your density preference; Explore is the documents reader
Command densitySystem › Settings, top card (shell.html:4070)Standard (default) / ProfessionalProfessional 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 demo label is appended to any tile, card or panel a fixture filled.
  • Fixtures are read through one choke point. demoData() returns null when 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):

TabGoes to
Rings#/rings
Work#/work
Trust#/trust
MoreOpens 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 seeAlmost 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 railThe scripts did not load. Check /console-v2/render.js returns 200
Controls you expected are absentCustomer posture. Check the account badge and the status pill's read-only suffix

Sources