red-teaming-api

red-teaming-api

Stub FastAPI service for splitting backend functionality into standalone apps.

Endpoints

  • GET /health -> runs SELECT 1 against configured SQL DB
  • GET /version -> {"version":"0.1.0"} (or APP_VERSION env var)
  • GET /openapi.json -> generated OpenAPI document for all CRUD resources
  • GET /docs -> Swagger UI for the OpenAPI document

CRUD API

The API now exposes CRUD routes for every relational table under /api/v1/....

  • Collection routes:
    • GET /api/v1/<resource> -> list rows
    • POST /api/v1/<resource> -> create a row
  • Single-column primary keys:
    • GET /api/v1/<resource>/{id}
    • PATCH /api/v1/<resource>/{id}
    • DELETE /api/v1/<resource>/{id}
  • Composite primary keys:
    • GET /api/v1/<resource>/item?<pk1>=...&<pk2>=...
    • PATCH /api/v1/<resource>/item?<pk1>=...&<pk2>=...
    • DELETE /api/v1/<resource>/item?<pk1>=...&<pk2>=...

Examples:

# Create an agent
curl -X POST http://localhost:8080/api/v1/agents \
  -H 'content-type: application/json' \
  -d '{"agent_id":"alexbot","name":"alexbot","app_name":"mangrove-alexbot"}'

# Read a single-key resource
curl http://localhost:8080/api/v1/agents/alexbot

# Read a composite-key resource
curl 'http://localhost:8080/api/v1/bug_votes/item?bug_id=bug-1&user_key=karl'

Database Connectivity

Set DATABASE_URL before starting the API or running migrations.

export DATABASE_URL='postgresql+psycopg://USER:PASSWORD@HOST:5432/fdcdb?sslmode=require'

For Fly deployment:

flyctl secrets set DATABASE_URL='postgresql+psycopg://USER:PASSWORD@HOST:5432/fdcdb?sslmode=require' --app red-teaming-api

Migrations

cd red-teaming/api
uv run alembic upgrade head      # apply all migrations
uv run alembic downgrade -1      # rollback one migration

The container entrypoint now runs alembic upgrade head automatically before starting uvicorn when DATABASE_URL is set.

Schema coverage

The current migration chain now creates a relational schema that mirrors the main Firebase RTDB domains used by the live red-teaming site and proxy:

  • agents, users
  • personal_notes, shared_notes, note_order
  • custom_metagames, metagame_votes
  • custom_scenarios, scenario_votes
  • bugs, bug_votes, bug_fixes
  • datasets, community_templates
  • daily_logs, daily_log_entity_summaries
  • workspace_snapshots, workspace_snapshot_files, agent_daily_memory_logs

Document-shaped fields that are still variable in the RTDB payloads, such as evidence, highlights, memory diffs, stats, and template file maps, are stored as JSON columns for now.

Run locally

cd red-teaming/api
export DATABASE_URL='sqlite+pysqlite:///./local.db'
uv run uvicorn app.main:app --reload --port 8080

Run tests

cd red-teaming/api
uv run pytest

Deploy manually

cd red-teaming/api
flyctl deploy --remote-only --app red-teaming-api

Always-on policy

fly.toml is configured to keep this app warm:

  • auto_stop_machines = "off"
  • min_machines_running = 1
  • [[restart]] policy = "always"

After a deploy, verify at least one machine is running:

flyctl machine list --app red-teaming-api --json | jq -r '.[] | [.id, .state, (.config.restart.policy // "unset")] | @tsv'

If the app is already suspended/stopped, recover with:

flyctl machine list --app red-teaming-api --json | jq -r '.[] | select(.state != "destroyed") | .id' | head -n1 | xargs -I{} flyctl machine start {} --app red-teaming-api