Demo Mode

When the console cannot reach a live backend or agent — for example on the public site console.kubestellar.io — it switches to demo mode. In demo mode every dashboard card displays synthetic data so visitors can explore the UI without a running Kubernetes cluster.

Cards that are showing demo data are marked with a Demo badge and a yellow outline.

When Demo Mode Is Active

Demo mode is controlled by a three-level priority system (source: web/src/lib/demoMode.ts):

PriorityConditionCan the user toggle it off?
1 — ForcedRunning on a Netlify deployment (console.kubestellar.io, deploy previews)No
2 — User togglelocalStorage['kc-demo-mode'] === 'true'Yes
3 — Token fallbackNo auth token or token equals demo-token, and the user has not explicitly disabled demo modeYes

On the public site (Priority 1) demo mode is always forced on because there is no backend or agent to serve real data.

When running a local development server with ./start-dev.sh or ./startup-oauth.sh, demo mode is off by default and live cluster data is used instead. Users can still enable demo mode manually through the UI toggle, which sets the localStorage key.

Where Demo Data Comes From

Demo data is generated in two places:

1. Unified Demo Data Registry

web/src/lib/unified/demo/demoDataRegistry.ts is a central registry that stores generator functions keyed by data type. Generator functions are registered at startup from web/src/lib/unified/demo/generators/registerDemoGenerators.ts.

The registry produces data for clusters, pod issues, deployment issues, events, security findings, GPU nodes, Helm releases, operators, and services. For example, the demo cluster list contains a set of realistic clusters (EKS, GKE, AKS, OKE, OpenShift, etc.) with plausible configurations.

2. Per-Hook Fallbacks

Each useCached* hook in web/src/hooks/useCachedData.ts defines an inline demo-data generator that is used as a last-resort fallback when:

  • Demo mode is active, or
  • Three or more consecutive API fetches have failed.

These hook-level generators are intentionally small and self-contained so that every card always has some data to display.

3. Per-Card Demo Files

A few cards ship their own demoData.ts alongside the component (for example web/src/components/cards/flatcar_status/demoData.ts). These are used when the card’s data shape is too specialised for the shared generators.

How a Card Decides What to Show

The decision path for each dashboard card is:

   ┌─────────────────────────────┐
   │  Card calls useCached*()    │
   │  hook for its data type     │
   └──────────┬──────────────────┘

   ┌──────────▼──────────────────┐
   │  Is demo mode active?       │──Yes──▶ Return demo data
   │  (isDemoMode())             │         isDemoFallback = true
   └──────────┬──────────────────┘
              │ No
   ┌──────────▼──────────────────┐
   │  Fetch from API / agent     │
   └──────────┬──────────────────┘

   ┌──────────▼──────────────────┐
   │  Fetch succeeded?           │──Yes──▶ Return live data
   └──────────┬──────────────────┘         isDemoFallback = false
              │ No (3+ failures)

   Return demo data as fallback
   isDemoFallback = true

The card component then reports its state up to CardWrapper via the useCardLoadingState or useReportCardDataState hook. Inside the hooks the flag is called isDemoFallback; the card passes it to CardWrapper as the isDemoData prop. When isDemoData is true, CardWrapper renders the yellow outline and Demo badge.

Loading vs. Demo Data

On first visit the card shows a loading skeleton while the API call is in flight. Demo data is never shown during this initial loading phase — the skeleton appears instead. On subsequent visits the card instantly renders cached data from IndexedDB while a background refresh runs (stale-while-revalidate pattern).

ScenarioWhat the user sees
First visit, backend reachableLoading skeleton → live data
Return visit, backend reachableCached data instantly → refresh spinner → updated data
No backend / demo modeDemo data with Demo badge and yellow outline
Backend reachable, fetch failsLoading skeleton → demo fallback after 3 failures

Cross-Tab Synchronisation

Toggling demo mode in one browser tab is picked up by all other open tabs through the storage event on localStorage plus a custom kc-demo-mode-change window event. When the mode changes, all registered caches are cleared so that every card transitions through a skeleton state before loading the appropriate data source.

Note on Data Consistency

Because demo data is generated independently by multiple sources (the unified registry, per-hook fallbacks, and per-card files), the synthetic values shown by different cards are not guaranteed to be mutually consistent. Two cards that each derive a summary from their own demo dataset may display different totals for the same logical metric. This is expected: demo mode is designed to showcase the UI layout and card interactions, not to present a coherent simulated cluster state.