- [The problem](#orgbb75f94)
- [The build](#orgfbe04a5)
- [bee-stack](#org2a0b6ad)
- [Ollama](#org1c426c5)
- [Qdrant](#orgdc1f624)
- [Postgres](#orge7fbc62)
- [Gitea](#org7a818c2)
- [bee-tools](#org208cd35)
- [Emacs integration](#org52a039b)
- [Bug bounty workflow](#orgc55ddfb)
- [What actually makes this interesting](#org1316116)
- [Code](#org0be0fe8)
# The problem
No broadband. Rural NC. One Android phone tethered over USB.
# 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.
```shell
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.
# bee-stack
The “beeattack” namespace is the whole point. All services are internal; NodePorts forward through the control-plane IP.
## 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.
## 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.
## 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.
## 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`.
## 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.
# 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`.
# 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`).
# 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.
# Code
[bee-stack](https://gitea.bee.local/yopi/bee-stack) (self-hosted, not public)
```bash
~/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
```