Post 021
The Concept
BD-1 is a custom Discord bot powered by Anthropic's Claude API. It lives in the Alliance Fleet Discord server and serves as an interactive knowledge assistant. Answering questions about the homelab infrastructure, study material, and project documentation using a Git-backed knowledge pipeline.
The constraint: hard $10/month budget cap on API costs. That constraint shaped every design decision.
Architecture
Discord ←→ BD-1 (Node.js) ←→ Anthropic API (Claude Haiku/Sonnet)
│
├── SQLite (conversation state, usage tracking)
├── PM2 (process management)
└── Git knowledge repo (synced from GitHub)
Host: Stinger Mantis VM (Node-B)
Runtime: Node.js 20+
Bot: 892 lines of JavaScript
DB: SQLite (local, no external dependencies)
Process: PM2 (auto-restart, log rotation)
Budget: $10/month hard cap (Anthropic API)
The Knowledge Pipeline
BD-1 doesn't just answer generic questions, it has context about the entire Alliance Fleet. This comes from a Git-backed knowledge system:
Source of Truth
A private GitHub repository contains the master knowledge document, a 1,128-line v3 export covering:
- Full homelab topology (nodes, VMs, containers, IPs)
- All 25+ services with codenames and configurations
- Network architecture (4 VLANs, firewall rules)
- AI agent configurations
- Career context and certification progress
Sync Mechanism
The knowledge repo is cloned to /opt/bd1-knowledge on the Stinger Mantis VM. A fine-grained read-only GitHub Personal Access Token (PAT) provides access:
# Initial clone
git clone https://<PAT>@github.com/timanlemvo/bd1-knowledge.git /opt/bd1-knowledge
# Scheduled sync (cron or n8n)
cd /opt/bd1-knowledge && git pull
BD-1 reads the knowledge files at startup and includes relevant sections in the system prompt when responding to queries. This means updating the bot's knowledge is a git push - no code changes, no redeployment.
Budget Management
The $10/month hard cap is enforced in code. BD-1 tracks token usage per message and maintains a running monthly total in SQLite:
// Simplified budget tracking
const MONTHLY_BUDGET_CENTS = 1000; // $10.00
const HAIKU_INPUT_COST = 0.025; // per 1K tokens
const HAIKU_OUTPUT_COST = 0.125; // per 1K tokens
async function checkBudget(estimatedTokens) {
const currentMonth = new Date().toISOString().slice(0, 7);
const spent = await db.get(
'SELECT SUM(cost_cents) as total FROM usage WHERE month = ?',
[currentMonth]
);
if ((spent?.total || 0) + estimatedCost > MONTHLY_BUDGET_CENTS) {
return { allowed: false, remaining: MONTHLY_BUDGET_CENTS - (spent?.total || 0) };
}
return { allowed: true, remaining: MONTHLY_BUDGET_CENTS - (spent?.total || 0) };
}
Model Routing
Most queries use Claude Haiku (cheapest, fastest). Complex questions or those requiring deeper reasoning are routed to Claude Sonnet. The routing decision is based on message length and detected complexity:
function selectModel(message) {
const wordCount = message.split(' ').length;
const hasCodeBlock = message.includes('```');
const isComplex = wordCount > 100 || hasCodeBlock;
return isComplex ? 'claude-sonnet-4-20250514' : 'claude-haiku-4-5-20251001';
}
Haiku handles ~90% of requests. Sonnet is reserved for technical deep-dives. This keeps costs well under the cap.
SQLite Schema
CREATE TABLE conversations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT NOT NULL,
channel_id TEXT NOT NULL,
message TEXT NOT NULL,
response TEXT NOT NULL,
model TEXT NOT NULL,
input_tokens INTEGER,
output_tokens INTEGER,
cost_cents REAL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE usage (
id INTEGER PRIMARY KEY AUTOINCREMENT,
month TEXT NOT NULL,
cost_cents REAL NOT NULL,
model TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_usage_month ON usage(month);
CREATE INDEX idx_conversations_user ON conversations(user_id, created_at DESC);
Process Management with PM2
# Start BD-1 with PM2
pm2 start /opt/bd1/index.js --name bd1
# Auto-restart on crash
pm2 save
pm2 startup
# Log rotation (prevent disk fill)
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7
PM2 provides:
- Automatic restart on crash
- Log management with rotation
- Memory/CPU monitoring
- Startup script generation (survives reboots)
Deployment Issues Resolved
Node.js Version
The Stinger Mantis VM shipped with Node.js 16, which doesn't support the Anthropic SDK's modern JavaScript features. Upgraded to Node.js 20:
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt-get install -y nodejs
Locale Fix
Internationalization errors from the Anthropic SDK due to missing locale data:
apt-get install -y locales
sed -i 's/# en_US.UTF-8/en_US.UTF-8/' /etc/locale.gen
locale-gen
export LANG=en_US.UTF-8
Wazuh Agent Enrollment
After deploying BD-1's VM, enrolled it as Wazuh agent #10 for security monitoring:
/tmp/install-wazuh-agent.sh "Stinger-Mantis"
What It Does
BD-1 responds to mentions and slash commands in the Alliance Fleet Discord:
- Infrastructure queries - "What's the IP for Grafana?" → Answers from knowledge base
- Study assistance - "Explain Azure RBAC" → Claude generates explanation with homelab analogies
- Troubleshooting - "Why would NPM return 502?" → Contextual answer knowing the Alliance Fleet topology
- Project status - "What's left on the SIEM project?" → References the master knowledge doc
Cost Results
| Month | Messages | Haiku Calls | Sonnet Calls | Total Cost |
|---|---|---|---|---|
| Feb 2026 | ~200 | ~180 | ~20 | $3.47 |
| Mar 2026 | ~150 | ~140 | ~10 | $2.12 |
Well under the $10 cap. Haiku is genuinely cheap for Discord-length interactions.
Related: Post 022 - K-2SO Architecture | Post 024 - Discord as an Ops Console