React + TypeScript + Vite SPA for the osctrl admin UI.
Talks exclusively to osctrl-api (port 8081 by default). Served as static files — no Node.js server in production.
The osctrl frontend is the modern operator UI for managing environments,
nodes, node console sessions, queries, saved queries, carves, tags, users,
settings, and optional posture data through osctrl-api.
The frontend is where new operator-facing UX improvements are landing. It
replaces the legacy server-rendered osctrl-admin experience with a React SPA
that talks directly to the API layer.
frontend/
├── src/
│ ├── main.tsx React 19 entry point
│ ├── router.tsx TanStack Router instance
│ ├── routes/ Page components (TanStack Router)
│ ├── components/ Reusable UI components (primitives, atoms, data, chrome, forms, feedback)
│ ├── features/ Feature modules (one folder per page: nodes, queries, carves, ...)
│ ├── api/ Typed API client + generated types
│ ├── lib/ Utilities, custom hooks, time formatting
│ └── styles/ Tailwind base + design token CSS
└── tests/
└── e2e/ Playwright end-to-end tests
| Script | Description |
|---|---|
npm run dev |
Start Vite dev server on port 5173, proxying /api to :8081 |
npm run build |
Type-check then produce dist/ |
npm run preview |
Preview the production build locally |
npm run check |
Run tsc --noEmit (type-check only) |
npm run lint |
Alias for check (linting config added in a later track) |
npm test |
Run Vitest once |
npm run test:watch |
Run Vitest in watch mode |
npm run test:e2e |
Run Playwright e2e tests |
# Terminal 1 — osctrl API (Go)
osctrl-api # start osctrl-api on :8081 for the Vite proxy
# Terminal 2 — React SPA
cd frontend
npm run dev # starts Vite on :5173, proxies /api/* to :8081Open http://localhost:5173 in the browser. Vite's dev proxy forwards all /api/* requests to the running Go API, so auth cookies work as same-origin.
The repository's docker-compose-dev.yml exposes the frontend through
osctrl-nginx at:
https://localhost:8444for the frontend SPA (React + Vite)
In that setup, osctrl-frontend stays internal-only on the Docker network and
osctrl-nginx performs TLS termination before proxying requests to it.
make frontend # runs npm ci + npm run build in frontend/Output: frontend/dist/. Deploy options:
- nginx — serve
dist/as the document root, reverse-proxy/api/*toosctrl-api. Seedeploy/nginx/frontend.conf.example. - Static hosting + CDN — upload
dist/to S3/Cloudfront/etc. Configure CORS on the API. - Docker — build the multi-stage image at
deploy/docker/dockerfiles/Dockerfile-osctrl-frontend(node:22-alpine→nginx:alpine). Single image, no separate Go binary.
- React 19 + TypeScript 7 (strict)
- Vite 8 + @vitejs/plugin-react 6
- TanStack Router 1, Query 5, Table 9
- Tailwind CSS 4 via @tailwindcss/vite
- Radix UI primitives (à la carte)
- react-hook-form 7 + zod 4 + @hookform/resolvers 5
- Monaco Editor 0.56 (@monaco-editor/react 4)
- clsx 2 + tailwind-merge 3 + lucide-react 1
- Vitest 4 + @testing-library/react 16 + jsdom 30
- @playwright/test 1 (e2e)
- @types/react 19, @types/react-dom 19, @types/node 26