Architecture
System Overview
FUSION GUI uses a React SPA + FastAPI backend architecture. The backend serves both the API and the pre-built React static files, enabling single-command startup for end users.
See: diagrams/architecture.txt for visual representation
Why This Architecture?
Alternative |
Why Not |
|---|---|
Electron/Tauri |
Adds native packaging complexity, 200MB+ bundle size, not needed since browser is acceptable |
PyQt5 WebEngine |
Cross-platform Qt issues, ties to Qt ecosystem, complicates pip distribution |
Separate repos |
API drift, coordinated releases, users must install two things |
React + FastAPI wins because:
Single
pip installincludes everythingFastAPI serves static files + API on one port
Subprocess isolation keeps server stable during long runs
REST API naturally extends to future HPC job submission
Industry-standard tooling with excellent documentation
Component Architecture
Backend (Python)
fusion/api/
├── __init__.py
├── main.py # FastAPI app, lifespan, static serving
├── config.py # Settings via pydantic-settings
├── dependencies.py # Dependency injection (DB sessions, etc.)
├── routes/
│ ├── __init__.py
│ ├── runs.py # /api/runs endpoints
│ ├── configs.py # /api/configs endpoints
│ ├── artifacts.py # /api/artifacts endpoints
│ ├── topology.py # /api/topology endpoints
│ └── system.py # /api/health, /api/version
├── schemas/
│ ├── __init__.py
│ ├── run.py # Run request/response models
│ ├── config.py # Config models
│ └── common.py # Shared models (pagination, errors)
├── services/
│ ├── __init__.py
│ ├── run_manager.py # Job lifecycle, process management
│ ├── config_service.py # Config loading/validation
│ ├── progress_watcher.py # Progress file monitoring
│ └── artifact_service.py # Safe artifact access
├── db/
│ ├── __init__.py
│ ├── database.py # SQLite connection, session factory
│ └── models.py # SQLAlchemy ORM models
└── static/ # Built React files (generated, gitignored)
└── .gitkeep
Frontend (React)
frontend/
├── package.json
├── vite.config.ts
├── tsconfig.json
├── tailwind.config.js
├── index.html
├── src/
│ ├── main.tsx # Entry point
│ ├── App.tsx # Root component, routing
│ ├── api/
│ │ ├── client.ts # Axios/fetch wrapper, error handling
│ │ ├── runs.ts # Run API functions
│ │ ├── configs.ts # Config API functions
│ │ └── types.ts # Generated/shared API types
│ ├── components/
│ │ ├── ui/ # Primitive UI components (Button, Card, etc.)
│ │ ├── layout/ # Layout components (Sidebar, Header)
│ │ ├── runs/ # Run-specific components
│ │ ├── config/ # Config editor components
│ │ ├── artifacts/ # File browser components
│ │ └── topology/ # Network visualization
│ ├── pages/
│ │ ├── RunListPage.tsx
│ │ ├── RunDetailPage.tsx
│ │ ├── NewRunPage.tsx
│ │ ├── ConfigEditorPage.tsx
│ │ └── TopologyPage.tsx
│ ├── hooks/
│ │ ├── useRuns.ts # Run queries/mutations
│ │ ├── useSSE.ts # SSE connection hook
│ │ └── useProgress.ts # Progress subscription
│ ├── stores/
│ │ └── ui.ts # UI state (sidebar, theme)
│ ├── lib/
│ │ └── utils.ts # Utility functions
│ └── styles/
│ └── globals.css # Tailwind imports, custom styles
└── public/
└── favicon.ico
Repository Layout (Monorepo)
FUSION/
├── fusion/ # Python package (existing)
│ ├── api/ # NEW: FastAPI server
│ ├── cli/
│ │ └── run_gui.py # MODIFY: Launch GUI server
│ └── ...
├── frontend/ # NEW: React application
├── docs/
│ └── gui/ # This documentation
├── data/
│ └── gui_runs/ # NEW: GUI-managed run data
├── pyproject.toml # MODIFY: Add [gui] extras
├── Makefile # MODIFY: Add GUI targets
└── .github/
└── workflows/
└── gui.yml # NEW: GUI CI workflow
Why Monorepo?
Benefit |
Explanation |
|---|---|
Atomic commits |
Frontend + backend changes in one PR |
No version drift |
Single version number, coordinated releases |
Simpler CI |
One pipeline builds and tests everything |
Shared tooling |
Common pre-commit, Makefile targets |
IDE flexibility |
Devs can open |
Communication Model
REST for CRUD Operations
Standard request/response for:
Creating/listing/deleting runs
Fetching configs and templates
Downloading artifacts
SSE for Real-Time Updates
Server-Sent Events for:
Log streaming (tail of
sim.log)Progress updates (watching
progress.jsonl)Run status changes
Why SSE over WebSocket?
One-way data flow (server to client only)
Auto-reconnect built into browsers
Simpler implementation, no connection state
Works through corporate proxies
Process Model
Simulation Subprocess
FastAPI Server (PID 1000)
|
+-- subprocess.Popen() with start_new_session=True
|
+-- fusion-sim process (PID 2000, PGID 2000)
|
+-- multiprocessing.Pool workers (PIDs 2001, 2002, ...)
Critical: Simulations run in a new process session so we can kill the entire process tree with os.killpg().
Concurrency Model
MVP: Sequential runs only (one active run at a time)
Later: Configurable concurrency limit, job queue
Persistence
Data |
Storage |
Survives Restart? |
|---|---|---|
Run metadata |
SQLite ( |
Yes |
Run status |
SQLite + filesystem |
Yes |
Logs |
Filesystem ( |
Yes |
Progress |
Filesystem ( |
Yes |
Artifacts |
Filesystem ( |
Yes |
Platform Support
Platform |
Status |
Notes |
|---|---|---|
Linux |
Full support |
Primary development platform |
macOS |
Full support |
Tested on Apple Silicon and Intel |
Windows |
Supported (M2+) |
Requires platform-specific process termination |
Process termination by platform:
POSIX (Linux/macOS): Use
start_new_session=True+os.killpg(pgid, signal)to kill entire process treeWindows: Use Job Objects or
taskkill /T /F /PIDfallback (see 06-backend-standards.md)
Windows support is tested in CI starting M2. MVP (M1) may work on Windows but is not the primary target.
Security Considerations
Localhost only: Bind to
127.0.0.1, not0.0.0.0No auth: Single-user local use, no credentials needed
Path traversal protection: Artifact downloads validate paths are within run directory, reject symlinks escaping the run directory
No secrets in logs: Config sanitization before display