Spaces:
Sleeping
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 `ActionType` enum (10 incident response + 8 governance) | |
| - `IncidentAction` with `justification`, `cited_lessons`, `method`, `scope`, `rollback_plan`, governance args | |
| - `CommanderProposal` pydantic model | |
| - `OversightDecision` enum: APPROVE / REVISE / VETO / FLAG_FOR_HUMAN | |
| - `OversightAction` with structured critique: `{risk_tier, weakness, missing_evidence, counter_proposal}` | |
| - `CouncilState` + `ProposalRecord` tracking all council history | |
| - `IncidentState` includes `governance_state`, `trust_state`, `council_state`, `stakeholder_state`, `adversary_gen` | |
| - `IncidentObservation` includes 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_score` rewards **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 updated | |
| - `trust_maintenance_score` = `mean(min(c2o,o2c))` per step, smoothed | |
| ### `playbook.py` β Shared Lesson Memory | |
| - `Lesson` dataclass: text, tags, utility, wins, losses, timestamp, adversary_gen, task_id, hour | |
| - `Playbook`: 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_playbook` context | |
| - `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 | |
| - `StakeholderAsk` pydantic 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_gen` override for ablation | |
| - Gen 4 branch: if `adversary_gen == 4` and `adversary_llm_client` provided, calls `gen4_adversary_turn` | |
| - `termination_reason` tracked and passed to `compute_final_score` | |
| - `hallucinated_citations` counted per step | |
| - `obs.metadata = info` set on every step return β exposes governance events, scores, investor state, systems snapshot to inference recorder | |
| - `obs.oversight_reward` set explicitly so Phase 2 training reads the correct signal (not commander_total) | |
| - Investor agent wired via `investor_llm_client` / `investor_model_name` constructor 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.md` for 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()` writes `transcript.json` + `transcript.md` + `dashboard.json` atomically | |
| ### `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`, calls `write_run_index` for 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 | |
| - `QwenInvestorClient` wrapper β uses already-loaded HF model with same `.chat.completions.create` interface | |
| - Commander must post to `#investor-relations` Slack every β€3 hours or silence penalty applies | |
| - Vague updates spike anxiety; specific reassuring updates calm it; final `investor_score` feeds into `compute_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-6` cosine | |
| - 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/` and `oversight/final/` | |
| - Saves `*_reward_curve.json` + `*_train_logs.json` per phase for analysis | |
| ### `training_dashboard.py` β Training Metrics Dashboard | |
| - Scans `checkpoints/<model>/` dirs, generates `runs/training_dashboard.html` | |
| - Model selector dropdown, 4 tabs: Overview / Commander / Oversight / Config | |
| - Reads `*_reward_curve.json` and `trainer_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.json` at 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: | |
| 1. `obs.metadata = info` on a Pydantic model missing the field β crashed every `env.step()` call (fixed: added `metadata` field + set it in step return) | |
| 2. Oversight reward reading `commander_total` instead of `oversight_reward` (fixed: added `oversight_reward` field, set `obs.oversight_reward = oversight_reward` explicitly) | |
| See [docs/results.md](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.json` saved to `runs/<run_id>/` | |
| - Feature flags: per-episode override confirmed | |
| - CI smoke test: `smoke.yml` runs 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. | |