# Repository Guide ## Current architecture - `frontend`: React Router SPA. Production files are built in `nginx/Dockerfile`. - `backend`: public FastAPI API and internal RustFS storage API in one process. - `runtime`: shared Jupyter lifecycle, MySQL edit leases and short-lived in-memory access tickets. - `schedule`: APScheduler, MySQL JobStore, MySQL Outbox polling and DAG execution. - `common`: SQLAlchemy models, database/session helpers, IDs and object-store helpers. - `migrations`: Alembic schema and seed migrations. - `nginx`: static frontend, `/api/` proxy and authenticated `/jupyter/` proxy. Redis and the former separate Storage API container are intentionally removed. ## Commands From the repository root: ```bash # Local Python workspace uv sync --all-packages # Static Python check python -m compileall common/src backend/src runtime/src schedule/src # Database migration uv run --package backend alembic upgrade head # Full Docker stack cp .env.example .env docker compose config docker compose up -d --build ``` Frontend development: ```bash cd frontend pnpm install pnpm dev pnpm typecheck pnpm build ``` ## Service rules - Browser traffic enters through Gateway only. - Frontend API calls use same-origin `/api/v1/...` paths. - Backend writes `schedule_runs` and `outbox_events`, then performs best-effort HTTP dispatch to Schedule Executor. - Schedule Executor always polls pending MySQL Outbox rows, so HTTP dispatch failure does not lose a task. - Cron jobs are persisted by APScheduler in MySQL table `apscheduler_jobs`. - Runtime must stay single-replica while file leases and Jupyter tickets use the simplified implementation. - Never expose the internal Jupyter token to the browser. - Never delete Docker volumes when preserving MySQL or RustFS data is required. ## Main entrypoints ```text frontend/app/routes/platform.tsx backend/src/backend/main.py runtime/src/runtime/main.py schedule/src/schedule/main.py nginx/default.conf.template ```