Wingsmith
CLI and console

Console Reference

Overview, serving, fleet, access, activity sections; fixtures mode; read-only mode; theming.

The Wingsmith console is a React web app that talks to the plane’s HTTP API. It never reaches clusters or the hub directly. Every cluster fact it shows reached the plane through the operator’s sync stream first.

Sign-in

/login has one form for two audiences:

  • Admin: account name admin with the plane’s admin password opens the full console.
  • Tenant: any other account name is a tenant slug with that tenant’s console token, and opens the tenant serving view at /serving.

Human access is a single operator. The plane issues one admin session for one admin password and has no per-person users or roles. Session tokens are kept in session storage, so they survive a reload but not the tab.

A 401 triggers one refresh. If that fails, the console signs out and returns to /login.

Pages

Under /_app, for the admin session:

Overview

Fleet health, what needs attention, cluster reachability.

Serving

  • Endpoints
  • Workloads (create, edit, scale, pause, drain)
  • Deployments
  • Artifacts (with hardware fitment)
  • Discovered workloads awaiting adoption

Fleet

  • Clusters (including connecting a new one)
  • Nodes
  • Devices
  • Device classes
  • Pools
  • Allocation board

Access

  • Tenants and their keys
  • Projects
  • Quotas
  • Priority classes
  • Policies

Activity

  • Alerts
  • Workbench (benchmarks and evaluations)
  • Operations (node cordon and drain; workload scale, pause, and drain)
  • Events

Settings

Plane configuration as the plane reports it, and console preferences.

Tenant view

/serving is the tenant’s own view: the models it is served and their metrics.

Read-only mode

A switch on the Settings page makes the console refuse every write before it leaves the browser. It’s a guard against accidents, not an access control: the plane enforces nothing different for it.

The default can be set with environment variables:

  • Build time: VITE_READ_ONLY=true
  • Container runtime: HANGAR_READ_ONLY=true

Fixtures mode

VITE_API_MODE=fixtures swaps the HTTP transport for an in-memory plane in the browser, so the console runs with no backend. Sign-in accepts any admin password without calling anything. Tenant sign-in needs a real plane.

A Fixtures badge shows in the sidebar while it is on. The offline plane is loaded through a dynamic import, so a normal build does not ship it.

Fixtures register against path templates and answer with the plane’s list envelope and error shapes. An unregistered path throws NotFoundError, so a screen built against an endpoint the offline plane does not know fails loudly.

Container image

The Dockerfile builds a static bundle served by nginx on port 8080. Nothing about the plane is baked in: runtime configuration is read from environment variables at container start.

VariableDefaultMeaning
HANGAR_API_BASE_URLemptyPlane origin (plus optional path). Empty means same origin, behind one ingress.
HANGAR_READ_ONLYfalseStart new browser sessions in read-only mode.

Runtime values win over VITE_* build-time ones. The image also sets a Content-Security-Policy, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, and a Referrer-Policy.

The plane image can serve this bundle itself on its own origin, in which case no configuration is needed.

Development configuration

Copy .env.example to .env.local for development. Every variable is optional.

VariableDefaultMeaning
VITE_API_MODEhttpfixtures runs against the in-browser offline plane.
VITE_API_BASE_URLemptyOrigin the browser calls for /v1. Empty means the console’s own origin.
VITE_API_PROXYhttp://localhost:8000Dev server only: where it forwards /v1 and /health. Ignored when VITE_API_BASE_URL is set.
VITE_READ_ONLYfalseStart new browser sessions in read-only mode.

In development the dev server proxies /v1 to the plane, so the browser makes no cross-origin requests and the plane’s CORS allowlist does not need the dev origin.

Theming

Press d to toggle dark and light. Colors are OKLch CSS variables in src/index.css.

Routing

File-based routes under src/routes/. Only route.tsx is a route. page.tsx and PascalCase.tsx files beside it are co-located components. Route components are code-split and load on first visit.

__root.tsx and _app.tsx are layout routes. _app guards everything under it on a signed-in admin session.

Architecture

src/
├── lib/
│   ├── api/             # transport seam: http.ts, fixtures.ts, index.ts
│   ├── config.ts        # runtime config over build-time VITE_* values
│   └── ...              # formatting, pagination, keyboard helpers
├── modules/<domain>/    # types, API calls, queries, derivations; no JSX
│   └── offline/         # the fixtures-mode plane
├── components/
│   ├── layout/          # shell, tables, page header, status, shared tiles
│   ├── <domain>/        # components shared by more than one route
│   └── ui/              # shadcn/ui, generated; do not edit by hand
├── routes/              # file-based routes
└── routeTree.gen.ts     # generated by the router plugin; do not edit

Data access and business rules live in src/modules/. A route folder holds its page and the components only that page uses.