______________________________________________ BEE-STACK: A HOMELAB FOR BUG BOUNTY RESEARCH Connal McInnis ______________________________________________ 2026-06-16 Table of Contents _________________ 1. The problem 2. The build 3. bee-stack .. 1. Ollama .. 2. Qdrant .. 3. Postgres .. 4. Gitea .. 5. bee-tools 4. Emacs integration 5. Bug bounty workflow 6. What actually makes this interesting 7. Code 1 The problem ============= No broadband. Rural NC. One Android phone tethered over USB. 2 The build =========== A Raspberry Pi 4 runs hostapd and dnsmasq, WiFi AP, DHCP, and NAT for the cluster. A TP-Link managed switch connects three nodes. All internet exits through the phone. ,---- | Three nodes run Talos Linux (v1.12.6, Kubernetes v1.35.2): | | [ WAN / Internet ] | │ (USB Tethering via Android / Mullvad) | ┌──────▼────────────────────────────────────────┐ | │ LoqArch Workstation (Arch Linux + Doom Emacs) │ | └──────┬────────────────────────────────────────┘ | │ (wlp8s0 / 10.10.10.243) | ┌──────▼────────────────────────────────────────┐ | │ Raspberry Pi 4 (AP + NAT + dnsmasq) │ | └──────┬────────────────────────────────────────┘ | │ (eth0 / 10.10.10.1) | [ TP-Link Managed Switch ] | │ | ├─► talos-cp (10.10.10.10) ── k8s Control Plane | │ | ├─► talos-w1 (10.10.10.11) ── Ollama (mirror), Qdrant, Storage | │ | └─► talos-w2 (10.10.10.12) ── Ollama (primary), Gitea, Postgres, bee-tools | `---- Everything uses local-path RWO PVCs. No cloud. No NFS. No CNI drama. Just pure pain and suffering. 3 bee-stack =========== The "beeattack" namespace is the whole point. All services are internal; NodePorts forward through the control-plane IP. 3.1 Ollama ~~~~~~~~~~ Two instances sharing model storage via PVCs. Primary on w2, mirror on w1. Custom model `bee-raw' — derived from a fine-tuned triage base, system-prompted for direct security research with no disclaimers, temperature 0.2, 8k context. Built inside the pod with a Modelfile. Also runs `gemma4' and `nomic-embed-text' for embeddings. 3.2 Qdrant ~~~~~~~~~~ Three collections: `docs' (reference material, 768-dim cosine), `notes' (org-denote vault + research notes), `code' (gitea repo contents). All ingested via a Python chunker that calls the nomic embedding model through Ollama's API. Gotchas worth noting: Qdrant 1.17+ changed the upsert format. The old `points' key breaks silently on some operations. WAL corruption happens if you patch the deployment before the pod finishes terminating — wait for full pod termination before reapplying. 3.3 Postgres ~~~~~~~~~~~~ Structured side of the research store: findings, targets, LLM sessions, research notes, doc full-text with a `tsvector' index, and a tool call log. Postgres FTS complements the Qdrant semantic search — exact-match queries go to Postgres, fuzzy conceptual queries go to Qdrant. Port-forwarded on 15432 locally. NodePort is technically open but firewalled by Talos. 3.4 Gitea ~~~~~~~~~ Self-hosted git. Registration disabled. Push webhook fires on every commit → bee-tools re-ingests changed files into `qdrant/code'. Token stored at `~/.config/bee-tools-token'. 3.5 bee-tools ~~~~~~~~~~~~~ FastAPI bridge. The connective tissue. Endpoints cover: RAG search (single collection and merged multi-collection), git file read/write/search, Postgres note and query operations, session save/load, doc ingest and FTS, bug bounty findings CRUD, targets, tool call logging, and a browser UI. Source lives in `~/Homelab/bee-stack/bee-tools.py'. Deployed via configmap — edit file, recreate configmap, rollout restart. One rule: all route definitions must appear before the `if __name__' block. FastAPI silently drops anything after it. 4 Emacs integration =================== The workstation (LoqArch, Arch Linux) runs Doom Emacs. All of the above surfaces through `gptel' via a custom config at `~/.config/doom/custom/llm-gptel.el'. Keybinds live under `SPC l': Key Action ------------------------------------------------------- `SPC l l' Open gptel chat buffer `SPC l B' Switch to bug bounty mode `SPC l T' Start recon on target domain `SPC l R' Toggle RAG auto-inject `SPC l q' Manual RAG search → `*bee-rag*' buffer `SPC l Q' Postgres FTS → `*bee-docs*' buffer `SPC l w' Web search → `*bee-web*' buffer `SPC l A' Security analysis of current buffer/region `SPC l e' Run shell command on LoqArch `SPC l F' Save finding to Postgres `SPC l L' List findings → `*bee-findings*' buffer RAG auto-inject runs before every prompt: pulls the top-scoring chunks from Qdrant across all three collections, filters below a 0.72 score threshold, injects up to 5 chunks into the system context. The model sees relevant docs without being asked. gptel tools (callable by the model during a session): `rag_search', `doc_search', `web_search', `url_fetch', `shell_exec', `file_read', `file_write', `git_read', `git_write', `git_search', `db_note', `db_query', `vuln_finding'. 5 Bug bounty workflow ===================== The whole stack exists to support this: ,---- | SPC l B → bug bounty mode (no-disclaimer system prompt) | SPC l T → enter target → LLM recon analysis auto-starts | SPC l w → search target intel | SPC l W → fetch JS, pages, endpoints | SPC l e → run local tools (curl, ffuf, nmap) | SPC l A → analyze interesting code or responses | SPC l F → confirmed finding → save to Postgres | SPC l L → review all findings | SPC X v → org-capture detailed report `---- Findings track: target, title, severity, description, PoC, impact, CVSS, status, and bounty paid. Status flow: `new → triaged → accepted → resolved' (or `duplicate' / `wontfix'). 6 What actually makes this interesting ====================================== Most homelab LLM setups front-end a single Ollama instance. This one is built around a research loop: 1. Docs and notes are embedded and searchable semantically and via FTS. 2. Every LLM session has access to the full tool set — search, shell, git, DB — without any manual copy-paste. 3. Findings feed back into the knowledge base. A resolved vuln becomes a note, gets embedded, and shows up in future sessions on similar targets. 4. The whole thing runs on a tethered phone connection. Nothing about the stack assumes reliable or fast internet. The constraint shaped the design. Local-first wasn't a preference — it was the only option. 7 Code ====== [bee-stack] (self-hosted, not public) ,---- | ~/Homelab/bee-stack/ | ├── bee-tools.py # FastAPI service | ├── bee-stack.sh # deploy postgres + bee-tools | ├── bee-tools-bb.sh # deploy findings/targets schema + elisp | └── fix-bee-tools.sh # clean rebuild + redeploy `---- [bee-stack]