CyberArena / README.md
Hussien Haider
H
04fc815
|
Raw
History Blame Contribute Delete
9.82 kB
metadata
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

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:

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:

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:
    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.