---
title: "Overview & Navigation"
description: "The Business Hub's front door: launching it, the six-destination shell, and how freshness and scope work across every page."
---

The **Business Hub** is RStack's live, local dashboard on port **3008** — a company-wide view of
every governed run across every project on the machine, rendered from the same `.rstack/` files
the CLI and the agents read. There is no database and no telemetry leaves the machine. This page
is the front door: how to launch it, how the six-destination navigation shell (issue #278) is
structured, and how the freshness indicator keeps every page honest about how live its data is.
The rest of the Business Hub section covers each destination's real content in depth.

## Launch it

The hub opens automatically when a governed session starts (Pi, the Claude Code SessionStart
hook, and Operator all launch it). You can also bring it up manually:

```bash
npx rstack-agents hub          # health-check :3008, start if down, open browser
```

Or run the server directly:

```bash
rstack-business --port 3008 --project .
```

<Info>
  Default port is **3008** (override with `RSTACK_BUSINESS_PORT`). Disable auto-launch with
  `RSTACK_NO_BUSINESS_HUB=1`. The legacy `rstack-observer` command is a compatibility alias that
  opens the same hub.
</Info>

## How it works

### Six destinations, not a flat page list

The shell (`src/observability/dashboard/ui/navigation.js`) is built from a small fixed list of
**destinations**, each holding one or more **children** (pages). Only a destination's non-hidden
children render as secondary-nav links; hidden children are still routable (a URL or deep link
still opens them) but don't clutter the nav:

| Destination | Visible children | Notable hidden/secondary pages |
|---|---|---|
| **Overview** | Command Center | — |
| **Runs** | Run Workspace, Studio | Projects & Runs, Workflow Map, Run Analytics, Agent Work |
| **Evidence** | Evidence Center | Release Readiness, Run Report, Security, Compliance |
| **Decisions** | Action Inbox, Approvals | Decisions / Readiness, Alerts & Guardrails |
| **Spend** | Spend Center | Business Flex |
| **Operations** | Operations Center, Live Feed, Environment & Integrations, Diagnostics | Team & Presence, Team & Layers |

Each destination has a `defaultPage` — the child it opens on when you click the primary nav
button — and clicking a destination's primary link expands its secondary nav (`navigationGroups`
in navigation.js). The mobile nav (`mobileNavigationMarkup`) renders the same groups inside a
`role="dialog"` panel with focus trapping and Escape-to-close.

<Note>
  Approvals was promoted into the visible nav in #537: it had been hidden alongside the legacy
  review pages even though it is the only surface where a human can approve or reject a gate.
  Action Inbox still triages first; Approvals is where you act.
</Note>

### Routing: hash + query params, shareable

`parseDashboardRoute({ hash, search })` reads `?page=` from the query string and `#page=…&run=…&section=…`
from the hash (a bare hash like `#traceability` with no `=` is treated as a page id directly, for
backward compatibility). `formatDashboardHash` writes it back. This means every page — and, for
the Run Workspace, every tab section (`summary | work | timeline | artifacts | metrics`) — has a
shareable URL; `showPage()` calls `history.pushState`/`replaceState` on every navigation, and the
`popstate` handler restores both the page and the run scope from the URL on back/forward.

### Page-switch motion is a live-DOM fade, not the View Transitions API

`showPage()` toggles `.page.active` and lets CSS handle the fade. The comment in
`navigationScript` documents a deliberate rejection (issue #521): the View Transitions API
suppresses painting *and* hit-testing of the live page for ~160–250ms during the transition, so a
click landing right after a navigation would vanish. The plain CSS fade keeps the page interactive
on every frame instead.

### Page bodies are static shells; data fills them in later

`pages/index.js`'s `pageMarkup()` renders one `<section class="page" id="page-<id>">` per entry in
`navigation.js`'s `pages` list, each with a shared head (`page-title`, a `<div class="last-updated">`
slot) and a per-page body from `pageBody(id)`. The bodies here are empty containers — KPI tiles,
`<table>` skeletons, panel `<div>`s with ids like `command-attention`, `overview-proof-rail`,
`ops-transport-body` — that the client-side data layer fills once a snapshot arrives. The Command
Center body (the Overview destination's only child) is the richest: a delivery-outcome banner fed
by the server-owned readiness projection, a Proof Rail (`overview-proof-rail`), an executive
mission brief, KPI grid, and Needs Attention / Stage Health / Active Delivery / Recent Activity
panels — all real `.rstack` data, never invented placeholder numbers.

### Freshness: the topbar never claims data is live when it isn't

`freshness.js`'s `classifyFreshness(opts)` is a pure function evaluated both server- and
client-side against `{ hasData, now, lastSnapshotAt, wsConnected, staleMs, disconnectMs }`
(defaults: 10s stale, 30s disconnect):

- no data yet → `loading`
- snapshot older than `disconnectMs` → `disconnected`, regardless of what the socket reports
- socket down but a poll or push still landed recently → `reconnecting`
- socket up but no snapshot inside `staleMs` → `stale` (the server-side poll stalled)
- otherwise → `live`

`freshnessLabel()` turns that into the topbar text ("Live · updated HH:MM:SS", "Stale · data as
of HH:MM:SS", etc.) and `freshnessDotClass()` maps it to the existing status-dot styling. The
dashboard is built to never silently show stale data as if it were current.

### Scope: project → run, shareable

A project/run switcher in the top bar scopes every page to the run you care about; `SCOPE.run` and
`SCOPE.project` persist across reloads and are restored from the URL's `run` param
(`restoreScopeFromRoute`) — so a link like `#page=run-workspace&run=<runKey>` deep-links straight
into a specific run's workspace, handy for sharing in Slack.

### Multi-project aggregation

The hub reads a global registry at `~/.rstack/known-projects.json` that every `rstack-agents init`
and `hub` call updates, so one running hub instance shows runs across every project on the
machine. Multiple people can point the same hub (or the same notification channel) at it.

## Try it

Open the hub, then try the shell mechanics directly:

```text
http://localhost:3008/#page=traceability          # jump straight to the Evidence Center
http://localhost:3008/#page=run-workspace&run=<runKey>&section=timeline
```

Both are real, bookmarkable URLs — `parseDashboardRoute`/`formatDashboardHash` round-trip them, and
`popstate` will restore the same page + run + section on browser back/forward.

<Warning>
  Some children exist and are fully routable but are deliberately not shown in the nav (`hidden`
  in navigation.js) — e.g. Workflow Map, Run Analytics, Security, Compliance, Team & Presence.
  They're superseded-in-nav, not removed; deep links and internal cross-links still open them.
</Warning>

## Related

<CardGroup cols={2}>
  <Card title="Cockpit Controls" icon="sliders" href="/business-hub/cockpit-controls">
    Authenticated, audited actions the Hub can take against a run (resume, checkpoint restore).
  </Card>
  <Card title="Approvals & Governance" icon="shield-check" href="/business-hub/approvals-and-governance">
    The Action Inbox and Approvals destination in depth.
  </Card>
  <Card title="Data Visualizations" icon="chart-gantt" href="/business-hub/data-visualizations">
    Quality/risk dials, the stage subway strip, and Spend Center charts.
  </Card>
  <Card title="Operations Center" icon="server" href="/business-hub/operations-center">
    Transport freshness, integrations, recovery, and context/memory health in one page.
  </Card>
</CardGroup>
