CyberArena / README.md
Hussien Haider
H
04fc815
|
Raw
History Blame Contribute Delete
9.82 kB
---
title: CyberArena Backend
emoji: πŸ›‘οΈ
colorFrom: purple
colorTo: blue
sdk: docker
app_port: 7860
pinned: false
license: mit
---
# CyberArena β€” Backend
> FastAPI backend for the CyberArena cybersecurity training platform.
> Single source of truth: this directory. The old `Apex/backend/` has
> been removed.
## Quick start
```bash
cd CyberArena
pip install -r requirements.txt
python main.py # serves on http://localhost:8090
```
The server loads `.env` automatically from this folder.
## Project layout
```
CyberArena/
β”œβ”€β”€ main.py ← 5-line uvicorn entry
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ start.cmd
β”‚
β”œβ”€β”€ app/ ← all real code
β”‚ β”œβ”€β”€ main.py ← FastAPI app, middleware, startup
β”‚ β”œβ”€β”€ types.py ← Pydantic request models
β”‚ β”‚
β”‚ β”œβ”€β”€ core/ ← constants + pure helpers (no I/O)
β”‚ β”‚ β”œβ”€β”€ config.py ← env-loaded handles (Supabase, AI URLs, keys)
β”‚ β”‚ β”œβ”€β”€ constants.py ← topic catalog, fallback HTML, cert config
β”‚ β”‚ β”œβ”€β”€ security.py ← normalize_vuln_key, ip/ioc/time matchers
β”‚ β”‚ β”œβ”€β”€ text.py ← parse_json_safe (LLM JSON parser)
β”‚ β”‚ └── module_router.py ← module β†’ challenge_type dispatcher
β”‚ β”‚
β”‚ β”œβ”€β”€ services/ ← business logic / I/O
β”‚ β”‚ β”œβ”€β”€ supabase_service.py ← PostgREST client (CRUD on pool tables)
β”‚ β”‚ β”œβ”€β”€ challenge_loader.py ← row β†’ TrainingData mappers (per type)
β”‚ β”‚ β”œβ”€β”€ scenario_service.py ← Groq scenario + challenge generation
β”‚ β”‚ β”œβ”€β”€ evaluator.py ← /evaluate handlers (5 types)
β”‚ β”‚ β”œβ”€β”€ completion_service.py ← user_completions idempotent tracker
β”‚ β”‚ β”œβ”€β”€ certificate_service.py ← PDF build + cert issue/verify/download
β”‚ β”‚ └── file_storage.py ← write downloadable files to disk
β”‚ β”‚
β”‚ β”œβ”€β”€ generators/ ← per-type AI pool generators
β”‚ β”‚ β”œβ”€β”€ __init__.py ← REGISTRY: list of (slug, module, teams)
β”‚ β”‚ β”œβ”€β”€ crypto.py ← owns encryption_challenges
β”‚ β”‚ β”œβ”€β”€ code_fixing.py ← owns code_fixing_challenges
β”‚ β”‚ β”œβ”€β”€ log_analysis.py ← owns log_analysis_challenges
β”‚ β”‚ └── vulnerability_hunter.py ← owns vulnerability_hunter_challenges
β”‚ β”‚
β”‚ β”œβ”€β”€ sandbox/ ← in-process OS simulator sandbox
β”‚ β”‚ β”œβ”€β”€ workdir.py ← per-challenge tempdir + safe_join
β”‚ β”‚ β”œβ”€β”€ tools.py ← built-in cat/ls/sha256sum/base64/…
β”‚ β”‚ └── terminal.py ← shlex parse + whitelist subprocess run
β”‚ β”‚
β”‚ └── api/ ← FastAPI routers (HTTP shell only)
β”‚ β”œβ”€β”€ auth.py ← /api/auth
β”‚ β”œβ”€β”€ xp.py ← /api/xp
β”‚ β”œβ”€β”€ leaderboard.py ← /api/leaderboard
β”‚ β”œβ”€β”€ certificates.py ← /api/certificates*
β”‚ β”œβ”€β”€ training.py ← /api/training/*
β”‚ β”œβ”€β”€ terminal.py ← /api/training/terminal*
β”‚ └── onevone.py ← /api/onevone/* (head-to-head matches)
β”‚
β”œβ”€β”€ db/ ← all database concerns
β”‚ β”œβ”€β”€ schema/ ← numbered, idempotent SQL migrations
β”‚ β”‚ β”œβ”€β”€ 000_extensions.sql
β”‚ β”‚ β”œβ”€β”€ 001_users.sql
β”‚ β”‚ β”œβ”€β”€ 002_encryption_challenges.sql
β”‚ β”‚ β”œβ”€β”€ 004_code_fixing_challenges.sql
β”‚ β”‚ β”œβ”€β”€ 005_log_analysis_challenges.sql
β”‚ β”‚ β”œβ”€β”€ 006_vulnerability_hunter_challenges.sql
β”‚ β”‚ β”œβ”€β”€ 007_onevone.sql
β”‚ β”‚ β”œβ”€β”€ 008_certificates.sql
β”‚ β”‚ β”œβ”€β”€ 009_user_completions.sql
β”‚ β”‚ β”œβ”€β”€ 010_challenge_files_bucket.sql
β”‚ β”‚ β”œβ”€β”€ 011_challenge_type_normalization.sql
β”‚ β”‚ └── 012_onevone_vuln_hunter.sql
β”‚ β”œβ”€β”€ seed/ ← per-type example JSON (template)
β”‚ β”‚ β”œβ”€β”€ README.md
β”‚ β”‚ β”œβ”€β”€ crypto.example.json
β”‚ β”‚ β”œβ”€β”€ web.example.json
β”‚ β”‚ β”œβ”€β”€ code_fixing.example.json
β”‚ β”‚ β”œβ”€β”€ log_analysis.example.json
β”‚ β”‚ └── vulnerability_hunter.example.json
β”‚ β”œβ”€β”€ apply.py ← apply every schema/*.sql in order
β”‚ └── README.md
β”‚
β”œβ”€β”€ scripts/ ← ops utilities
β”‚ β”œβ”€β”€ check_state.py ← one-page DB health report
β”‚ └── seed_pools.py ← pool size + status (read-only by default)
β”‚
β”œβ”€β”€ .env ← secrets (never commit)
└── README.md
> **No local file storage.** AI-generated challenge files are uploaded
> to the `challenge-files` Supabase Storage bucket (see
> `db/schema/010_challenge_files_bucket.sql`) and the public URL is
> returned in the training payload. The backend never writes to its
> own disk.
```
## API surface
The FastAPI app exposes **28 routes** under `/api/`. The full list
lives in `app/main.py`; the most important are:
| Method | Path | Notes |
|--------|-------------------------------------|-------|
| POST | `/api/auth` | proxied to Supabase `apex-auth` edge function |
| POST | `/api/xp` | proxied to Supabase `apex-xp` edge function |
| GET | `/api/leaderboard` | top users by XP |
| GET | `/api/training/list` | dashboard cards |
| POST | `/api/training/generate` | hydrate cached scenario into a full challenge |
| POST | `/api/training/evaluate` | legacy red+blue AI evaluator |
| POST | `/api/training/evaluate-web` | 3-layer web exploitation validator |
| POST | `/api/training/evaluate-code-fix` | AI code-fix grader |
| POST | `/api/training/evaluate-log-analysis` | 4-field exact match + Mistral feedback |
| POST | `/api/training/evaluate-vulnerability-hunter` | exact canonical-key match |
| POST | `/api/training/terminal*` | OS simulator sandbox |
| POST | `/api/certificates` | list / issue |
| GET | `/api/certificates/{id}/pdf` | streamed PDF |
| GET | `/api/certificates/verify/{code}` | public QR landing |
| GET | `/api/certificates/progress` | (completions, required) |
| POST | `/api/onevone/rooms` | 1v1 mode (see `app/api/onevone.py`) |
## Pool architecture
Each challenge type owns its own table + its own generator. The
orchestrator in `app/main.py::populate_pool_background` walks
`app.generators.REGISTRY` and starts one watcher per (type, team).
Pool model: `POOL_TARGET / POOL_THRESHOLD / POOL_BATCH` are module
constants inside each generator. The watcher refills the table
when the count drops to `POOL_THRESHOLD` and inserts `POOL_BATCH` new
rows in parallel via `asyncio.gather`.
See `../AGENTS.md` "Pool Architecture" for the full contract.
## Database
Apply the migrations to Supabase with:
```bash
python db/apply.py # needs SUPABASE_SERVICE_KEY in .env
```
Each `.sql` file is idempotent (`CREATE … IF NOT EXISTS`, `DROP
POLICY IF EXISTS + CREATE`). See `db/README.md` for details.
## Running tests
There is no formal test suite yet. Use:
```bash
python -c "from app.main import app; print(len(app.routes), 'routes')"
python scripts/check_state.py # requires SUPABASE_URL
```
## Deployment
The provided `Dockerfile` pins `python:3.11-slim` and starts the
server on `PORT` (default 7860, matches Hugging Face Spaces).
### Hugging Face Spaces (Docker SDK)
1. Create a new Space β†’ **Docker** SDK.
2. Push this folder (`CyberArena/`) to the Space's git remote:
```bash
cd CyberArena
git remote add hf https://huggingface.co/spaces/Alpha-Team/CyberArena
git push hf main
```
3. In the Space's **Settings β†’ Repository secrets**, add every key
the backend needs. HF injects them as environment variables
(the loader in `app/_env.py` does **not** override process env,
so secrets take priority over any `.env` shipped in the repo).
| Secret | Required |
|---|---|
| `SUPABASE_URL` | yes |
| `SUPABASE_ANON_KEY` | yes |
| `CLOUDFLARE_API_TOKEN` | yes (primary AI) |
| `CLOUDFLARE_ACCOUNT_ID` | yes |
| `CLOUDFLARE_MODEL` | optional (default `@cf/qwen/qwen2.5-coder-32b-instruct`) |
| `GROQ_API_KEY` | yes (fallback AI) |
| `GROQ_MODEL` | optional (default `llama-3.1-8b-instant`) |
| `NVIDIA_API_KEY` | yes (tertiary AI) |
| `NVIDIA_MODEL` | optional (default `deepseek-ai/deepseek-v4-pro`) |
| `MISTRAL_API_KEY` | yes (quaternary AI + vuln-hunter grader) |
| `MISTRAL_MODEL` | optional (default `mistral-small-latest`) |
4. The Space URL will be `https://<user>-<space>.hf.space`. The
frontend's `VITE_API_URL` must be set to that URL + `/api`
(e.g. `https://alpha-team-cyberarena.hf.space/api`).
5. **Workdir caveat**: per-challenge user scripts (`solve.py`, …)
live in `tempfile.mkdtemp()` on the container's `/tmp`. The
container's disk is **lost on Space sleep / restart**, but
challenge data and user XP live in Supabase and persist.
6. The Space can sleep after 48h of inactivity. To stay always-on,
upgrade the Space's hardware tier or wake it manually before
class.
> The `.env` file is **never** uploaded β€” it's listed in
> `.dockerignore`. The HF secrets panel is the only source of
> environment variables in production.