Table of Contents

The Explorer navigation model

This page documents the Orleans.Lattice.Explorer packages, which are in progress, in the documentation for Orleans.Lattice 9.9.0 (release line 9.9), built 2026-10-04. It is also published as markdown, with every table and list, at navigation-model.md, and llms.txt lists every page.

In the Explorer, the address is the navigation. Every page has one canonical address. The browser's URL carries it, and the address line at the top of every page shows it as a chain of nodes. You can type into that chain to go somewhere else. A directory spine on the left lists the areas you can reach. A command palette in the same address line runs anything a page can do.

This page covers the address grammar, the address line, completions, the command palette, the directory spine, and how tenancy re-roots an address.

The address grammar

An address is an optional tenant root, an area, the segments of the object within that area, and an optional query:

[/t/{tenant}]/{area}[/{segment}...][?{key}={value}&...]

A bare / (or /t/{tenant} when tenancy is on) is Home, the estate overview. Some examples:

/
/data
/data/a/crm/orders
/data/a/crm/orders?tab=history&key=order-42
/t/acme/replication/trees
/cluster/wal?tree=orders&partition=3

For the Data area, the segments below the area are the parts of the logical tree id, so the tree a/crm/orders is /data/a/crm/orders. The full ABNF is in the UI package README.

The encoding rules are strict, so that every address has exactly one spelling:

  • Everything is lower case. An area key is a lower-case letter followed by lower-case letters, digits and hyphens. A segment keeps a-z, 0-9, -, ., _ and ~, and percent-encodes everything else as UTF-8 with upper-case hex, including an upper-case letter. A tree named Orders is addressed as %4Frders.
  • Dot segments are encoded. The segments . and .. are written %2E and %2E%2E, so a browser cannot collapse them.
  • Query values keep their case. A query value keeps the RFC 3986 unreserved characters, upper case included, and percent-encodes the rest. Each query key appears at most once.
  • Some area keys are reserved. No area may take t, which introduces a tenant root, or not-found, which is the not-found page.

Parsing typed or pasted input is lenient. An upper-case area key is read as lower case, a leading ./ and a trailing / are ignored, a fragment is dropped, and a raw character that the canonical form would encode is taken as itself. The address the Explorer then shows and links to is always the canonical form.

Every link the Explorer renders is relative to the application's base path, so the console works unchanged when a host mounts it under a subpath (see Running and hosting the Explorer).

Query keys

Query keys are how a page records its state in the address, so every view can be bookmarked, shared and walked with Back and Forward. Three keys mean the same thing wherever they appear: key names one key within the object, prefix names a key prefix, and at names a point in time or a revision. Each area adds its own keys, such as tab on a Data tree or range on a Telemetry board; they are listed in The Explorer areas.

Routes and the not-found page

Every page route the Explorer declares is lower case and, apart from Home's bare /, begins with a literal segment, and none is a catch-all. A hygiene test fails the build otherwise. A catch-all at the application root would also match static asset paths, so a request for a script could be answered by the whole console. An object deeper than an area's routes can express is carried in the query instead; for example, an app's in-app path beyond its declared segments travels as ?path=.

An address that does not resolve lands on the not-found page. It says that nothing lives at that address, shows the address, and links the nearest address that does exist for you: the root of its area when that area is visible to you, and Home otherwise. An address in an area that is hidden from you renders the same page, so the Explorer never confirms that such an area exists (see Area availability).

The address line

When you are not typing, the address line shows the current address as a chain of nodes in a mono typeface. Each ancestor is a link and the current node is drawn as the marker, the order diagram's "you are here". The chain is the tenant root (t/acme) or Home, then the area, then the path grouped as the area says: a logical tree id such as orders/eu is one node, not one per /. A query, such as ?tab=history, is state within the page rather than a place, so it is never a node.

Press / (outside a text field) or Ctrl+K (Cmd+K on a Mac), or select the line, to turn it into an input. The input starts with the current address selected, so typing replaces it. What you type decides what the line does:

Input Mode Suggestions
Starts with / Address A "Go to" entry for the typed address, plus matches from every visible area.
Starts with > Command palette The commands whose title or id contains the text.
Starts with t/ Tenant The tenants you may reach. Choosing one re-roots the current address.
Starts with a/ App Matches from the visible areas' completions, such as installed apps.
Anything else Search The visible areas whose key or name matches, a "Go to" entry when the text is an area address, and matches from every visible area.

The input is an ARIA 1.2 combobox. The arrow keys move through the suggestions, Enter goes to the highlighted one (or the first when none is highlighted), and Escape restores the chain and returns focus to where you started. A polite status region announces how many suggestions there are.

Completions

Each visible area answers completions from its own data, such as tree ids in Data or backup ids in Backups. The Explorer asks every visible area in parallel, each under its own time bound of two seconds, and shows each area's group as soon as it answers. The groups always appear in directory order, however the answers race, so the list never reshuffles under the pointer. An area that does not answer in time contributes nothing and is named in a note ("Data did not answer in time." or "Data could not be searched."); it never holds back the others. A new keystroke cancels the completions still running. An area that is unavailable to you is never asked.

