A React + Vite + Tailwind CSS single-page application that provides a real-time interface for submitting intents, monitoring the safety gate, inspecting the System 2 reasoning process, visualising the resulting JSON-LD execution plan as a directed acyclic graph (DAG), and monitoring the Hybrid Governance workflow pipeline.
- Technology Stack
- Project Structure
- Application Flow
- Components
- API Client
- Build and Development
- Production Deployment
| Tool | Version | Role |
|---|---|---|
| React | 18 | UI component framework |
| Vite | 5 | Build tool and dev server |
| Tailwind CSS | 3 | Utility-first CSS styling |
| PostCSS + Autoprefixer | — | CSS processing pipeline |
No external state management library is used. Application state is handled with React's built-in useState and useEffect.
frontend/
├── index.html Entry HTML shell (mounts #root)
├── vite.config.js Vite config + dev-server proxy to backend
├── tailwind.config.js Tailwind content paths
├── postcss.config.js PostCSS plugins (tailwind + autoprefixer)
├── package.json NPM dependencies and scripts
├── Dockerfile Multi-stage build: Vite → nginx:alpine
├── nginx.conf SPA fallback + /api/ reverse proxy + security headers
└── src/
├── main.jsx React DOM bootstrap
├── index.css Tailwind directives (@tailwind base/components/utilities)
├── App.jsx Root component — orchestrates the full pipeline UI
├── components/
│ ├── IntentInput.jsx Prompt textarea + max-retries field + submit button
│ ├── Console.jsx Scrollable reasoning log (green-on-black terminal style)
│ └── DagViz.jsx Step-by-step plan visualiser (card-based DAG)
└── services/
└── api.js Typed fetch wrappers for all backend endpoints
The UI mirrors the three-phase core pipeline and locks the form while a phase is running. A system state badge reflects the engine lifecycle in real time.
User types prompt
│
▼
[1/3] POST /api/v1/validate
├── rejected → show SafetyGate metrics (red), stop
└── approved → show metrics (green), continue
│
▼
[2/3] POST /api/v1/transform
├── error (retries exhausted) → log failure, show SEALED badge, stop
└── success → render DagViz with plan steps, log reasoning steps in Console
│
▼
[3/3] POST /api/v1/deploy
├── failure → show error in ExecutionResult panel
└── success → show container ID + output, refresh system state badge
Throughout the process, a System State badge at the top of the page reflects INIT, ACTIVE, or SEALED in real time.
The root component. It owns all shared state and drives the three-phase pipeline:
| State variable | Type | Purpose |
|---|---|---|
phase |
PHASE enum |
Tracks which pipeline step is running |
systemState |
string | Current engine state (INIT / ACTIVE / SEALED) |
gate |
object | SafetyGate result from /validate |
plan |
object | PlanSchema result from /transform |
result |
object | ExecutionResult from /deploy |
logs |
string[] | Accumulated reasoning log lines for the Console |
The handleSubmit function calls the three API methods in sequence. Any error causes a transition to ERROR and halts the pipeline.
System State badge colours:
- Yellow —
INIT - Green —
ACTIVE - Red —
SEALED(also disables the input form)
A controlled form component for composing the intent.
| Element | Behaviour |
|---|---|
| Textarea | Bound to prompt state; submit disabled if fewer than 10 chars |
| Retries field | Number input clamped to 0–3; mirrors IntentSchema.max_retries |
| Submit button | Disabled when phase is a busy state or systemState === "SEALED" |
Full validation is delegated to the backend IntentSchema — the UI only enforces the minimum-length guard.
A read-only scrollable log panel styled as a terminal.
- Receives a
lines: string[]prop; each element is one log entry. - Lines rendered as
<div>withwhitespace-pre-wrapso multi-line reasoning steps display correctly. - When empty, displays "Awaiting output…" in muted grey.
- Auto-scrolls to the latest entry.
Renders the JSON-LD execution plan as a vertical card-based DAG.
Each step card shows:
- Step number in a colour-coded circle: purple for
EXEC, teal forROUTE. - Action type badge with matching colour scheme.
- TTL in seconds, aligned right.
paramsJSON as a<pre>block for non-empty params.
Steps are connected by a vertical line between circles. The plan header shows the truncated intent_id (first 8 chars) and checksum (first 12 chars) for quick integrity reference.
src/services/api.js wraps all backend calls. All functions return parsed JSON or throw { status, detail }.
api.getStatus() // GET /api/v1/status
api.validateIntent(rawPrompt, retries) // POST /api/v1/validate
api.transformIntent(rawPrompt, retries) // POST /api/v1/transform
api.deployPlan(plan) // POST /api/v1/deployThe Vite dev server proxies /api/* to http://localhost:8000, so the frontend calls relative URLs during development without CORS issues. In production, nginx handles the same proxy rule.
cd frontend
npm install
npm run dev # http://localhost:5173 (hot module replacement enabled)The backend must be running on port 8000.
npm run build # outputs to dist/
npm run preview # local preview of the production buildNo environment variables are required for the frontend. The API base path is /api/v1 (relative), proxied by both Vite (dev) and nginx (prod).
The Dockerfile uses a two-stage build:
Stage 1 — Builder (node:20-alpine):
- Runs
npm ciandnpm run build. - Produces a static
dist/directory.
Stage 2 — Runtime (nginx:1.27-alpine):
- Copies
dist/to nginx's web root. - Uses the custom
nginx.confwhich:- Serves
index.htmlfor all unknown paths (SPA fallback). - Proxies
/api/to theapex-backendcontainer. - Adds
X-Frame-Options,X-Content-Type-Options, andContent-Security-Policyheaders.
- Serves
The resulting image is stateless and contains no Node.js or build tooling at runtime.