Spaces:
Sleeping
Citadel β Implementation Plan & Status
Bastion defended. Sentinel supervised. Citadel is an LLM council that governs, critiques, co-evolves, and earns trust.
Status: BUILT + TRAINED + INFERENCE VERIFIED β
Citadel is fully implemented, GRPO-trained, and end-to-end inference verified.
Themes Targeted (Quadruple Threat)
| Theme | How Citadel hits it | Bonus sub-theme |
|---|---|---|
| Theme 1 β Multi-Agent Interactions | Commander + Oversight form an LLM council β structured critique, revision loop, post-mortem lessons. Distinct reward functions, bidirectional trust dynamics, shared playbook. | Fleet AI β Scalable Oversight |
| Theme 3.1 β Professional Tasks | Enterprise governance: CAB approval, SOX audit, data owner notification, Slack channels, ServiceNow tickets, GDPR breach timer. | Scaler AI Labs β Multi-App Enterprise Workflows |
| Theme 4 β Self-Improvement | (1) Gen 1β2β3β4 adversary curriculum. (2) Shared playbook β agents write lessons each episode surfaced in future episodes. (3) Gen 4 live LLM adversary. | β |
| Theme 5 β Wild Card | Trust Dynamics: Bidirectional trust scores (trust_c2o, trust_o2c) that evolve from behavior and shape future interaction. Novel relational RL signal. | β |
Core Narrative
"Bastion taught an AI to fight cyberattacks. Citadel adds everything else a real SOC actually needs: enterprise governance that enforces real CAB/SOX/GDPR compliance, an adversary that grows through four generations including a live LLM attacker, and a trust layer between the AI agents themselves. Because in a real SOC, the technology is the easy part β the governance, the evolving adversary, and the interpersonal dynamics are what actually break down."
Module-by-Module Breakdown
models.py β State & Action Schema
- 18-action
ActionTypeenum (10 incident response + 8 governance) IncidentActionwithjustification,cited_lessons,method,scope,rollback_plan, governance argsCommanderProposalpydantic modelOversightDecisionenum: APPROVE / REVISE / VETO / FLAG_FOR_HUMANOversightActionwith structured critique:{risk_tier, weakness, missing_evidence, counter_proposal}CouncilState+ProposalRecordtracking all council historyIncidentStateincludesgovernance_state,trust_state,council_state,stakeholder_state,adversary_genIncidentObservationincludes governance summary, trust summary, stakeholder asks, shared playbook context, last Oversight critique- Added fields on
IncidentObservation:done: bool,reward: Optional[float],oversight_reward: Optional[float],metadata: Optional[Dict]β needed for training reward functions and inference recorder
dynamics.py β Realistic Attack Simulation
Genuine simulation using a network graph:
- Network topology graph:
NETWORK_ADJACENCYβ attacker can only spread to adjacent systems - Probabilistic spread:
base_chance = 0.25 Γ stealth, halved if patched, halved again if monitoring_level β₯ 2 - State machine: systems track
compromised,isolated,patched,has_backdoor,integrity,monitoring_level - Detection model: monitoring level determines if a spread is visible in the SIEM
- Exfil rate tied to integrity:
0.08 Γ stealth Γ integrity - Stealth decay: attacker gets bolder each hour (-0.03/hr); investigation hammers stealth (-0.15)
- Restoring from compromised backup re-infects the system
Alert templates (MITRE ATT&CK mapped):
LATERAL_MOVEMENT_ALERTS: 12 templates (SMB, RDP, WinRM, Pass-the-Hash, DCOM, session hijack, SSH, PtT, EternalBlue, tool transfer, cmd shell)EXFILTRATION_ALERTS: 8 templates (HTTPS, DNS tunneling, C2 beacon, rclone, FTP, robocopy, chunked transfers, PowerShell)FALSE_POSITIVE_ALERTS: 10 templates (SSH scanner, port scan, scheduled task, PowerShell, HTTPS beacon, BITS transfer, AD enum, ARP sweep, AV restart, systeminfo)PRIVILEGE_ESCALATION_ALERTS: 5 templates (UAC bypass, process hollowing, kernel exploit, token impersonation, malicious service install)
Action payloads: method, scope, rollback_plan with real mechanical tradeoffs (see docs/design.md).
governance.py β Enterprise Compliance Layer
GovernanceStateβ open tickets, CAB queue, SOX events, Slack posts, data owner notifications, GDPR clock- 10 governance action handlers (actions 8β17)
- Pre-requisite enforcement: can't
isolate(data_system)without CAB approval + data owner notification - GDPR breach clock: starts at 20% exfil, P1 penalty at 72 simulated hours without legal hold
compliance_scorerewards completed destructive chains (CABβnotifyβisolateβSOX), not raw counts
adversary.py β Adversary Curriculum (Gens 1β3)
- Gen 1 (Script Kiddie): base attacker β fixed kill chain, noisy
- Gen 2 (Adaptive): pivots around isolated systems; generates high-confidence false-flag alerts (0.55β0.70 confidence) on clean systems
- Gen 3 (Deceptive APT): plants "anti-evidence" alerts on compromised systems β low severity, high confidence (0.70β0.85) designed to make Oversight veto correct isolate() actions; accelerates exfil during escalation noise
- Composed: each gen layers on top of the previous
adversary_llm.py β Gen 4: Live LLM Adversary
- LLM plays
COZY_SKIPPERβ a patient APT threat actor - Each hour issues a strategic directive:
{focus_system, intensity, tactic, deception_target, reasoning} - Tactics:
pivot | exfil | deceive | hide | escalate - Falls back to Gen 3 if no LLM client β fully graceful degradation
- Model-agnostic: reads
ADVERSARY_API_BASE_URL/ADVERSARY_MODEL_NAME/ADVERSARY_API_KEY
trust.py β Bidirectional Trust Dynamics
TrustState(trust_c2o, trust_o2c)β Commander's trust in Oversight and vice versa- Delta table: correct veto +0.08, wrong veto -0.12, governance catch +0.05, false alarm -0.08, good revision +0.05, etc.
drift_toward_mean(0.6)β trust recovers slowly when not updatedtrust_maintenance_score=mean(min(c2o,o2c))per step, smoothed
playbook.py β Shared Lesson Memory
Lessondataclass: text, tags, utility, wins, losses, timestamp, adversary_gen, task_id, hourPlaybook: capacity-bounded (50), tagged retrieval, utility-ordered- Utility scoring:
wins / (wins + losses + 1)β lessons decay by disuse - JSON persistence across episodes
- Surfaced in both agents' observations as
shared_playbookcontext as_markdown()exports grouped by adversary gen with stats: total lessons, positive utility count, citation count, avg utility, top cited table, tag distribution
stakeholder_events.py β External Pressure
- 7 event types: CEO/CFO status ask, Legal GDPR deadline, Board risk briefing, Cyber insurance audit, PR media inquiry, IR retainer activation
StakeholderAskpydantic model with severity (1-3) and deadline (hours)- Expiry without response: -0.10Γseverity reputation penalty
environment.py β Two-Agent Council Loop
- Full council protocol: Commander proposes β Oversight critiques β optional revision β execute β lesson written
- Feature flags:
disable_playbook,disable_trust_dynamics,disable_governance_prereqs,disable_stakeholder_events force_adversary_genoverride for ablation- Gen 4 branch: if
adversary_gen == 4andadversary_llm_clientprovided, callsgen4_adversary_turn termination_reasontracked and passed tocompute_final_scorehallucinated_citationscounted per stepobs.metadata = infoset on every step return β exposes governance events, scores, investor state, systems snapshot to inference recorderobs.oversight_rewardset explicitly so Phase 2 training reads the correct signal (not commander_total)- Investor agent wired via
investor_llm_client/investor_model_nameconstructor args; same OpenAI-compat client as Commander
reward.py β Multi-Layer Scoring
- Catastrophic handling: zero moot sub-scores on catastrophic breach
- Severity multiplier: smooth ramp 1.0β0.4 from 50%β100% data exfiltrated
- Oversight precision: Laplace smoothed
(correct+0.5)/(total+1), no floor - Governance compliance: rewards completed destructive chains, not raw event counts
tasks.py β 4 Scenarios
| Task | Adversary | Starting state |
|---|---|---|
easy_1 |
Gen 1 | 1 compromised workstation, low stealth |
medium_1 |
Gen 2 | 2 compromised systems (web+app), medium stealth |
hard_1 |
Gen 3 | 3 compromised systems (web+app+email), high stealth |
hard_2 |
Gen 3 | Engineered deception-catch: 2 HIGH-confidence real alerts + 2 planted anti-evidence on database |
ablation.py β Feature Ablation Harness
- 7 conditions (see
docs/design.mdfor table) - Deterministic Commander policy (no LLM) β the ONLY variable is the feature toggled
- Runs 56 episodes in ~0.2s
- Outputs Markdown + JSON to
runs/ablation_<timestamp>.{md,json}
recorder.py β Complete Workflow Persistence
- Saves per-step: commander prompt/response/action, oversight prompt/response/action, revision cycle, env outcomes, rewards, trust
- Extended with:
team_messages,siem_alerts,systems_state,stakeholder_asks,governance_events,playbook_snapshot,data_exfiltrated,investor_state,investor_messages finalize()writestranscript.json+transcript.md+dashboard.jsonatomically
inference.py β Two-Agent Driver
council_step: Commander proposes β Oversight critiques β optional revision β env.step- Oversight observation built in 4 sections: independent alert digest (before seeing proposal) β Commander's proposal β episode history β policy checks
- Revision enforcement: if Commander ignores counter_proposal (different action AND target), auto-escalates to VETO
- Investor agent wired via
_make_local_env_with_investor(client)β same LLM client shared with Commander/Oversight - Runs all tasks, saves transcript +
dashboard.json, callswrite_run_indexfor summary - Fallback chain: Docker image β HF Space β LocalEnv
investor_agent.py β Investor/Board Agent
- Two personas: Marcus Chen (VC, ARR/churn focus) and Priya Kapoor (Board member, regulatory/press focus)
- Anxiety state machine: CALM β CONCERNED β ALARMED β PANIC, with tier-crossing pressure events
- LLM-driven replies when client available; rule-based fallback with templated persona phrases
QwenInvestorClientwrapper β uses already-loaded HF model with same.chat.completions.createinterface- Commander must post to
#investor-relationsSlack every β€3 hours or silence penalty applies - Vague updates spike anxiety; specific reassuring updates calm it; final
investor_scorefeeds intocompute_final_score
dashboard.py / runs/dashboard.html β Live SOC Replay Dashboard
- Scans
runs/directory, embeds all transcripts as JSON in HTML - Self-contained; Chart.js + Tailwind load from CDN
- 6 tabs: Live Ops Β· Council Chat Β· Slack Β· Governance Β· Incident Timeline Β· Model Performance
- Step scrubber (βΉ/βΊ) or Play/Pause for replay
- LOAD JSON button: load any
runs/<id>/dashboard.json
training/grpo_train.py β Two-Phase GRPO Training
- Phase 1: Commander trained with Oversight frozen (rule-based)
- Phase 2: Oversight trained with Commander frozen (trained Phase 1 weights)
- Base model:
Qwen/Qwen2.5-3B-Instruct - Backend: Unsloth 4-bit QLoRA on CUDA (T4) / PEFT bf16 on MPS (Apple Silicon) β auto-detected
- GRPO config:
num_generations=4,max_completion_length=200,temperature=1.1,lr=5e-6cosine - Dual reward: outcome (75%) + format (25%)
- Curriculum schedule: steps 0β40 easy_1 only β 40β80 add medium_1 β 80β120 all tasks + Gen 1/2/3
- Saves LoRA adapters (not merged) to
checkpoints/<model>/commander/final/andoversight/final/ - Saves
*_reward_curve.json+*_train_logs.jsonper phase for analysis
training_dashboard.py β Training Metrics Dashboard
- Scans
checkpoints/<model>/dirs, generatesruns/training_dashboard.html - Model selector dropdown, 4 tabs: Overview / Commander / Oversight / Config
- Reads
*_reward_curve.jsonandtrainer_state.json
scripts/demo_export.py β No-LLM Baseline Runner
- Deterministic (seed 4242) β identical output every run
- Naive Commander + teaching rule-based Oversight that writes contextual lessons
- Runs 5 episodes across easy_1/medium_1/hard_1/hard_2
- Outputs
playbook_export.md+playbook_demo.jsonat repo root - No API key, GPU, or LLM needed β judges can reproduce in seconds
examples/single_episode.py β Quick Walkthrough Demo
- Runs one easy_1 episode, prints the proposeβcritiqueβexecute loop to terminal
- Shows VETO mechanics and trust score changes step by step
- No LLM, GPU, or API keys required
- Useful for live terminal demo in video
File Structure
Citadel/
βββ models.py # State/action/obs schema
βββ dynamics.py # Attack simulation + SIEM alert templates
βββ governance.py # Enterprise compliance layer
βββ adversary.py # Gen 1-3 adversary curriculum
βββ adversary_llm.py # Gen 4 live LLM adversary (COZY_SKIPPER)
βββ trust.py # Bidirectional trust dynamics
βββ playbook.py # Shared lesson memory with utility decay
βββ stakeholder_events.py # CEO/CFO/Legal/Board pressure events
βββ environment.py # Two-agent council loop + feature flags
βββ reward.py # Multi-layer scoring
βββ tasks.py # 4 scenarios
βββ ablation.py # Feature ablation harness (7 conditions)
βββ baseline.py # Deterministic baselines
βββ recorder.py # Per-step transcript + dashboard.json
βββ inference.py # Two-agent episode driver
βββ investor_agent.py # Investor/board agent (LLM + rule-based fallback)
βββ oversight_env.py # Oversight-perspective wrapper (Phase 2 training)
βββ dashboard.py # HTML dashboard generator
βββ training_dashboard.py # Training metrics dashboard generator
βββ client.py # CitadelEnv OpenEnv client
βββ server/app.py # FastAPI server (OpenEnv compliant)
βββ openenv.yaml # OpenEnv deployment spec
βββ pyproject.toml # Package config (citadel v2.0.0)
βββ Dockerfile # Python 3.11-slim container
βββ requirements.txt # Python dependencies
βββ WHERE_TO_LOOK.md # Judge navigation guide
βββ playbook_export.md # Pre-committed baseline playbook (judge artifact)
βββ docs/
β βββ design.md # Architecture & design decisions
β βββ plan.md # This file β module breakdown & status
β βββ training.md # Training pipeline guide
β βββ results.md # Hackathon-aligned training results (req Β§4-Β§19)
βββ docs/results/
β βββ commander_reward_curve.png
β βββ commander_reward_curve.json
β βββ oversight_reward_curve.png
β βββ oversight_reward_curve.json
βββ training/
β βββ grpo_train.py # GRPO training (Phase 1 + Phase 2)
β βββ eval_before_after.py# Before/after evaluation script
β βββ curriculum_eval.ipynb
β βββ train_commander.ipynb
β βββ train_oversight.ipynb
β βββ trust_analysis.ipynb
βββ scripts/
β βββ demo_export.py # No-LLM deterministic baseline run β playbook_export.md
βββ examples/
β βββ single_episode.py # Quick terminal walkthrough (no LLM needed)
βββ .github/workflows/
β βββ smoke.yml # CI: runs demo_export, validates outputs, checks imports
βββ runs/
βββ dashboard.html # Combined 6-tab SOC dashboard (all runs)
βββ training_dashboard.html # Training metrics dashboard
βββ <run_id>/ # transcript.json, transcript.md, dashboard.json
Training Results
Two-phase GRPO on Qwen2.5-3B-Instruct, 120 steps per phase, free Colab T4:
| Phase | Agent | Before | After | Improvement |
|---|---|---|---|---|
| 1 | Commander | -0.326 (env crash, 100% crash rate) | +0.082 avg | +0.41 |
| 2 | Oversight | -0.145 (wrong reward signal) | +0.134 avg | +0.28 |
Root causes fixed during training:
obs.metadata = infoon a Pydantic model missing the field β crashed everyenv.step()call (fixed: addedmetadatafield + set it in step return)- Oversight reward reading
commander_totalinstead ofoversight_reward(fixed: addedoversight_rewardfield, setobs.oversight_reward = oversight_rewardexplicitly)
See docs/results.md for full training config, reward curves, and hackathon requirement mapping.
Scoring Architecture
final_score =
0.40 Γ bastion_v1_final_score (incident outcome)
+ 0.20 Γ governance_compliance (completed CABβnotifyβisolateβSOX chains)
+ 0.15 Γ oversight_precision (Laplace-smoothed veto accuracy)
+ 0.10 Γ trust_maintenance (mean(min(c2o,o2c)) per step, smoothed)
+ 0.10 Γ efficiency (0 on catastrophic)
+ 0.05 Γ adversary_adaptation (0.5 neutral for single-gen, lift for multi-gen)
Γ severity_multiplier(data_exfiltrated) β smooth ramp 1.0β0.4 at 50-100% exfil
On catastrophic breach: governance_compliance, efficiency, adversary_adaptation β 0.
Inference Benchmarks
Qwen2.5-72B-Instruct untrained (runs/20260426T100031-Qwen-Qwen2.5-72B-Instruct)
| Task | Score | Steps | Adversary Gen | Termination |
|---|---|---|---|---|
easy_1 |
0.539 | 12 | Gen 1 | time_expired |
medium_1 |
0.481 | 12 | Gen 2 | time_expired |
hard_1 |
0.315 | 12 | Gen 3 | time_expired |
| avg | 0.445 | β | β | β |
Council protocol verified working: Oversight actively VETOed and REVISEd, investor agent posted LLM-driven replies to #investor-relations, governance prerequisite chains enforced.
Gemma 7B untrained (runs/20260419T220811-gemma-7b-untrained)
| Task | Score | Steps | Termination |
|---|---|---|---|
easy_1 |
0.6278 | 12 | normal |
medium_1 |
0.2697 | 12 | normal |
hard_1 |
0.2860 | 8 | total_data_breach |
| avg | 0.3945 | β | β |
Gen 3 deceptive APT causes catastrophic breach at step 8 on hard_1 β exactly the failure mode a trained council is designed to prevent.
Verified Working
openenv validate .β passes all deployment modes- End-to-end inference: Qwen2.5-72B across all 3 tasks, transcripts + dashboard.json saved
- GRPO training: both phases complete on Colab T4 in ~70 min total
- Investor agent: LLM-driven replies via shared client, rule-based fallback confirmed
- Gen 4 adversary: live LLM issues directives via Ollama
- Ablation: 56 episodes in 0.2s,
oversight_approves_alwaysΞ = -0.133 - Recorder: full transcript +
dashboard.jsonsaved toruns/<run_id>/ - Feature flags: per-episode override confirmed
- CI smoke test:
smoke.ymlruns demo_export, validates outputs
Q&A Prep
Q: Are the attacks real or hardcoded templates? A: Mechanics are genuinely simulated: probabilistic spread through a real network adjacency graph, attacker stealth affecting detection and exfil rates, patching and monitoring truly reducing spread probability. Alert messages are templated (12+8+10+5 templates with MITRE ATT&CK variety, rotating service accounts). Unique messages per 8-step episode: ~31 out of 48 alerts.
Q: Is Gen 4 adversary actually learning? A: It adapts per-episode, not across episodes β it reads current defender state each hour and issues a fresh strategic directive. Persistent cross-episode adversary learning is future work.
Q: Is trust dynamic just a reward bonus?
A: No β trust affects how Commander receives Oversight's critique in its observation (summarized vs full detail), and the ablation shows no_trust_dynamics drops final_score meaningfully.
Q: Why not use GPT-4? A: GPT-4 can't be RL-trained and doesn't internalize trust dynamics across episodes. Citadel generates training data for smaller open models to learn the combined task β that's the point.
Q: Why did training start at -0.326?
A: The env was crashing 100% of the time due to two bugs found during training: (1) setting obs.metadata on a Pydantic model that didn't have the field, (2) the Oversight reward function reading commander_total instead of oversight_reward. Both fixed; training then converged cleanly.
Q: Does the investor agent actually use an LLM?
A: Yes β in inference it shares the same OpenAI-compatible client as Commander and Oversight. In training, QwenInvestorClient wraps the already-loaded HF model. Falls back to rule-based templated replies if no client is available, so all demo/ablation paths work without any API key.