The command palette

Typing > turns the address line into the command palette. The palette offers:

  • Chrome commands. go.home goes to Home, go.{area} goes to each visible area (for example go.data), and the appearance commands set the theme, contrast and density (appearance.theme.system, appearance.theme.paper, appearance.theme.board, appearance.contrast.system, appearance.contrast.standard, appearance.contrast.more, appearance.density.comfortable and appearance.density.compact). While the tenant switcher is offered, tenant.switch ("Switch tenant") opens it.
  • Area commands. Each visible area contributes its own, such as data.refresh or backups.capture. They are listed with their areas in The Explorer areas.

Choosing a command first navigates to the page the command belongs to, then runs it there. Nothing is palette-only: every command is also a visible control on its page, and that control carries the command's id in a data-lt-command attribute. The palette is a faster way to reach a control, never the only way.

The directory spine

The directory spine is the order diagram's sidebar: hollow nodes on a hairline, with the current stop drawn as the ringed marker node and a heavier label. Home heads the spine, followed by one stop per area you can reach, in a fixed order: Data, Apps, Access, Schema, Tenancy, Replication, Backups, Telemetry and Cluster.

  • A visible area's stop links to its root. It may carry a short badge, such as a count.
  • An unavailable area's stop is shown, demoted, with the one-sentence reason it cannot be opened now, such as "Sign in to administer access on this cluster."
  • A hidden area has no stop at all.

How an area decides between these is described in Area availability. The spine is asked again on every navigation, and whenever sign-in, the connection or the configuration changes, because each of those can change which areas you may see.

Home shows the same stops as an estate overview, each with a one-line status under its name, such as how many trees there are. Each status arrives independently under its own time bound, so a slow area never delays the others.

At different widths

The Explorer is phone-first-class for reading and simple actions. It measures its own width and uses three bands:

Band Width Spine Header Address line
Expanded 1200px and up A full spine with badges and reasons. Connection, appearance, tenant switcher and identity controls in a row. The full chain.
Medium 768px to 1199px A rail: every label, but no badges or reasons. As expanded. The full chain.
Compact Below 768px A slide-in sheet opened from the Directory button, which returns focus when it closes; for an operator it starts with the tenant switcher. The mark and the name, shortened to "Lattice Explorer" so it is never cut off, with a Menu button holding the connection, identity and appearance controls. The current node alone, on one line, after a ... elision (not a node) that opens the full chain as a list. The command palette opens as a full-screen sheet.

Until the width has been measured, the expanded layout renders, so nothing depends on script to be usable.

Skip links come first on every page: Skip to directory, Skip to address and Skip to content.

Tenancy and re-rooting

The web head always registers the tenant view, so whether an address carries a tenant root depends on the caller. For a caller whose tenancy is on, the active tenant is the root node of Home and of every tenant-scoped address: /t/acme/data/orders rather than /data/orders. Tenancy is off for a caller scoped to the reserved default tenant who is not a platform operator (on a cluster without the tenancy add-on, that is every caller who cannot see the Access area), and in a head that does not register the tenant view. Access and Cluster have both shapes: /access and /cluster are cluster-wide and stay plain, while /t/{tenant}/access and /t/{tenant}/cluster keep their tenant root and show only that tenant's rules or trees. The Tenancy area's operator directory at /tenancy is cluster-wide, while its My tenant pages at /t/{tenant}/tenancy are tenant-rooted. Every other area is tenant-scoped.

At a tenant-rooted address every area lists only that tenant's items: its listings, and the counts, Home status lines, spine badges, address completions and pickers that summarise them. The reserved default tenant owns the bare (unprefixed) trees and never sees another tenant's items. A tenant-rooted deep link to another tenant's rule, tree, backup or schema tree is not found, and is never read. See Tenant scope.

The Explorer keeps every address canonical for your tenancy:

  • Tenancy off. No address carries a tenant root, and an address that has one is redirected to the same address without it.
  • Tenancy on. An address without a tenant root is rooted at the active tenant. A cluster-wide area's address loses any tenant root it was given, except that Access and Cluster keep one they were given, as their tenant-rooted form.
  • Another tenant's address. Arriving at /t/{other}/... is a request to switch to that tenant. It goes through the operator-gated tenant switch. If the switch succeeds, the Explorer says so. If it is refused, the Explorer redirects to the active tenant's equivalent address and shows a warning, so a URL can never scope you beyond what you may reach.

A successful switch is not drawn as a toast, because the address and the header already show the new tenant: "Scoped to tenant {tenant}." is read out in the notification region's polite live announcement, and nothing covers the page or waits to be dismissed. The first address a circuit opens (a reload, a bookmark or a pasted link) only establishes the tenant, so it announces nothing. A refused switch is still a warning toast, which stays until it is dismissed, because it left you somewhere you did not ask to be.

