#!/usr/bin/env python3 """Build the AegisOps AI hackathon slide deck PDF. Generates a clean, dark-themed, 16:9 PDF covering every lablab.ai judging axis: problem -> solution -> 4-agent architecture -> AMD/ROCm proof -> demo flow -> business value -> originality -> roadmap -> ask. Reproducible from source so the deck always matches the README and code. Usage: python scripts/build_slides.py # writes docs/AegisOps_AI_Slides.pdf """ from __future__ import annotations import json from datetime import datetime, timezone from pathlib import Path from typing import Sequence from reportlab.lib.colors import HexColor from reportlab.lib.units import inch from reportlab.pdfgen import canvas from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont REPO = Path(__file__).resolve().parent.parent ASSETS = REPO / "assets" DOCS = REPO / "docs" OUTPUT = DOCS / "AegisOps_AI_Slides.pdf" # 16:9 at print-friendly resolution PAGE_W, PAGE_H = 13.333 * inch, 7.5 * inch BG = HexColor("#020617") PANEL = HexColor("#0E1223") BORDER = HexColor("#334155") FG = HexColor("#F8FAFC") FG_MUTED = HexColor("#94A3B8") FG_DIM = HexColor("#64748B") PURPLE = HexColor("#8B5CF6") RED = HexColor("#EF4444") BLUE = HexColor("#3B82F6") GREEN = HexColor("#22C55E") AMBER = HexColor("#F59E0B") CYAN = HexColor("#06B6D4") def _try_register_inter() -> tuple[str, str]: """Use Inter if available, otherwise fall back to Helvetica.""" candidates = [ ("Inter", "/usr/share/fonts/truetype/inter/Inter-Regular.ttf", "/usr/share/fonts/truetype/inter/Inter-Bold.ttf"), ("DejaVu", "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"), ] for family, regular, bold in candidates: if Path(regular).exists() and Path(bold).exists(): try: pdfmetrics.registerFont(TTFont(family, regular)) pdfmetrics.registerFont(TTFont(f"{family}-Bold", bold)) return family, f"{family}-Bold" except Exception: continue return "Helvetica", "Helvetica-Bold" REGULAR, BOLD = _try_register_inter() def _draw_background(c: canvas.Canvas) -> None: c.setFillColor(BG) c.rect(0, 0, PAGE_W, PAGE_H, fill=1, stroke=0) def _draw_chrome(c: canvas.Canvas, slide_no: int, total: int, title: str) -> None: # Top brand bar c.setFillColor(PANEL) c.rect(0, PAGE_H - 0.55 * inch, PAGE_W, 0.55 * inch, fill=1, stroke=0) c.setFillColor(FG) c.setFont(BOLD, 13) c.drawString(0.5 * inch, PAGE_H - 0.36 * inch, "AegisOps AI") c.setFillColor(PURPLE) c.setFont(BOLD, 13) c.drawString(0.5 * inch + c.stringWidth("AegisOps ", BOLD, 13), PAGE_H - 0.36 * inch, "AI") c.setFillColor(FG_DIM) c.setFont(REGULAR, 9) c.drawString(1.65 * inch, PAGE_H - 0.36 * inch, "MITRE TO DETECTION COPILOT · AMD DEVELOPER HACKATHON 2026") c.setFillColor(FG_DIM) c.drawRightString(PAGE_W - 0.5 * inch, PAGE_H - 0.36 * inch, f"{slide_no:02d} / {total:02d} · {title}") # Bottom border line c.setStrokeColor(BORDER) c.setLineWidth(0.5) c.line(0.5 * inch, 0.45 * inch, PAGE_W - 0.5 * inch, 0.45 * inch) c.setFillColor(FG_DIM) c.setFont(REGULAR, 8) c.drawString(0.5 * inch, 0.27 * inch, "github.com/ztothez/aegisops-ai") c.drawRightString(PAGE_W - 0.5 * inch, 0.27 * inch, "Track 1 · AI Agents & Agentic Workflows") def _draw_title(c: canvas.Canvas, eyebrow: str, title: str, accent=PURPLE) -> float: c.setFillColor(accent) c.setFont(BOLD, 10) c.drawString(0.55 * inch, PAGE_H - 1.0 * inch, eyebrow.upper()) c.setFillColor(FG) c.setFont(BOLD, 28) c.drawString(0.55 * inch, PAGE_H - 1.45 * inch, title) return PAGE_H - 1.75 * inch # body baseline def _draw_paragraph(c: canvas.Canvas, x: float, y: float, w: float, text: str, size: int = 11, color=FG_MUTED, leading: float = 1.45) -> float: c.setFillColor(color) c.setFont(REGULAR, size) words = text.split() line: list[str] = [] line_w = 0.0 space_w = c.stringWidth(" ", REGULAR, size) line_height = size * leading for word in words: word_w = c.stringWidth(word, REGULAR, size) if line and line_w + space_w + word_w > w: c.drawString(x, y, " ".join(line)) y -= line_height line, line_w = [word], word_w else: line_w = word_w if not line else line_w + space_w + word_w line.append(word) if line: c.drawString(x, y, " ".join(line)) y -= line_height return y def _draw_bullets(c: canvas.Canvas, x: float, y: float, w: float, items: Sequence[str], size: int = 11, accent=PURPLE) -> float: bullet_w = 0.18 * inch for item in items: c.setFillColor(accent) c.setFont(BOLD, size) c.drawString(x, y, "›") next_y = _draw_paragraph(c, x + bullet_w, y, w - bullet_w, item, size=size) y = next_y - 0.06 * inch return y def _draw_pill(c: canvas.Canvas, x: float, y: float, label: str, fg, bg) -> float: pad_x = 0.12 * inch pad_y = 0.06 * inch c.setFont(BOLD, 9) text_w = c.stringWidth(label, BOLD, 9) w = text_w + 2 * pad_x h = 9 + 2 * pad_y c.setFillColor(bg) c.setStrokeColor(fg) c.roundRect(x, y, w, h, 4, fill=1, stroke=1) c.setFillColor(fg) c.drawString(x + pad_x, y + pad_y + 1, label) return x + w + 0.08 * inch def _draw_card(c: canvas.Canvas, x: float, y: float, w: float, h: float, eyebrow: str, lines: Sequence[str], accent=PURPLE) -> None: c.setFillColor(PANEL) c.setStrokeColor(BORDER) c.roundRect(x, y, w, h, 6, fill=1, stroke=1) c.setStrokeColor(accent) c.setLineWidth(2) c.line(x, y + h, x + w, y + h) c.setFillColor(accent) c.setFont(BOLD, 9) c.drawString(x + 0.18 * inch, y + h - 0.32 * inch, eyebrow.upper()) cy = y + h - 0.62 * inch c.setFillColor(FG) c.setFont(REGULAR, 10) for line in lines: c.drawString(x + 0.18 * inch, cy, line) cy -= 0.22 * inch def _slide_cover(c: canvas.Canvas, slide_no: int, total: int) -> None: _draw_background(c) cover = ASSETS / "cover.png" if cover.exists(): c.drawImage(str(cover), 0, 0, width=PAGE_W, height=PAGE_H, preserveAspectRatio=True, mask="auto", anchor="c") # Title overlay band band_h = 1.6 * inch c.setFillColorRGB(0.012, 0.024, 0.090, alpha=0.85) c.rect(0, 0, PAGE_W, band_h, fill=1, stroke=0) c.setFillColor(FG) c.setFont(BOLD, 30) c.drawString(0.6 * inch, band_h - 0.55 * inch, "AegisOps AI") c.setFillColor(PURPLE) c.setFont(BOLD, 30) c.drawString(0.6 * inch + c.stringWidth("AegisOps ", BOLD, 30), band_h - 0.55 * inch, "") c.setFillColor(FG_MUTED) c.setFont(REGULAR, 12) c.drawString(0.6 * inch, band_h - 0.85 * inch, "MITRE to Detection Copilot · 4-Agent Purple Team Pipeline · vLLM on ROCm · AMD MI300X") c.setFillColor(FG_DIM) c.setFont(REGULAR, 10) c.drawString(0.6 * inch, 0.35 * inch, "AMD Developer Hackathon 2026 · Track 1: AI Agents & Agentic Workflows") c.drawRightString(PAGE_W - 0.6 * inch, 0.35 * inch, datetime.now(timezone.utc).strftime("%Y-%m-%d")) c.drawRightString(PAGE_W - 0.6 * inch, band_h - 0.85 * inch, f"{slide_no:02d} / {total:02d}") def _slide_problem(c: canvas.Canvas, slide_no: int, total: int) -> None: _draw_background(c) _draw_chrome(c, slide_no, total, "Problem") y = _draw_title(c, "the gap", "Threat intel doesn't become detection fast enough.", RED) y = _draw_paragraph(c, 0.55 * inch, y, PAGE_W - 1.1 * inch, "Security teams have more MITRE ATT&CK intel than they can operationalize. " "Converting a technique into precise SIEM/EDR detection logic + response playbooks " "needs rare dual offensive/defensive expertise.", size=12) y -= 0.25 * inch _draw_bullets(c, 0.7 * inch, y, PAGE_W - 1.4 * inch, [ "A typical purple-team engagement: $20,000-$50,000 and 2-3 weeks per scenario.", "Sensitive infrastructure data cannot be sent to cloud AI APIs.", "Generic threat intel produces generic, low-precision detection rules.", "Blue teams don't see how red teams actually execute techniques in the wild.", ], size=12, accent=RED) def _slide_solution(c: canvas.Canvas, slide_no: int, total: int) -> None: _draw_background(c) _draw_chrome(c, slide_no, total, "Solution") y = _draw_title(c, "aegisops ai", "High-fidelity simulation -> high-precision defense.", GREEN) y = _draw_paragraph(c, 0.55 * inch, y, PAGE_W - 1.1 * inch, "AegisOps AI is a 4-agent purple-team copilot. Drop in a MITRE ATT&CK technique ID " "(or APT group, kill chain, or sandbox topology) and get back observables, Sigma rules, " "realtime SIEM/EDR alert logic, and a SOC response playbook in minutes.", size=12) y -= 0.2 * inch # Pipeline cards row card_w = (PAGE_W - 1.5 * inch) / 4 card_h = 1.6 * inch cy = 1.0 * inch cards = [ ("Red / Threat", "Authorized high-fidelity simulation, observables, telemetry, exploit code patterns.", RED), ("Detection / Blue", "Sigma rule + Real-Time Detection Plan grounded in the exact red artifacts.", BLUE), ("Response", "Triage, containment, hunt, mitigation, escalation, reporting - mapped to telemetry.", GREEN), ("Validation", "Coverage score, covered/missing observables, scope check, improvement suggestions.", PURPLE), ] for i, (title, desc, color) in enumerate(cards): x = 0.55 * inch + i * (card_w + 0.12 * inch) _draw_card(c, x, cy, card_w, card_h, title, [], accent=color) # body body_w = card_w - 0.36 * inch _draw_paragraph(c, x + 0.18 * inch, cy + card_h - 0.55 * inch, body_w, desc, size=10) def _slide_architecture(c: canvas.Canvas, slide_no: int, total: int) -> None: _draw_background(c) _draw_chrome(c, slide_no, total, "Architecture") y = _draw_title(c, "stack", "LangGraph 4-agent state machine on vLLM + ROCm + MI300X.", BLUE) y -= 0.05 * inch # Two-column: left text, right ascii-style stack text_x = 0.55 * inch text_w = (PAGE_W - 1.1 * inch) * 0.5 - 0.2 * inch y2 = _draw_bullets(c, text_x, y, text_w, [ "Streamlit UI with 4 modes: Single Technique, APT Group, Kill Chain, Topology Lab.", "LangGraph: stateful directed pipeline (Red -> Blue -> Response -> Validation).", "MITRE ATT&CK v14 enterprise-attack.json shipped locally - no external API call for threat intel.", "OpenAI-compatible client -> vLLM OpenAI server -> ROCm container -> AMD Instinct MI300X.", "Per-agent latency + token usage instrumented and rendered live in the UI.", ], size=11, accent=BLUE) # Right: stack visual sx = text_x + text_w + 0.4 * inch sw = (PAGE_W - 1.1 * inch) * 0.5 - 0.2 * inch layers = [ ("Streamlit UI", FG, "#0E1223"), ("LangGraph 4-agent pipeline", PURPLE, "#1A1230"), ("ChatOpenAI (langchain-openai)", BLUE, "#0F1B30"), ("vLLM OpenAI API server", AMBER, "#2A1A0A"), ("ROCm container (AMD)", RED, "#2A0F0F"), ("AMD Instinct MI300X (192GB HBM3)", GREEN, "#0F2A18"), ] layer_h = 0.45 * inch sy = PAGE_H - 1.9 * inch for label, fg, bg_hex in layers: c.setFillColor(HexColor(bg_hex)) c.setStrokeColor(fg) c.roundRect(sx, sy, sw, layer_h, 5, fill=1, stroke=1) c.setFillColor(fg) c.setFont(BOLD, 11) c.drawString(sx + 0.15 * inch, sy + layer_h / 2 - 4, label) sy -= layer_h + 0.08 * inch def _slide_amd_proof(c: canvas.Canvas, slide_no: int, total: int) -> None: _draw_background(c) _draw_chrome(c, slide_no, total, "AMD MI300X · ROCm Proof") y = _draw_title(c, "verifiable amd evidence", "Live vLLM on ROCm. Every claim is in the repo.", AMBER) bench_path = ASSETS / "rocm_benchmark.json" smi_path = ASSETS / "rocm_smi.json" bench_lines = ["Benchmark not captured yet - run scripts/rocm_benchmark.py on the MI300X."] if bench_path.exists(): try: data = json.loads(bench_path.read_text()) bench_lines = [ f"endpoint: {data.get('endpoint','-')}", f"model: {data.get('model','-')}", f"runtime: {data.get('runtime','-')}", f"gpu: {data.get('gpu','-')}", f"requests: {data.get('successful',0)}/{data.get('requests',0)} (concurrency {data.get('concurrency','-')})", f"p50: {data.get('latency_ms_p50','-')} ms p95: {data.get('latency_ms_p95','-')} ms", f"throughput: {data.get('tokens_per_second','-')} tokens/sec", f"captured: {data.get('captured_at','-')}", ] except Exception: pass smi_state = "captured" if smi_path.exists() else "pending - run start_vllm.sh on the MI300X" _draw_card(c, 0.55 * inch, 1.0 * inch, (PAGE_W - 1.4 * inch) / 2, 4.4 * inch, "rocm_benchmark.json (live)", bench_lines, accent=AMBER) _draw_card(c, 0.55 * inch + (PAGE_W - 1.4 * inch) / 2 + 0.3 * inch, 1.0 * inch, (PAGE_W - 1.4 * inch) / 2, 4.4 * inch, "rocm_smi.json + vllm_info.txt", [ f"rocm_smi.json: {smi_state}", "Captured by ./start_vllm.sh from the live ROCm container", "vllm_info.txt: vLLM version, model id, endpoint, capture timestamp", "All files committed in /assets/ for reproducible judging", "Streamlit UI links to these files from the ROCm Live panel", ], accent=GREEN) def _slide_demo_flow(c: canvas.Canvas, slide_no: int, total: int) -> None: _draw_background(c) _draw_chrome(c, slide_no, total, "Demo Flow") y = _draw_title(c, "live demo arc - under 5 minutes", "Realistic ATT&CK behavior becomes precise detection.", CYAN) _draw_bullets(c, 0.7 * inch, y, PAGE_W - 1.4 * inch, [ "Open AegisOps AI. Top panel shows LIVE - vLLM on ROCm | MI300X with /v1/models latency.", "Run Single Technique with T1059.001 (PowerShell). Show per-agent latency + token cards.", "Inspect Red/Threat output: simulation phases, exploit code section, observables, telemetry.", "Inspect Detection output: Sigma YAML + Real-Time Detection Plan grounded in those observables.", "Inspect Response + Validation: triage, containment, coverage score, covered/missing observables.", "Switch to Topology Lab: 9-node sandbox, 3 lateral-movement paths, hop-by-hop reaction time.", ], size=12, accent=CYAN) def _slide_business_value(c: canvas.Canvas, slide_no: int, total: int) -> None: _draw_background(c) _draw_chrome(c, slide_no, total, "Business Value") y = _draw_title(c, "market & roi", "Replace 2-3 weeks of purple-team work with minutes of inference.", GREEN) # Two-column grid of cards cw = (PAGE_W - 1.4 * inch) / 2 ch = 1.7 * inch gap = 0.2 * inch # left col _draw_card(c, 0.55 * inch, PAGE_H - 4.0 * inch, cw, ch, "ROI per scenario", [ "Today: $20,000-$50,000 / 2-3 weeks / 2-3 senior consultants", "AegisOps AI: minutes per technique, one operator, no cloud dependency", "Each Sigma rule + response playbook is exportable to PDF for SOC handoff", ], accent=GREEN) _draw_card(c, 0.55 * inch + cw + gap, PAGE_H - 4.0 * inch, cw, ch, "Revenue model", [ "SaaS: $500-$2,000 / month per SOC team", "On-prem AMD GPU deployment for data-sovereignty buyers (banks, gov, MSSP)", "Add-on: detection-pack subscription per ATT&CK update wave", ], accent=PURPLE) _draw_card(c, 0.55 * inch, PAGE_H - 4.0 * inch - ch - gap, cw, ch, "Market & TAM", [ "Penetration testing: $1.7B (2023), growing 13% annually", "Purple teaming = fastest-growing segment (continuous validation)", "TAM (MSSPs + Enterprise SOC needing on-prem AI): ~$340M", ], accent=AMBER) _draw_card(c, 0.55 * inch + cw + gap, PAGE_H - 4.0 * inch - ch - gap, cw, ch, "Customers", [ "MSSPs running purple-team exercises across many clients", "Enterprise SOC teams without dual red/blue expertise", "Detection engineering teams automating Sigma generation", "Red-team consultancies productizing repeatable reports", ], accent=BLUE) def _slide_originality(c: canvas.Canvas, slide_no: int, total: int) -> None: _draw_background(c) _draw_chrome(c, slide_no, total, "Originality") y = _draw_title(c, "what makes this different", "Not a chatbot. Not a wrapper. A purple-team workflow engine.", PURPLE) _draw_bullets(c, 0.7 * inch, y, PAGE_W - 1.4 * inch, [ "4-agent stateful pipeline (Red -> Blue -> Response -> Validation) with structured outputs at every hop.", "Topology Lab: sandbox lateral-movement visualization mapped hop-by-hop to detection + reaction time.", "Realtime Detection Plan: each technique produces streaming SIEM/EDR alert logic, not just a static rule.", "On-prem AMD/ROCm path: sensitive infra context never leaves operator-controlled MI300X.", "Validation Agent enforces scope: zero-day generation explicitly OUT, known ATT&CK behavior IN.", "Per-agent live latency + token observability surfaced directly in the UI - judges see ROCm working.", ], size=12, accent=PURPLE) def _slide_safety(c: canvas.Canvas, slide_no: int, total: int) -> None: _draw_background(c) _draw_chrome(c, slide_no, total, "Safety & Scope") y = _draw_title(c, "responsible offensive ai", "High fidelity, professionally bounded.", AMBER) cw = (PAGE_W - 1.4 * inch) / 2 ch = 3.2 * inch _draw_card(c, 0.55 * inch, 1.0 * inch, cw, ch, "In scope", [ "Known MITRE ATT&CK behavior simulation", "Detection-useful command patterns with placeholders", "Sigma + Real-Time Detection Plans", "Response, hunt, containment guidance", "Validation scoring + coverage analysis", ], accent=GREEN) _draw_card(c, 0.55 * inch + cw + 0.3 * inch, 1.0 * inch, cw, ch, "Out of scope", [ "Zero-day exploit generation", "Novel malware authoring", "Real target exploitation instructions", "Unbounded offensive automation", "Live engagement against unauthorized targets", ], accent=RED) def _slide_roadmap(c: canvas.Canvas, slide_no: int, total: int) -> None: _draw_background(c) _draw_chrome(c, slide_no, total, "Roadmap") y = _draw_title(c, "next 90 days", "From hackathon prototype to MSSP-ready product.", BLUE) _draw_bullets(c, 0.7 * inch, y, PAGE_W - 1.4 * inch, [ "Multi-model routing on AMD - Qwen for reasoning, Llama for generation, served from one ROCm host.", "Direct Sigma deployment to Splunk, Elastic, Microsoft Sentinel.", "Domain fine-tuned detection model trained on MITRE + Sigma corpus on MI300X.", "SOC handoff bundle: ZIP of Sigma rules, MITRE CSV, executive summary, PDF report.", "ATT&CK coverage heatmap visualizing tactic/technique gaps across customer estate.", "Continuous validation runner: scheduled re-runs as ATT&CK and detection rules drift.", ], size=12, accent=BLUE) def _slide_ask(c: canvas.Canvas, slide_no: int, total: int) -> None: _draw_background(c) _draw_chrome(c, slide_no, total, "Ask") y = _draw_title(c, "judging axes", "Built to score 5/5 on every criterion.", PURPLE) cw = (PAGE_W - 1.5 * inch) / 4 ch = 2.4 * inch cy = PAGE_H - 4.5 * inch cards = [ ("Presentation", ["Cover image (16:9)", "Slide deck PDF (this)", "Sub-5-min video script", "Live demo URL"]), ("Business Value", ["$1.7B market, 13% CAGR", "Clear SaaS + on-prem revenue", "MSSP + Enterprise SOC ICP", "Replaces $20-50K work"]), ("Application of Tech", ["Live vLLM on ROCm MI300X", "rocm-smi + benchmark JSON", "Per-agent latency in UI", "LangGraph + Llama 3.3 70B"]), ("Originality", ["4-agent purple-team flow", "Topology Lab visualization", "Realtime Detection Plan", "On-prem AMD/ROCm story"]), ] accents = [CYAN, GREEN, AMBER, PURPLE] for i, ((title, lines), accent) in enumerate(zip(cards, accents)): x = 0.55 * inch + i * (cw + 0.12 * inch) _draw_card(c, x, cy, cw, ch, title, lines, accent=accent) # Bottom CTA c.setFillColor(FG) c.setFont(BOLD, 14) c.drawCentredString(PAGE_W / 2, 0.85 * inch, "Try it live - link in submission · github.com/ztothez/aegisops-ai") def build() -> Path: DOCS.mkdir(parents=True, exist_ok=True) slides = [ _slide_cover, _slide_problem, _slide_solution, _slide_architecture, _slide_amd_proof, _slide_demo_flow, _slide_business_value, _slide_originality, _slide_safety, _slide_roadmap, _slide_ask, ] total = len(slides) c = canvas.Canvas(str(OUTPUT), pagesize=(PAGE_W, PAGE_H)) c.setTitle("AegisOps AI - AMD Developer Hackathon 2026") c.setAuthor("AegisOps AI") c.setSubject("Multi-agent purple-team copilot on AMD MI300X via vLLM + ROCm") for i, slide in enumerate(slides, start=1): slide(c, i, total) c.showPage() c.save() print(f"Wrote {OUTPUT} ({total} slides, 16:9)") return OUTPUT if __name__ == "__main__": build()