File size: 14,920 Bytes
c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 0cb4cc5 c4f5819 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | <!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ROGLAG · Tech Stack</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans+Thai:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
:root{--bg:#0b0f17;--panel:#121826;--panel2:#0f1420;--line:#1f2a3d;--txt:#e6edf6;
--muted:#aab8cc;--accent:#4da3ff;--accent2:#a78bfa;--teal:#5eead4;--ok:#34d399;--warn:#fbbf24;--crit:#f87171;--code:#0a0e16;
--hair:rgba(148,163,184,0.18);}
*{box-sizing:border-box}html{scroll-behavior:smooth}
body{margin:0;background-color:#0b0f17;background:radial-gradient(1200px 600px at 80% -10%,#15203a 0%,var(--bg) 55%);
color:var(--txt);font-family:"IBM Plex Sans Thai",-apple-system,"Segoe UI",sans-serif;line-height:1.65;font-size:16px}
.wrap{max-width:1060px;margin:0 auto;padding:32px 22px 96px}
header.hero{border:1px solid var(--line);border-radius:18px;padding:26px 30px;
background:linear-gradient(135deg,#13203a,#0f1420);margin-bottom:14px}
.hero h1{margin:0 0 6px;font-size:32px}.hero p{margin:0;color:var(--muted)}
.chips{display:flex;gap:8px;flex-wrap:wrap;margin-top:14px}
.chip{font-size:13px;padding:5px 11px;border:1px solid var(--line);border-radius:999px;background:#16203250;color:var(--teal)}
nav.toc{position:sticky;top:0;z-index:5;backdrop-filter:blur(8px);background:#0b0f17cc;
border:1px solid var(--line);border-radius:14px;padding:12px 16px;margin:18px 0 26px;display:flex;gap:14px;flex-wrap:wrap}
nav.toc a{color:var(--muted);text-decoration:none;font-size:13px}nav.toc a:hover{color:var(--accent)}
section{border:1px solid var(--line);border-radius:16px;padding:24px 26px;background:var(--panel);margin-bottom:22px}
h2{font-size:22px;margin:0 0 10px;display:flex;align-items:center;gap:10px}
h3{font-size:16px;margin:20px 0 8px;color:var(--accent)}
.sub{color:var(--muted);margin:2px 0 16px;font-size:13px}
small{font-size:12px;color:var(--muted)}
code{background:var(--code);border:1px solid var(--line);border-radius:6px;padding:1px 6px;font-size:13px;color:#cfe3ff;
font-family:"IBM Plex Mono",ui-monospace,monospace}
table{width:100%;border-collapse:collapse;margin:12px 0;font-size:13px}
th,td{border:1px solid var(--line);padding:9px 12px;text-align:left;vertical-align:top}
th{background:var(--panel2);color:var(--accent)}
td b{color:#fff}
.ver{color:var(--teal);font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:13px}
.flow{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:14px 0;font-size:13px}
.node{border:1px solid var(--line);background:var(--panel2);border-radius:10px;padding:8px 12px}
.arrow{color:var(--accent2);font-weight:700}
.callout{border:1px solid var(--hair);background:#0e1a2e;border-radius:10px;padding:12px 16px;margin:14px 0;font-size:13px}
.callout.warn{background:#241d0e}
.tag{display:inline-block;font-size:13px;padding:2px 8px;border-radius:999px;margin-left:6px}
.t-now{background:#0e2a1e;color:var(--ok);border:1px solid #1c4b36}
.t-prod{background:#241a2e;color:#c4a3ff;border:1px solid #3d2c4b}
ul{margin:8px 0 8px 4px;padding-left:20px}li{margin:4px 0}
footer{color:var(--muted);text-align:center;font-size:13px;margin-top:30px}
</style>
</head>
<body>
<div class="wrap">
<header class="hero">
<h1>🧱 ROGLAG · Tech Stack</h1>
<p>สแตกจริงของระบบ Security Log RAG + Realtime Threat Console</p>
<div class="chips">
<span class="chip">Python 3.12</span><span class="chip">stdlib http.server</span>
<span class="chip">Vanilla JS</span><span class="chip">Three.js</span>
<span class="chip">Rule + RAG</span><span class="chip">FastAPI</span>
<span class="chip">SQLAlchemy</span><span class="chip">Docker</span><span class="chip">OpenRouter</span>
</div>
<p style="margin-top:14px;font-size:13px;color:var(--muted)">
<span class="tag t-now">NOW</span> ที่รันอยู่จริงในเดโม่ ·
<span class="tag t-prod">PROD</span> สแตกระดับ production ที่อยู่ใน repo เดียวกัน
</p>
</header>
<nav class="toc">
<a href="#arch">สถาปัตยกรรม</a><a href="#fe">Frontend</a><a href="#be">Backend</a>
<a href="#detect">Detection & RAG</a><a href="#data">Data & Generator</a>
<a href="#prod">Production stack</a><a href="#ext">External</a><a href="#full">สรุปทั้งหมด</a>
</nav>
<section id="arch">
<h2>ภาพรวมสถาปัตยกรรม</h2>
<p class="sub">เดโม่ปัจจุบันเน้น zero-dependency รันได้ทันที, แต่ใน repo มีสแตก production พร้อมไปต่อ</p>
<div class="flow">
<span class="node">Log sources<br><small>auth/nginx/fw/system</small></span><span class="arrow">→</span>
<span class="node">Generator / CSV<br><small>ingest</small></span><span class="arrow">→</span>
<span class="node">Rule classifier<br><small>detect_rule</small></span><span class="arrow">→</span>
<span class="node">In-memory index<br><small>TF-IDF + vector</small></span><span class="arrow">→</span>
<span class="node">HTTP/JSON API</span><span class="arrow">→</span>
<span class="node">Dashboard<br><small>vanilla JS</small></span>
</div>
<div class="callout">
เดโม่ใช้ <b>Python stdlib ล้วน</b> (ไม่ต้องลง dependency) เก็บ log ใน memory + ตอบ JSON API; ส่วน production ใช้ FastAPI + SQLAlchemy + Docker (หัวข้อ 6)
</div>
</section>
<section id="fe">
<h2>Frontend <span class="tag t-now">NOW</span></h2>
<p class="sub">หน้าเดียว ไม่มี build step: โหลดเร็ว แก้ง่าย</p>
<table>
<tr><th>ส่วน</th><th>ใช้</th><th>หมายเหตุ</th></tr>
<tr><td><b>Markup/Logic</b></td><td>HTML5 + Vanilla JavaScript (ES2020+)</td><td>ไฟล์เดียว <code>web/index.html</code> ไม่มี framework/bundler</td></tr>
<tr><td><b>3D Globe</b></td><td>Three.js <span class="ver">0.160.0</span> (CDN)</td><td>WebGL globe + arcs + particles ในหน้า Overview</td></tr>
<tr><td><b>Charts</b></td><td>SVG เขียนเอง</td><td>Threat activity graph (เส้น risk + bar severity) ไม่ใช้ chart lib</td></tr>
<tr><td><b>Realtime monitor</b></td><td>Polling (fetch + setInterval)</td><td>tail-f log monitor, sort by time, ดึง events จาก ingest API</td></tr>
<tr><td><b>Styling</b></td><td>CSS เขียนเอง (custom properties)</td><td>dark theme, ไม่มี Tailwind/Bootstrap</td></tr>
<tr><td><b>Fonts</b></td><td>IBM Plex Mono + system fonts</td><td>โทน console/SOC</td></tr>
</table>
</section>
<section id="be">
<h2>Backend เดโม่ <span class="tag t-now">NOW</span></h2>
<p class="sub"><code>server.py</code>: stdlib ล้วน ไม่มี dependency ภายนอก</p>
<table>
<tr><th>ส่วน</th><th>ใช้</th><th>หมายเหตุ</th></tr>
<tr><td><b>Runtime</b></td><td>Python <span class="ver">3.12</span> (รองรับ 3.10+)</td><td>ใช้ type hints แบบ PEP 604</td></tr>
<tr><td><b>HTTP server</b></td><td><code>http.server.ThreadingHTTPServer</code></td><td>stdlib, multithread, ไม่มี framework</td></tr>
<tr><td><b>Storage</b></td><td>In-memory (Python lists/dicts)</td><td>โหลด JSONL เข้า RAM ตอน start + append สดผ่าน <code>add_rows()</code></td></tr>
<tr><td><b>Vector store</b></td><td>SQLite <span class="ver">(stdlib sqlite3)</span></td><td><code>lograg_vectors.sqlite</code> เก็บ float32 embedding</td></tr>
<tr><td><b>HTTP client</b></td><td><code>urllib.request</code></td><td>เรียก OpenRouter โดยไม่ต้องลง requests</td></tr>
</table>
<h3>API endpoints</h3>
<table>
<tr><th>Method</th><th>Path</th><th>หน้าที่</th></tr>
<tr><td>GET</td><td><code>/api/health · /api/stats · /api/logs · /api/alerts</code></td><td>dashboard data</td></tr>
<tr><td>POST</td><td><code>/api/search · /api/ask</code></td><td>RAG retrieval + Q&A</td></tr>
<tr><td>POST</td><td><code>/api/mock/emit</code></td><td>ยิง mock log realtime (คืน events)</td></tr>
<tr><td>POST</td><td><code>/api/ingest/csv</code></td><td>อัปโหลด CSV → classify</td></tr>
<tr><td>GET</td><td><code>/api/generate/csv</code></td><td>สร้าง CSV ใหม่ (unseeded)</td></tr>
</table>
</section>
<section id="detect">
<h2>Detection & RAG <span class="tag t-now">NOW</span></h2>
<table>
<tr><th>ส่วน</th><th>ใช้</th><th>หมายเหตุ</th></tr>
<tr><td><b>Classifier</b></td><td>Rule-based (regex patterns)</td><td><code>detect_rule()</code> → 7 categories + MITRE ATT&CK + risk score, ทำงานออฟไลน์</td></tr>
<tr><td><b>Keyword retrieval</b></td><td>TF-IDF เขียนเอง (postings + idf)</td><td>query expansion ไทย/อังกฤษ, ไม่ใช้ search engine</td></tr>
<tr><td><b>Vector retrieval</b></td><td>cosine similarity (SQLite blobs)</td><td>optional, ใช้เมื่อมี index + API key</td></tr>
<tr><td><b>Embeddings</b></td><td>OpenRouter <span class="ver">nvidia/llama-nemotron-embed</span></td><td>optional; fallback keyword เสมอ</td></tr>
<tr><td><b>LLM answer</b></td><td>OpenRouter (<span class="ver">openrouter/free</span>)</td><td>optional; มี local rule-based summary สำรอง</td></tr>
</table>
<div class="callout warn">ทุกชั้นมี fallback: vector → keyword → rule-based local answer → ระบบไม่ล่มแม้ไม่มี API key</div>
</section>
<section id="data">
<h2>Data & Generator <span class="tag t-now">NOW</span></h2>
<table>
<tr><th>ส่วน</th><th>ใช้</th><th>หมายเหตุ</th></tr>
<tr><td><b>Dataset</b></td><td>JSONL (synthetic security logs)</td><td>10k / 50k records, 7 categories</td></tr>
<tr><td><b>Generator</b></td><td>Python (<code>app/generator.py</code>)</td><td>สุ่มไม่ซ้ำทุกครั้ง (unseeded), profile: mixed/attack/realistic</td></tr>
<tr><td><b>CSV I/O</b></td><td><code>csv</code> stdlib</td><td>generate + ingest ผ่าน DictReader/DictWriter</td></tr>
<tr><td><b>Categories</b></td><td>brute_force, port_scan, web_attack, priv_esc, data_exfil, c2_beacon, benign</td><td>map → MITRE + runbook</td></tr>
</table>
</section>
<section id="prod">
<h2>Production stack <span class="tag t-prod">PROD</span></h2>
<p class="sub">อยู่ใน repo เดียวกัน (<code>app/</code> + Docker) สำหรับ deploy จริง / self-host เต็มรูปแบบ</p>
<table>
<tr><th>ชั้น</th><th>ใช้</th><th>เวอร์ชัน</th></tr>
<tr><td><b>Web framework</b></td><td>FastAPI</td><td class="ver">0.115.6</td></tr>
<tr><td><b>ASGI server</b></td><td>Uvicorn (standard)</td><td class="ver">0.34.0</td></tr>
<tr><td><b>ORM / DB toolkit</b></td><td>SQLAlchemy (async)</td><td class="ver">2.0.36</td></tr>
<tr><td><b>DB driver</b></td><td>asyncpg (Postgres) / aiosqlite (SQLite)</td><td class="ver">0.30.0 / 0.20.0</td></tr>
<tr><td><b>HTTP client</b></td><td>httpx (async)</td><td class="ver">0.28.1</td></tr>
<tr><td><b>File upload</b></td><td>python-multipart</td><td class="ver">0.0.20</td></tr>
<tr><td><b>Realtime</b></td><td>WebSocket (FastAPI native)</td><td>fan-out broadcast in-process</td></tr>
<tr><td><b>Database</b></td><td>PostgreSQL 16 / SQLite</td><td>portable ด้วย <code>DATABASE_URL</code></td></tr>
<tr><td><b>Container</b></td><td>Docker + docker-compose</td><td>python:3.12-slim, postgres:16-alpine</td></tr>
</table>
</section>
<section id="ext">
<h2>External / Optional services</h2>
<table>
<tr><th>บริการ</th><th>ใช้ทำอะไร</th><th>สถานะ</th></tr>
<tr><td><b>OpenRouter</b></td><td>LLM answer + embeddings</td><td>optional (มี local fallback)</td></tr>
<tr><td><b>Qdrant</b></td><td>vector DB สำหรับ production RAG</td><td>optional (ตั้งผ่าน env)</td></tr>
<tr><td><b>n8n</b></td><td>automation: alert → enrich → ticket/notify</td><td>แนะนำใน plan</td></tr>
<tr><td><b>Vercel</b></td><td>deploy static + Python serverless (<code>api/*.py</code>)</td><td>มี <code>vercel.json</code></td></tr>
</table>
</section>
<section id="full">
<h2>สรุป Full Stack</h2>
<table>
<tr><th>Layer</th><th>เดโม่ (NOW)</th><th>Production (PROD)</th></tr>
<tr><td><b>Frontend</b></td><td>Vanilla JS + Three.js + SVG</td><td>เหมือนเดิม (เพิ่ม WebSocket realtime)</td></tr>
<tr><td><b>Backend</b></td><td>stdlib http.server</td><td>FastAPI + Uvicorn</td></tr>
<tr><td><b>Storage</b></td><td>In-memory + SQLite</td><td>PostgreSQL (SQLAlchemy async)</td></tr>
<tr><td><b>Realtime</b></td><td>Polling</td><td>WebSocket</td></tr>
<tr><td><b>Retrieval</b></td><td>TF-IDF + SQLite vector</td><td>Qdrant / pgvector</td></tr>
<tr><td><b>Detection</b></td><td>Rule-based (regex + MITRE)</td><td>เหมือนเดิม + ML ในอนาคต</td></tr>
<tr><td><b>LLM</b></td><td>OpenRouter (optional)</td><td>OpenRouter / self-host Ollama</td></tr>
<tr><td><b>Deploy</b></td><td>รันตรง <code>python3 server.py</code></td><td>Docker Compose / Vercel</td></tr>
<tr><td><b>Automation</b></td><td>ไม่มี</td><td>n8n workflows</td></tr>
</table>
<div class="callout">ปรัชญา: <b>เดโม่ต้องรันได้ทันทีแบบ zero-dependency และออฟไลน์</b> แล้วค่อยสเกลขึ้น production ด้วยสแตกที่เตรียมไว้ในโปรเจกต์เดียวกัน</div>
</section>
<footer>ROGLAG · Tech Stack · ดู <a href="/plan" style="color:var(--accent)">/plan</a> สำหรับแผนยกระดับ production</footer>
</div>
</body>
</html>
|