Before the page is interactive. The tenant you last held is remembered in the browser's preference store, which the server prerender cannot read. So for a caller who can reach more than one tenant, the prerender of an address that names no tenant shows a neutral Resolving your tenant state instead of the page: no page content, no redirect, no tenant-rooted link and no tenant switcher under a tenant it could only guess. Once the page is interactive, the Explorer reads the preference store first, restores the remembered tenant (revalidated against the tenants you may reach), and only then redirects to the canonical /t/{tenant} address. An address that names a tenant still decides it during the prerender, and a caller with one reachable tenant is never held. If the reachable tenants cannot be read, the prerender fails closed to the neutral state.

To re-root the current address, type t/ in the address line. It completes the tenants you may reach, marking the active one. Choosing one keeps the rest of the address and replaces its tenant root; a cluster-wide address, including plain /access and /cluster, is unchanged.

A caller scoped to the reserved default tenant who is not a platform operator sees no tenancy chrome: addresses stay plain, and /t/default/... is redirected to the plain form. See Tenant scope for the full tenancy model and the Tenancy area.

The tenant switcher

A platform operator who can reach two or more tenants also gets a tenant switcher in the top bar. It is a button naming the active tenant; selecting it opens a panel and moves focus into a field that lists the tenants you can reach straight away, up to 20 at once, with the active tenant named under the field. Typing filters the list ("Type to filter"), Down and Up move through it, Enter switches, and one Escape closes the list and the panel together and returns focus to the button. Opening it closes any other header panel. The palette command tenant.switch ("Switch tenant") opens the same field, and the button carries that command id. At the compact width the switcher is the field itself, at the top of the Directory sheet above the spine, and focusing it lists the tenants in the same way.

The switcher is absent, not disabled, unless every condition holds: you are signed in, tenancy is on for you, the operator-gated switcher proves you may switch, and there are at least two tenants to choose between. Any fault reads as "not offered". Its list is the same reachable-tenant list as the t/ completions and the Tenancy directory, so for an operator it includes the reserved default tenant. It is read again on every navigation, so a sign-in, a sign-out or a new identity never shows the previous caller's tenants.

Choosing a tenant makes the same fail-closed switch as typing t/{tenant}. At a tenant-scoped address you go to the same address re-rooted at the chosen tenant, and a refusal redirects back with the usual warning. At a cluster-wide address, such as an Access page, you stay where you are while the tenant changes, and the Explorer announces "Scoped to tenant {tenant}." or, on a refusal, shows a warning toast saying it can't scope to that tenant.

Pickers

Every field that chooses something the cluster already knows (a tree, a region, a user or group, a tenant, a key, a schema member, a WAL provider key) is a type-ahead picker. As you type, it lists the matching existing values in a mono face, with a short description beside each where there is one, such as a principal's display name. A polite status region announces how many values match, or that more match than are shown and you should keep typing.

A picker works in one of two ways:

  • Pick existing. Only a listed value is accepted. Leaving the field, or submitting the form, with a value that names nothing is refused with an inline error such as "No tree is named orders. Choose one from the list." Fields that act on an existing tree, region, subject or tenant work this way.
  • Suggest. Any text is accepted, and existing values are offered as suggestions. A typed value that already exists is flagged, for example "This tree exists: restoring replaces what it holds." (the Restore into tree field).

A field that names a new thing is not a picker, because there is nothing to pick: a new group id, a new rule id, a new tenant id, a snapshot's destination tree and a schema remediation's rename target are plain text boxes with no list and no arrow. As you type, the name is checked against the existing ones (at most one check in flight, however fast you type), and a taken name is refused inline, for example "A group named operators already exists.", or, for the rename target, only flagged. A slower check, such as asking the identity directory about a new group id, runs when you leave the field and again when you submit. A check that cannot answer leaves a note rather than a refusal, because the cluster checks the name again when it is written.

A field that takes several values, such as a tenant's allowed regions or a new tenant's admin subjects, shows the chosen values as removable chips. Choosing a suggestion, pressing Enter or typing a comma adds the next value, and duplicates are ignored.

The list is a bounded answer (8 values by default), never a full listing. Typing does not start a query per key: at most one query is outstanding, a new keystroke cancels it, and the keys typed meanwhile collapse into one query for the latest text. Small lists such as regions and tenants are read once and reused for up to 30 seconds; a WAL move's provider keys are read once for each tree you name.

A picker never blocks a form. When its source cannot list values (no identity directory is configured, the cluster does not serve the facade, or the read is refused or fails), the field says why and accepts what you type as free text.

Suggestions are tenant-scoped. Tree suggestions come from the same catalogue the Data area reads, so with tenancy on only the active tenant's trees are offered; anything a picker remembers is filed under the caller (the sign-in, the endpoint and the tenant the circuit asserts), so a value read under one tenant, or for one identity, is never offered under another.

The picker is an ARIA 1.2 combobox with no focus trap. Down and Up open the list and move the highlighted value, Enter chooses it, Escape closes the list, Home and End return to editing the text, and Tab leaves the field.

Where the address ends and preferences begin

The address carries where you are. Preferences carry how you like the console, such as the theme and density, and an explicit address is never overridden by a remembered value. See What the Explorer remembers.

See also