Live demo: https://sudopay.onrender.com
No setup needed to try it. Open the link, press Run Adversarial Campaign, and the whole pipeline runs in front of you. The instance is on a free plan and sleeps when idle, so the first request after a quiet period can take 50 seconds or more to wake it. Everything after that is quick.
The application itself is branded AI Defense Lab. sudopay.onrender.com is only the
deployment hostname, and sudoWin is only the GitHub repository name. See
Naming if that is confusing.
A red team / blue team laboratory for payment fraud. Synthetic fraud agents attack a fake payment environment, an ensemble detector scores every event, and a feedback loop finds whatever slipped through, mutates it, and throws it back at the detector.
The interesting part is what happens next. adaptive_security/ runs both sides as adapting
agents: a population of attack strategies that evolves against a defender that re-tunes its own
policy each generation. Both spend from a synthetic budget. They push against each other for
as long as you let them run.
Everything here is synthetic and local. There are no real card numbers, CVVs, OTPs, bank details
or personal data anywhere in the repository. See docs/THREAT_MODEL.md for the boundary.
Three different names show up around this project, so to be explicit:
| Project | Adversarial Payment Security Lab |
| Application branding (what the UI says) | AI Defense Lab |
| GitHub repository | https://github.com/aroh3006/sudoWin |
| Live deployment | https://sudopay.onrender.com |
| Active branch | feat/project-titan |
Python 3.12 with FastAPI, XGBoost, scikit-learn, pandas and networkx on the backend. React 19 with TypeScript, Vite, Tailwind 4, react-router and recharts on the frontend. SQLite for storage. No external services are required to run it.
The brief asks for three things, judged separately:
- Identify the landscape of GenAI-era payment fraud.
docs/ATTACK_CATALOG.mdcovers seven families implemented as running agents plus eight more researched but not built. - Generate those attacks at scale.
attack_harness/does this against the synthetic world incore/, deterministically under a seed. - Defend against them.
detector/scores, explains and mitigates;feedback_loop/closes the loop.
| Family | Description |
|---|---|
card_testing | Automated low-value probes against stolen payment instruments |
synthetic_identity | Fabricated personas that pass KYC, then bust out |
mule_network | Structuring: many senders, few collectors, then cash-out |
cnp_social_engineering | Social-engineered account recovery, then a large card-not-present charge |
refund_abuse | Repeated purchase-then-refund cycles against chatbot approval flows |
account_takeover | Credential-stuffing burst, successful login, account drain |
promo_abuse | Disposable personas farming a new-customer promo |
The first five were the required set. The last two were added because they exercise different
parts of the graph. Full specs are in docs/ATTACK_CATALOG.md.
Three signals feed one score:
- ML: XGBoost over 26 engineered features. All of them are computed causally. A feature for a given event only ever looks at earlier events. There is a regression test for this.
- Graph: anomaly detection over a customer / device / IP / instrument graph. It ranks a node by how many distinct customers touch it, by percentile rather than raw degree. Not a GNN.
- Rules: a hand-written score that stays readable and works before any model has trained.
They combine into a 0-100 risk score and an ALLOW / REVIEW / BLOCK decision. After that, a triage step writes a short explanation and a mitigation step records a simulated action. The LLM triage runs fine with no API key; it falls back to a deterministic explainer.
Known weak spots are written up honestly in docs/MODEL_CARD.md.
Generate, detect, evaluate, find the false negatives, mutate whichever family is doing best at evading, then run it again. The mutated variant goes first against the unchanged detector, which shows the evasion, then the detector retrains and scores it again, which shows the recovery.
On the last full run, redistributing devices and IPs across cnp_social_engineering and
refund_abuse dropped recall from 0.958 to 0.656. Retraining brought it back to 0.912. Numbers
are in docs/EVALUATION.md.
adaptive_security/ is the second loop. Instead of mutating one family at a time against a fixed
detector, both sides adapt.
Attacks are represented as genomes: ten behavioral traits plus a mix of families, which express into the same parameter dicts the existing agents already take. Nothing about attack generation is reimplemented. Selection is NSGA-II across four objectives (evasion, extracted value, novelty, efficiency) with crossover, elitism and a genealogy that records where every strategy came from.
Mutation is guided rather than random. Each strategy knows which detector component caught it, so it drifts in the direction that component is weakest.
The defender searches a bounded neighbourhood of its current policy each generation, scoring candidates against recent history before deploying one. Both sides pay for what they do. Attacker infrastructure, defender investigations, mitigations and false-positive friction all post to a double-entry ledger, which means "catch everything" is not automatically the winning move.
On top of that there is a novelty engine that clusters observed behavior and flags genuinely new modes. An Oracle reads the whole run and reports who is adapting faster.
Longer write-up in docs/ADAPTIVE_SECURITY.md.
Python 3.12+ and Node 20+.
python -m venv .venv
.venv\Scripts\activate # Windowssource .venv/bin/activate # macOS/Linux
pip install -r requirements.txtcd frontend && npm installCopy .env.example to .env if you want. Nothing in it is required. The only setting that
does anything is LLM_API_KEY, which swaps the fallback triage explainer for a real model.
Two terminals:
python -m uvicorn backend.app.main:app --reload --port 8000cd frontend && npm run devThen open http://localhost:5173 and press Run Adversarial Campaign. That builds a world,
injects attacks, trains the detector and runs the feedback loop. Reset Experiment clears it.
In development the Vite server proxies /api to the backend on port 8000. In the deployed build
there is no proxy, because the backend serves the frontend itself (see Deployment).
There are six pages:
| Page | What it shows |
|---|---|
| Overview | Headline detection metrics, the ALLOW / REVIEW / BLOCK split, and the agent catalog |
| Red Team | Each attack agent, plus the fraud ring clusters found in the entity graph |
| Blue Team | Precision, recall, F1, ROC-AUC, per-family recall, feature importances, confusion matrix |
| Feedback Loop | Generation-by-generation recall, and the mutation records behind it |
| Live Stream | Every scored event with its evidence, triage explanation and mitigation, filterable by decision, with an optional ground-truth reveal for evaluation |
| War Room | The co-evolution layer. Starts its own runs and shows the Arms Race Index, Red population, deployed Blue policy, Oracle findings, economy ledger and strategy library |
Judge Mode is not one of these pages. It runs from the command line or through the
/api/judge endpoints (see below).
Generate attacks:
python -m attack_harness.orchestrator --agent card_testing --events 5000 --seed 42
python -m attack_harness.orchestrator --scenario full --events 10000 --seed 42 --resetRun the feedback loop and the full evaluation:
python -m feedback_loop.loop --seed 42 --generations 3 --baseline-events 6000 --mutation-events 300
python -m evaluation.run_evaluation --seed 42 --generations 3 --baseline-events 6000 --mutation-events 300The evaluation writes data/results/EVALUATION_RESULTS.md plus a timestamped JSON file.
Run the co-evolution simulation:
python -m adaptive_security.simulation.runner --generations 10 --seed 42 --mode fast
python -m adaptive_security.simulation.runner --generations 100 --seed 42 --mode researchModes are debug, fast, standard and research. They differ only in scale. A quick run
and a long one are directly comparable. Any subsystem can be switched off to see what it was
contributing: --no-graph, --no-rules, --no-novelty, --no-adaptive-defender, --no-memory,
--no-economy, --no-coevolution.
For a single reproducible demo of the whole thing:
python -m adaptive_security.judge --seed 42 --generations 12That runs the real simulation and then compares generation 0 against the final generation. It is
not a recording. If nothing novel emerges in a run, it says so. The same thing is reachable over
HTTP via POST /api/judge/run, then GET /api/judge/status and GET /api/judge/narrative.
GET /api/judge/plan describes what it will do before you start it.
The experiment lab runs named arms and ablation suites and writes results with the seed and config attached:
python -m adaptive_security.experiments.lab --list
python -m adaptive_security.experiments.lab --suite core --generations 20 --seed 42python -m pytest tests/ -q275 tests, all passing. Roughly a quarter cover the base pipeline and the rest cover the co-evolution layer, including checkpoint round-trips and a full end-to-end simulation.
Live at https://sudopay.onrender.com, deployed from the feat/project-titan branch of
https://github.com/aroh3006/sudoWin.
It is one Render web service rather than two. The frontend already calls the API on relative
/api paths, so the FastAPI process serves the built React app as well. That means the deployed
build needs no API base URL of its own, no CORS exchange happens, and there is a single URL. A
catch-all returns index.html for unmatched paths so deep links such as /war-room work on a
direct load, while unknown /api paths still return a JSON 404.
The whole definition lives in render.yaml:
| Build | pip install -r requirements.txt, then npm ci && npm run build in frontend/ |
| Start | uvicorn backend.app.main:app --host 0.0.0.0 --port $PORT |
| Health check | /api/health |
| Environment | PYTHON_VERSION, DATABASE_URL, DEFAULT_SEED, PYTHONUNBUFFERED |
No secret is configured and none is needed. Python is pinned to 3.12 because numpy 1.26.4 publishes no wheels for 3.13.
Worth knowing before you judge the live instance:
- The free plan sleeps when idle. The first request after a quiet period can take 50 seconds or more. Requests after that are normal.
- SQLite sits on an ephemeral disk, so campaign data does not survive a redeploy. This is deliberate, since the dashboard regenerates it on demand. If the live site looks empty, press Run Adversarial Campaign.
- The free plan caps memory at 512 MB and the scientific stack is large.
debugandfastsimulation modes are the ones verified on the deployed instance. A fullresearchrun at scale is more likely to be constrained there than locally. - Detector weaknesses, including the families it handles least well, are documented honestly in
docs/MODEL_CARD.mdrather than smoothed over.
docs/ARCHITECTURE.mdanddocs/ADAPTIVE_SECURITY.md: how it fits togetherdocs/ATTACK_CATALOG.md: every fraud vector, implemented or notdocs/MODEL_CARD.md: the detector, including where it is weakdocs/EVALUATION.md: methodology and measured resultsdocs/THREAT_MODEL.md: what this does and does not touchdocs/CHALLENGE_REQUIREMENTS.md: requirement traceability for the submissiondocs/REPORT_TRACKER.md: the consolidated project reportdocs/ORIGINALITY_AUDIT.mdanddocs/SOURCES.md: provenancedocs/AI_Defense_Lab_Solution_Walkthrough.pptx: the walkthrough deck
Similarity check:
npx jscpd . --ignore "node_modules/**,.venv/**,data/**"