Port Map 192.168.50.138
| Port | Service | Unit |
|---|---|---|
| 8088 | GuardDex API + Dashboard | guarddex.service |
| 8089 | Attack Studio | guarddex-attackstudio.service |
| 9090 | TestBench (mothership) / Cockpit (battleship) | guarddex-testbench.service / cockpit.service |
| 5433 | PostgreSQL (Docker: data-postgres) | data-postgres container |
| 5432 | PostgreSQL (native battleship / gaming-postgres) | postgresql.service |
| 6379 | Redis | data-redis container / redis.service |
| 11434 | Ollama (mothership only) | ollama.service |
| 3001 | BeEF Framework | manual |
| 8000 | TTS Studio backend (reserved) | Docker |
Requirements
| Item | Minimum | Notes |
|---|---|---|
| RAM | 4 GB | install.sh hard-stops at <2 GB, warns at <3.5 GB |
| Disk | 10 GB free | 20 GB+ recommended if running Ollama |
| OS | Ubuntu 22.04/24.04, Debian 12/13 | 64-bit only. Debian 13 (Trixie) minimal server recommended for new battleship installs. |
| Python | 3.11+ | installed by install.sh |
| Node.js | 22 (LTS) | needed for APK builds only |
| Java | 21 | needed for APK builds only — JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 |
| Network | Static LAN IP | clients must reliably reach the server |
Supported hardware: Lenovo ThinkPad T430, Dell Wyse 5070, Raspberry Pi 4/5 (4 GB+). Not supported: Pi 3B/3B+ (insufficient RAM).
Installation
Battleship package (recommended)
# On this machine — build the deployment tarball bash /opt/Guarddex0/make-battleship-package.sh # Copies to: guarddex-battleship-YYYYMMDD.tar.gz (~4 MB) # SCP to target node scp guarddex-battleship-*.tar.gz user@TARGET_IP:~ # On target node tar -xzf guarddex-battleship-*.tar.gz && cd guarddex-battleship-*/ sudo bash install.sh
What install.sh prompts for (v1.2)
- Server name + Public URL (auto-detected from
hostname -I) - Admin email, password (with confirmation re-type), and optional admin settings PIN (with confirmation)
- ntfy server URL, username, password (push notifications)
- HIBP (HaveIBeenPwned) API key — optional, enables breach monitor
- VirusTotal API key — optional, improves URL threat scoring
- NextDNS API key + profile ID — optional, auto-adds blocked domains to DNS denylist
- SMTP credentials for email alerts (optional)
- Anthropic API key — write-only (not displayed), required for AI on battleships without Ollama
make-battleship-package.sh, shareable API keys (Anthropic, VT, HIBP, OTX, GreyNoise, Shodan, etc.) are bundled in a keys-preset.env file inside the tarball. install.sh loads them automatically and shows [preset] at each prompt — just press Enter to accept. The preset file is deleted after .env is written.
What install.sh does (in order)
- RAM check — aborts if <2 GB, warns if <4 GB
- Loads
keys-preset.envif present (battleship package) - apt install: python3, pip, postgresql, redis, nmap (no Node.js on battleship)
- Creates Python venv at
/opt/guarddex/venv, pip install -r requirements.txt - Creates
.envfrom prompts + presets - Creates PostgreSQL user + database; grants schema privileges (PG15+ compatible)
- Runs
python3 backend/init_db.py— creates all tables including LAN scanner + breach tables - Creates admin user + NotificationPrefs row via API bootstrap script
- Writes and enables
guarddex.servicesystemd unit withExecStartPrethat auto-updatesPUBLIC_URLin .env from current LAN IP on every boot - Waits up to 24 seconds for service to start (retry loop), then tails first 20 log lines
- Deletes
keys-preset.envif present
@, #, or $ in the password field — they break URL parsing. Use alphanumeric + -_!. only.
Post-install Configuration
First login
http://<SERVER_IP>:8088/dashboard/login.html
Log in as the admin account you created during install. Go to Profile to confirm notification preferences are saved.
ntfy provisioning QR
Visit /dashboard/provision.html — this generates a QR code family members scan to subscribe their ntfy app automatically. Share this page URL or take a screenshot of the QR.
Verify the admin account is flagged as admin
SELECT email, is_admin FROM users; -- If is_admin = false for admin account: UPDATE users SET is_admin = true WHERE email = '[email protected]';
Cockpit — browser-based system monitor
Cockpit provides CPU, RAM, disk, network, logs, and a browser terminal. Available at http://<SERVER_IP>:9090. A shortcut is in the Admin Settings page Quick Links bar. Log in with your OS user credentials.
sudo apt install cockpit -y && sudo systemctl enable --now cockpit.socket
Dynamic MOTD (login message)
If hpt430-master-setup.sh was run before install.sh, the server displays a GuardDex status banner after every SSH login showing CPU, RAM, disk, service states, and fail2ban ban counts.
Verify Installation
bash /opt/Guarddex0/verify.sh
Runs 11 checks: RAM, systemd service, PostgreSQL (native or Docker), Redis (native or Docker), nmap, /health, register, login, notification prefs, URL scan, AI status. All should show [PASS]. A 3 GB machine will show 1 warning (RAM) — expected.
data-postgres and data-redis containers; on battleships it checks native systemd services.
Service Management systemd
sudo systemctl start guarddex # start sudo systemctl stop guarddex # stop sudo systemctl restart guarddex # restart (after .env changes) sudo systemctl reload guarddex # reload config without full restart (if supported) sudo systemctl status guarddex # current state + last few log lines sudo systemctl enable guarddex # auto-start on boot sudo systemctl disable guarddex # remove from boot
Unit file location
/etc/systemd/system/guarddex.service
After editing the unit file: sudo systemctl daemon-reload && sudo systemctl restart guarddex
Other services
sudo systemctl restart guarddex-attackstudio # Attack Studio :8089 sudo systemctl restart guarddex-testbench # TestBench :9090 sudo systemctl restart postgresql # native PostgreSQL (battleship) sudo systemctl restart redis # native Redis (battleship) docker restart data-postgres # Docker PostgreSQL (mothership) docker restart data-redis # Docker Redis (mothership)
Logs & Monitoring
Live log tail
sudo journalctl -u guarddex -f
Time-scoped
sudo journalctl -u guarddex --since "1 hour ago" sudo journalctl -u guarddex --since "2026-05-02 08:00" --until "2026-05-02 10:00"
Filter by severity
sudo journalctl -u guarddex -p err -n 50 # errors only sudo journalctl -u guarddex -p warning -f # warnings + errors, live
JSON scan stats
cat /opt/Guarddex0/guarddex_stats.json | python3 -m json.tool
AI status
curl -s http://localhost:8088/api/v1/analytics/ai-status | python3 -m json.tool
Recent scans (database)
SELECT url, verdict, created_at FROM url_scans ORDER BY created_at DESC LIMIT 20;
Recent alerts (database)
SELECT alert_type, severity, description, created_at FROM alerts ORDER BY created_at DESC LIMIT 20;
Upgrading
Standard upgrade (same node)
cd /opt/Guarddex0 sudo systemctl stop guarddex # Pull latest source (if using git) git pull origin main # Or SCP a new battleship package and extract over the existing directory # (only replaces backend/ and dashboard/ — .env is preserved) # Upgrade Python dependencies venv/bin/pip install -r backend/requirements.txt --quiet # Run DB migrations if any (check CHANGES file first) PYTHONPATH=backend venv/bin/python3 backend/init_db.py sudo systemctl start guarddex sudo journalctl -u guarddex -n 30 # verify clean startup
docs/CHANGES_*.md before upgrading.
Rebuild blocklist rules after updating gd_blocklist.txt
python3 /opt/Guarddex0/extension/scripts/generate_rules.py
# Then rebuild extension zip
bash /opt/Guarddex0/extension/scripts/build.sh
Backup & Restore
Full backup script (run as cron or manually)
BACKUP_DIR="/opt/backups/guarddex/$(date +%Y%m%d)" mkdir -p "$BACKUP_DIR" # Database dump pg_dump -U guarddex_user guarddex_db > "$BACKUP_DIR/guarddex_db.sql" # Or via Docker: docker exec data-postgres pg_dump -U guarddex_user guarddex_db > "$BACKUP_DIR/guarddex_db.sql" # Config and stats cp /opt/Guarddex0/.env "$BACKUP_DIR/env.bak" cp /opt/Guarddex0/guarddex_stats.json "$BACKUP_DIR/" cp /opt/Guarddex0/guarddex_alerts.json "$BACKUP_DIR/" echo "Backup complete → $BACKUP_DIR"
Restore from backup
# Restore .env first, then DB sudo systemctl stop guarddex cp /opt/backups/guarddex/20260502/env.bak /opt/Guarddex0/.env # Native PostgreSQL psql -U guarddex_user -d guarddex_db < /opt/backups/guarddex/20260502/guarddex_db.sql # Docker PostgreSQL docker exec -i data-postgres psql -U guarddex_user -d guarddex_db \ < /opt/backups/guarddex/20260502/guarddex_db.sql sudo systemctl start guarddex
User Management database
List all users
SELECT id, email, full_name, is_admin, created_at FROM users ORDER BY created_at;
Grant admin
UPDATE users SET is_admin = true WHERE email = '[email protected]';
Reset a user's password
# Generate bcrypt hash python3 -c "from passlib.context import CryptContext; \ print(CryptContext(schemes=['bcrypt']).hash('NewPassword123!'))" # Apply it UPDATE users SET hashed_password = '<hash_output>' WHERE email = '[email protected]';
Delete a user and their data
BEGIN; DELETE FROM notification_prefs WHERE user_id = (SELECT id FROM users WHERE email = '[email protected]'); DELETE FROM url_scans WHERE user_id = (SELECT id FROM users WHERE email = '[email protected]'); DELETE FROM alerts WHERE user_id = (SELECT id FROM users WHERE email = '[email protected]'); DELETE FROM users WHERE email = '[email protected]'; COMMIT;
View a user's scan history
SELECT url, verdict, confidence, created_at FROM url_scans WHERE user_id = (SELECT id FROM users WHERE email = '[email protected]') ORDER BY created_at DESC LIMIT 50;
Check notification preferences
SELECT u.email, n.ntfy_enabled, n.ntfy_topic, n.email_enabled, n.sms_enabled FROM notification_prefs n JOIN users u ON u.id = n.user_id;
Battleship Node Deployment new in v2.1
A battleship is a lean GuardDex node for a family member or remote office. It runs the API and dashboard but not Ollama or Attack Studio. AI falls back to Claude Haiku.
Build package on mothership
bash /opt/Guarddex0/make-battleship-package.sh # Output: guarddex-battleship-YYYYMMDD.tar.gz (~4 MB) # Contains: backend/ dashboard/ install.sh verify.sh APK guarddex.apk # Also bundles keys-preset.env (chmod 600) — shareable API keys auto-loaded by install.sh # Keys bundled: Anthropic, VT, HIBP, OTX, GreyNoise, Shodan, AbuseIPDB, URLScan, etc. # Keys NOT bundled: NextDNS, SMTP, Twilio, DB/JWT secrets (per-node only)
Deploy
scp guarddex-battleship-*.tar.gz [email protected]:~ ssh [email protected] tar -xzf guarddex-battleship-*.tar.gz && cd guarddex-battleship-*/ sudo bash install.sh
Key battleship .env settings
ENABLE_OLLAMA=false # no local GPU — use Haiku ANTHROPIC_API_KEY=sk-ant-... # required for AI on battleship DATABASE_URL=postgresql://guarddex_user:PASSWORD@localhost:5432/guarddex_db # Note: native PostgreSQL uses port 5432 (not 5433 like Docker on mothership)
Provision Android app on a battleship
- Install the APK on the user's phone:
adb install guarddex-v2.2-release.apk— or download from/dashboard/guarddex.apkin a browser - On the battleship server, open
/dashboard/provision.html - Show the user the QR code — phone camera scan → GuardDex app opens → server URL saved automatically
- Guide user through enabling Notification Access (Settings → Apps → Special app access → Notification access → GuardDex → On)
- Set GuardDex as default browser (Settings → Apps → Default apps → Browser)
Appliance Setup hpt430-master-setup.sh
Run this script before install.sh on a fresh OS install to harden the machine and prepare it for operation as a GuardDex appliance. Designed for HP t430 thin clients and similar low-power hardware.
- Dell Wyse 5070: tap
F2at boot → Power Management → AC Recovery → Power On - HP t430: tap
F10at boot → Advanced → Power-On Options → After Power Loss → Power On - Other hardware: look for “AC Recovery”, “After Power Loss”, “Restore on AC/Power Loss”, or “Power-On after Power Fail” → set to On (or Last State). Mac minis do this automatically — no change needed.
Temporary failure in name resolution the moment the router hiccups. Add public
fallbacks and prefer IPv4 (one-time, per node):
CONN=$(nmcli -t -f NAME,DEVICE connection show --active | awk -F: '$2=="'"$(ip route show default | awk '{print $5; exit}')"'"{print $1}')
sudo nmcli connection modify "$CONN" +ipv4.dns "1.1.1.1,8.8.8.8"
sudo nmcli connection modify "$CONN" ipv4.ignore-auto-dns no
grep -q '^precedence ::ffff:0:0/96' /etc/gai.conf || echo 'precedence ::ffff:0:0/96 100' | sudo tee -a /etc/gai.conf
sudo nmcli device reapply "$(ip route show default | awk '{print $5; exit}')"
getent ahosts api.nextdns.io (IPv4 should lead) — plain getent hosts won't reflect the change. Safe to run over Tailscale; it can't lock you out.sudo bash /opt/Guarddex0/hpt430-master-setup.sh
Key variables at the top of the script
| Variable | Default | Description |
|---|---|---|
| INSTALL_DOCKER | 0 | Set 1 to install Docker (not needed for GuardDex native) |
| ENABLE_FAIL2BAN | 1 | SSH jail: 3 failed attempts = 24-hour ban |
| ENABLE_ZRAM | 1 | Compressed RAM swap (lz4, 50% of physical RAM). Helps 3 GB machines. |
| SWAP_SIZE_GB | 2 | Fallback swap file size in GB (written to /swapfile) |
| ENABLE_VNC | 0 | Set 1 to install TigerVNC + noVNC browser desktop on port 6080 |
| VNC_PASSWORD | guarddog | VNC session password |
| INSTALL_GUARDDEX | 0 | Set 1 to chain directly into install.sh after setup completes |
| STATIC_IP | "" | Set to e.g. 192.168.1.246 to configure a static LAN IP. Leave blank to skip. |
| STATIC_GW | "" | Gateway for static IP, e.g. 192.168.1.1 |
| STATIC_DNS | "1.1.1.1 9.9.9.9" | DNS servers for static IP config |
What it configures (in order)
- Static IP via Netplan (Ubuntu) or
/etc/network/interfaces(Debian) ifSTATIC_IPis set - Base packages: curl, wget, git, htop, net-tools, fail2ban, unzip, tmux, rsync, nmap
- Tailscale (auto-installs and displays auth URL for one-time setup)
- Docker opt-in (disabled by default)
- fail2ban SSH jail: 3 failures = 24-hour ban
- zram compressed RAM (lz4, 50% of physical RAM) + swap file + sysctl swappiness=10
- Dynamic MOTD at
/etc/update-motd.d/10-guarddex— shows GuardDex banner, CPU/RAM/disk stats, service states, and fail2ban counts on every SSH login - TigerVNC + noVNC browser desktop on port 6080 (opt-in)
- Chains into install.sh (opt-in)
Browser-based desktop (VNC over noVNC)
When ENABLE_VNC=1, the desktop accessible via HDMI can also be viewed in a browser at http://<SERVER_IP>:6080. Useful for diagnosing UI issues or running graphical apps remotely without needing an HDMI monitor attached.
# Start/stop the noVNC session manually
sudo systemctl start guarddex-vnc
sudo systemctl stop guarddex-vnc
sudo systemctl status guarddex-vnc
User Profile Features new in v2.2
Each logged-in user can access these features from their Profile page (/dashboard/profile.html).
Data Export
Downloads a full JSON file of the user's account data: profile, scan history, alerts, notification preferences, breach check history.
GET /api/v2/auth/export-data # Auth: JWT → returns JSON download
Delete Account
Permanently removes the user and all their data across 6 database tables. Requires current password + the exact phrase DELETE MY ACCOUNT.
DELETE /api/v2/auth/account
# Body: {"password": "...", "confirmation": "DELETE MY ACCOUNT"}
Security Report
Generates a printable HTML security summary covering the past N days: URL scans, alert breakdown, breach status, notification history.
GET /api/v2/auth/security-report?days=30 # Auth: JWT → returns HTML
Two-Factor Authentication (TOTP)
TOTP-based 2FA using any authenticator app (Google Authenticator, Authy, Bitwarden, etc.).
- Go to Profile → Two-Factor Authentication → Set Up 2FA
- A QR code appears — scan it with your authenticator app
- Enter the 6-digit code from the app to confirm setup
- On future logins, the login page will prompt for the 6-digit code after your password
qrcode[pil] Python package (Pillow). This is included in requirements.txt as of v2.2. If scanning the QR gives "no usable data," re-run venv/bin/pip install qrcode[pil] Pillow and restart the service.
Build APK
# Prerequisites (mothership only) export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 # Node.js 22 must be installed (via NodeSource) # Debug build (default) bash /opt/Guarddex0/build-apk.sh # Release build (requires keystore) bash /opt/Guarddex0/build-apk.sh release
Output: /opt/Guarddex0/guarddex-v2.2-release.apk (~3.1 MB signed). Also copied to dashboard/guarddex.apk — downloadable from /dashboard/guarddex.apk.
Source: /opt/Guarddex0/mobile/ — Capacitor 8 + Android SDK API 34
Key source files
| File | Purpose |
|---|---|
| mobile/android/app/src/main/java/…/BrowserActivity.java | Security gateway browser (6 privacy extensions) |
| mobile/android/app/src/main/java/…/GuardDexNotificationListener.java | Pre-click phishing scan on all notifications |
| mobile/android/app/src/main/java/…/ServerConfigPlugin.java | SharedPreferences bridge for server URL |
| mobile/android/app/src/main/java/…/DeviceSecurityPlugin.java | Native telemetry for Security Center |
| mobile/android/app/src/main/assets/gd_blocklist.txt | 132-domain ad/tracker blocklist |
| mobile/www/setup.html | First-launch server URL config (QR + manual) |
Build Browser Extension new in v2.1
bash /opt/Guarddex0/extension/scripts/build.sh
# Regenerates rules, icons, zips to guarddex-extension.zip (15 KB)
Rebuild blocklist rules (after updating gd_blocklist.txt)
python3 /opt/Guarddex0/extension/scripts/generate_rules.py # Reads mobile/android/app/src/main/assets/gd_blocklist.txt # Writes extension/rules/blocklist_rules.json (132 rules)
Install in Chrome (development)
# chrome://extensions → Developer mode → Load unpacked # Point to: /opt/Guarddex0/extension/
Install in Firefox (temporary)
# about:debugging → Load Temporary Add-on # Select: /opt/Guarddex0/guarddex-extension.zip
Requires Chrome 111+ / Firefox 128+ for world: "MAIN" fingerprint guard support.
Environment Variables /opt/Guarddex0/.env
| Variable | Default | Notes |
|---|---|---|
| SECRET_KEY req | — | JWT signing secret. Generate: openssl rand -hex 32 |
| DATABASE_URL req | — | Full PostgreSQL URL. Mothership: port 5433. Battleship: port 5432. |
| REDIS_URL | redis://localhost:6379/0 | Rate limiter + cache. Mothership: 6379. Battleship: same. |
| ENABLE_OLLAMA | true | Set false on battleships without GPU |
| ANTHROPIC_API_KEY opt | — | Claude Haiku fallback. Required on battleships with ENABLE_OLLAMA=false |
| ENABLE_AI | true | Master AI on/off switch (both Ollama + Haiku) |
| ACCESS_TOKEN_EXPIRE_MINUTES | 60 | JWT lifetime. Inactivity timeout in frontend auth.js is separate. |
| NTFY_SERVER opt | https://ntfy.sh | ntfy server base URL |
| NTFY_USERNAME | — | ntfy auth username (self-hosted) |
| NTFY_PASSWORD | — | ntfy auth password |
| SMTP_HOST opt | — | Gmail: smtp.gmail.com |
| SMTP_PORT | 587 | STARTTLS |
| SMTP_USERNAME | — | Gmail address |
| SMTP_PASSWORD | — | Gmail app password (not account password) |
| TWILIO_ACCOUNT_SID opt | — | Twilio SID |
| TWILIO_AUTH_TOKEN | — | Twilio auth token |
| TWILIO_FROM_NUMBER | — | E.164 format: +15555550100 |
| NEXTDNS_API_KEY opt | — | NextDNS API key |
| NEXTDNS_PROFILE_ID | — | e.g. 4344ac |
| POSTGRES_PASSWORD | — | Used by install.sh to create DB user (may differ from URL) |
Key API Endpoints
| Method | Path | Auth | Notes |
|---|---|---|---|
| GET | /health | — | Service health check |
| POST | /api/v1/scan/url | optional | {"url":"https://..."} → verdict + signals |
| POST | /api/v2/auth/register | — | {"email","password","full_name","phone"} |
| POST | /api/v2/auth/login | — | Returns {"access_token","token_type","user"} |
| GET | /api/v2/auth/me | JWT | Current user profile |
| GET/PUT | /api/v2/notifications/preferences | JWT | Notification prefs |
| POST | /api/v2/notifications/test | JWT | {"type":"test"} — sends test notification |
| GET | /api/v2/my/scans | JWT | Per-user scan history |
| POST | /api/v1/security/assistant/chat | JWT | SSE streaming AI chat. Body: {"message":"..."} |
| GET | /api/v1/analytics/ai-status | — | AI backend + model info (includes haiku_available) |
| GET | /api/v1/security/attack-lab/status | — | Attack lab tool availability |
| GET | /api/v2/auth/export-data | JWT | Full JSON export of user data (profile, scans, alerts, breach history) |
| DELETE | /api/v2/auth/account | JWT | Delete account + all data. Body: {"password":"...","confirmation":"DELETE MY ACCOUNT"} |
| GET | /api/v2/auth/security-report?days=30 | JWT | Printable HTML security report |
| GET | /api/v2/auth/2fa/setup | JWT | Returns TOTP secret + QR code as base64 PNG |
| POST | /api/v2/auth/2fa/enable | JWT | Confirms + activates 2FA. Body: {"secret":"...","code":"123456"} |
| GET | /api/v2/notifications/history | JWT | Notification send history. Query: days, channel, severity, limit, offset |
| GET | /api/v2/dns/autoblock | JWT | Get DNS auto-block state |
| POST | /api/v2/dns/autoblock | JWT | Set auto-block state. Body: {"enabled":true} |
Scan response shape
{
"verdict": "safe" | "challenge" | "block",
"is_blocked": false,
"is_challenged": false,
"matched_brand": "Bank of America" | null,
"confidence": 0.0 – 1.0,
"signals": ["typosquatting", "fresh domain"],
"reason": "string explanation"
}
AI Configuration
| Condition | AI Backend | Model |
|---|---|---|
| ENABLE_OLLAMA=true + Ollama reachable + model available | Ollama (local) | nous-hermes2:10.7b (assistant), phi3:mini (URL analyzer) |
| ENABLE_OLLAMA=false + ANTHROPIC_API_KEY set | Haiku (cloud) | claude-haiku-4-5-20251001 |
| Ollama unreachable + ANTHROPIC_API_KEY set | Haiku (fallback) | claude-haiku-4-5-20251001 |
| Neither available | None — 503 | — |
Install Ollama (mothership)
curl -fsSL https://ollama.com/install.sh | sh ollama pull nous-hermes2:10.7b # security assistant ollama pull phi3:mini # URL analyzer ollama pull dolphin-mistral:7b # Attack Studio red team AI
Check AI status
curl -s http://localhost:8088/api/v1/analytics/ai-status | python3 -m json.tool
NextDNS Integration
When configured, scan verdicts of block auto-add the domain to the NextDNS denylist, protecting all devices on your network at the DNS level.
NEXTDNS_API_KEY=your_api_key
NEXTDNS_PROFILE_ID=4344ac # "Dave's" profile
Manually check denylist count
curl -H "X-Api-Key: YOUR_KEY" \ "https://api.nextdns.io/profiles/4344ac/denylist" | python3 -m json.tool | grep -c domain
Architecture
Android / Browser Extension
│ HTTPS :8088
▼
┌────────────────────────────────────────┐
│ FastAPI (backend/main.py) │
│ ───────────────────────────────── │
│ /api/v1/ challenge_api.py │ scan, brands, analytics
│ security_api.py │ 15 detectors, AI assistant
│ /api/v2/ auth_api.py │ JWT register/login/me
│ notification_api.py │ prefs, test, dispatch
│ ───────────────────────────────── │
│ api/ ip_reputation.py │ threat intel aggregator
│ notification_service.py │ ntfy / email / SMS
│ security/ phishing_detector.py │ 44-brand + heuristics
│ ai_analyzer.py │ Ollama or Haiku
└──────────┬─────────────────────────────┘
│
┌────────┴──────────┐
│ │
PostgreSQL Redis Ollama
:5433 (Docker) :6379 :11434
:5432 (native) rate limit models
users, scans cache (mothership)
alerts, prefs
Scam Gallery & Drills new in v2.7
A browsable museum of real scams that doubles as a detector, plus consented family fire-drills. Backend: gallery_api.py (/api/v2/gallery), drill_api.py; UI: dashboard/scam-gallery.html.
Pillars
- Browse / Check / Contribute / Trending — seeded scams + a live OpenPhish/URLhaus feed; Check runs the rule detector, gallery-similarity match, and a live-feed link cross-reference.
- Drills — turn any gallery scam into a consent-gated phishing test for a household member. Eligible members must have
allow_drillson (Breach Monitor → "🎯 OK to drill"); consent is verified through the target's own channel before launch. - Child-safety — the 🛡️ Safety tab shows sanitized grooming/sextortion patterns (never victim data) + a reporting path (
/api/v2/gallery/safety-resources: NCMEC, IC3, Childhelp). Uploads to thepredatorcategory are blocked by design.
Moderation & PII
User/edge submissions are auto-redacted (email/phone/long-number/@handle) and queued. Approve via the admin queue: GET /api/v2/gallery/pending → POST /api/v2/gallery/{id}/approve.
Picket edge nodes picket/
# A picket = a thin edge node (Raspberry Pi / weak box) that does OCR + rule-check # LOCALLY, then shares a redacted scam UP to the mothership gallery with a node key. # Mothership .env: PICKET_KEYS=ab12…:living-room-pi,cd34…:kitchen-pi # key:label, comma-separated # Node posts to POST /api/v2/gallery/submit-picket (header X-Picket-Key: ab12…) # Install on the Pi: cd picket && ./install.sh (uvicorn server:app on :8077)
Terminology: picket = the thin gallery edge node. A full GuardDex node is still a battleship (see Deploy Node).
Drill public routing
GUARDDEX_PUBLIC_URL=https://richeybusiness.com # tokenized /d/ drill pages must be reachable # on a real phone, past any access proxy (only /d/ is proxied to :8088)
DNS Scanner & Skip List new in v2.7
Pulls query data from Pi-hole / Technitium / NextDNS, classifies new domains (rules + Claude), and surfaces what needs a decision. UI: dashboard/dns-scanner.html; analytics: dns-analytics.html.
The review queue — four buckets
The queue separates three very different things (the summary cards reflect these):
| Bucket | Meaning | Filter |
|---|---|---|
| 🎯 Slipped through | Resolved & risky — NOT caught by DNS. Needs your decision. | dns_blocked=slipped |
| 🤖 Auto-blocked | GuardDex auto-blocked it (reviewed_by='auto-block'). | status=autoblocked |
| 🛡 Caught by DNS | Already blocked by the DNS server — informational. | status=caughtbydns |
| ✋ Blocked by you | Your manual blocks. | status=blocked |
Auto-block
ENABLE_AUTO_BLOCK=false # when true, high-confidence threats are blocked automatically (reviewed_by='auto-block')
Skip list dns-skiplist.html
Domains the scanner ignores entirely — public resolvers, GuardDex's own threat-intel (URIBL/DNSBL) lookups, your infrastructure. Manage in the UI or via /api/v2/dns/config/skip-list (GET/POST/DELETE/PATCH). Matches a domain and its sub-domains; categories: trusted, public_dns, infrastructure, internal, personal.
Network Device Allowlist new in v2.7
The passive network monitor (network_monitor_loop in security_api.py) watches for ARP spoofing / new devices. The allowlist suppresses your own devices so a real intruder's alert is meaningful. UI: dashboard/network-devices.html.
Match by MAC or hostname or IP
Modern phones rotate their Wi-Fi MAC, so a MAC-only allowlist mis-fires constantly. A device is trusted if any of MAC / reverse-DNS hostname / IP matches (_device_is_known / _effective_known_macs). Hostnames come from reverse-DNS with an avahi mDNS fallback (needs PTR or mDNS on the LAN; falls back to MAC+IP otherwise). Stored in arp_known_devices.json.
# API (/api/v1/security/network/known-devices) GET .../known-devices # current devices + trusted flag POST .../known-devices # {mac,name,ip,hostname} — trust one POST .../known-devices/learn-current # trust everything currently on the LAN DELETE .../known-devices/{mac} # forget NETWORK_MONITOR_ENABLED=true # .env — passive ARP/DNS-hijack monitor
Versioning & the Installer new in v2.7
Single source of truth
/opt/Guarddex0/VERSION # e.g. 2.7.0 — bump this to cut a release # Every installer reads it & prints it; the package is named guarddex-battleship-vX.Y.Z-DATE, # PACKAGE.txt records it, and a node writes its own: cat /opt/guarddex/INSTALLED_VERSION # what version installed this node
Interactive installer
install.sh uses whiptail dialogs when available (colored CLI otherwise). It's sectioned, every API key shows how to get it, bundled keys can be kept with Enter, and it ends with a masked "Configured keys" summary.
# Headless / unattended — set GD_<VARNAME> env vars + GD_ASSUME_YES=1 GD_ADMIN_EMAIL=… GD_ADMIN_PASS=… GD_ANTHROPIC_KEY=… GD_ASSUME_YES=1 sudo -E bash install.sh # Box type: --box=hpt430 (USB data-drive prep first) | default = regular
Troubleshooting — Service / Boot
Service fails to start
sudo journalctl -u guarddex -n 50 --no-pager
Common causes and fixes:
| Error in logs | Fix |
|---|---|
| ModuleNotFoundError | Venv not built: python3 -m venv venv && venv/bin/pip install -r backend/requirements.txt |
| could not connect to server (PostgreSQL) | DB not running — start it, then restart guarddex |
| No such file or directory: .env | Run install.sh or create .env manually |
| Address already in use :8088 | sudo lsof -i :8088 — kill conflicting process |
| PYTHONPATH error / ImportError | Check unit file: ExecStart must include PYTHONPATH=/opt/Guarddex0/backend |
Troubleshooting — Database
Test connectivity
# Native psql -U guarddex_user -d guarddex_db -c "SELECT 1" # Docker (mothership) docker exec data-postgres psql -U guarddex_user -d guarddex_db -c "SELECT 1"
Recreate all tables (dev only — destroys data)
PYTHONPATH=backend venv/bin/python3 backend/init_db.py
Check tables exist
\dt -- inside psql: should list users, url_scans, alerts, notification_prefs
API returns 500 on every request
Almost always a database connection issue. Check DATABASE_URL in .env — watch for @, #, $ in password (must be URL-encoded or avoided). Mothership uses port 5433; battleship uses 5432.
Troubleshooting — Auth / JWT
Login returns 401 for valid credentials
Check SECRET_KEY in .env is set and hasn't changed since the token was issued. Changed SECRET_KEY invalidates all existing tokens — users must log in again.
Admin account shows is_admin = false
UPDATE users SET is_admin = true WHERE email = '[email protected]';
Forgot admin password
python3 -c "from passlib.context import CryptContext; \
print(CryptContext(schemes=['bcrypt']).hash('NewPassword!'))"
UPDATE users SET hashed_password = '<hash>' WHERE email = '[email protected]';
Troubleshooting — Notifications
Notifications not arriving
| Check | How |
|---|---|
| ntfy topic matches | Profile ntfy_topic must match phone subscription topic exactly |
| Throttle window active | phishing_block: 1/hr, security_alert: 1/4hr, dns_critical: 1/hr. Wait or restart service to reset. |
| NotificationPrefs row exists | SELECT * FROM notification_prefs WHERE user_id = (SELECT id FROM users WHERE email='...'); |
| Send test | curl -X POST http://localhost:8088/api/v2/notifications/test -H "Authorization: Bearer TOKEN" -H "Content-Type: application/json" -d '{"type":"test"}' |
| ntfy server reachable | curl -d "test" "$NTFY_SERVER/guarddex-test-topic" |
phishing_block, login_challenge, security_alert, dns_critical, dns_scan_batch. Mismatched keys = silent throttle bypass (fixed in v2.1).
Troubleshooting — Android App
| Symptom | Cause / Fix |
|---|---|
| App shows "Cannot reach server" | Phone not on same LAN as server. Check Wi-Fi network. Or VPN required for remote access. |
| QR code scan does nothing | Intent filter for guarddex:// missing — rebuild APK or reinstall. Camera app must support deep links. |
| Notification scanning not working | Notification Access not granted. Settings → Apps → Special app access → Notification access → GuardDex → On. |
| Browser shows scan error on every page | Server URL not configured or server down. Go to app settings and re-enter server URL. |
| Pages blocked that should be safe | Check scan API response: curl -X POST http://SERVER:8088/api/v1/scan/url -d '{"url":"https://..."}'. Adjust brand DB or heuristics if needed. |
| Dark mode not working | Some sites block CSS injection via strict CSP headers. Not a bug — expected limitation. |
| Bitwarden autofill not appearing | Check Bitwarden is set as autofill provider in Android settings. Our code sets IMPORTANT_FOR_AUTOFILL_YES on the WebView. |
Troubleshooting — Browser Extension
| Symptom | Fix |
|---|---|
| "Scan error" badge on every page | Server URL wrong in popup, or server down, or CF Access blocking API. Check popup → server URL. Add CF Access bypass for your domain. |
| Fingerprint guard not working | Requires Chrome 111+ or Firefox 128+ for world:MAIN content scripts. |
| Ad blocking not working | Verify declarativeNetRequest rulesets enabled: open chrome://extensions → GuardDex → Details → check rule counts. |
| Dark mode flickers on page load | Expected — CSS injects after document_start. Content-Security-Policy on some sites may block the style tag. |
| Extension removed after Firefox restart | Temporary install. Use about:addons → Install Add-on From File for permanent install. |
Troubleshooting — AI / Ollama
# Check Ollama is running curl http://localhost:11434/api/tags # Check available models ollama list # Pull a missing model ollama pull nous-hermes2:10.7b # Check GuardDex AI status endpoint curl -s http://localhost:8088/api/v1/analytics/ai-status | python3 -m json.tool
If haiku_available: false in the AI status response and ENABLE_OLLAMA=false, set ANTHROPIC_API_KEY in .env and restart the service.
If ollama_available: false and ENABLE_OLLAMA=true, check that ollama.service is running and a model from the SECURITY_MODELS list is installed.
Third-party API Keys optional integrations
GuardDex's core scanner works out of the box with no external accounts. These integrations are optional add-ons that improve detection coverage, add notification channels, or enable DNS-level blocking. Each key goes in /opt/Guarddex0/.env, then sudo systemctl restart guarddex.
Anthropic — Claude AI
Used for: AI assistant in Security Center, URL phishing confidence scoring. Required on battleship nodes where Ollama is not available (ENABLE_OLLAMA=false).
ANTHROPIC_API_KEY=sk-ant-api03-...
| Step | Detail |
|---|---|
| Sign up | console.anthropic.com → Create account |
| Get key | API Keys → Create Key → copy the sk-ant-... value |
| Add credit | Billing → Add payment method. No subscription needed — pay per use. |
| Free tier | None, but Haiku costs ~$0.25/M tokens. Typical GuardDex usage: under $1/month. |
| Model used | claude-haiku-4-5-20251001 — fastest and cheapest Claude model |
VirusTotal — URL & IP Threat Intelligence
Used for: Enriches URL scans with VirusTotal's multi-engine verdict (70+ AV engines). Adds signal confidence to the phishing scorer.
VIRUSTOTAL_API_KEY=64-char-hex-key
| Step | Detail |
|---|---|
| Sign up | virustotal.com/gui/join-us → free account |
| Get key | Profile icon (top right) → API Key → copy |
| Free tier | 500 requests/day, 4 requests/min. More than enough for personal/family use. |
| Paid tier | VirusTotal Premium from $40/mo — not needed for GuardDex at family scale |
Shodan — Network & Host Intelligence
Used for: DNS Scanner enrichment — looks up open ports, services, and known vulnerabilities on scanned IPs. Useful for IMSI catcher and C2 beacon detection context.
SHODAN_API_KEY=your-shodan-key
| Step | Detail |
|---|---|
| Sign up | account.shodan.io → Create a free account |
| Get key | account.shodan.io/api_info → copy API Key |
| Free tier | Limited to 1 query/sec, no scan credits. Read-only lookups work fine for GuardDex. |
| Membership | $49 one-time "Lifetime Membership" unlocks full API — worth it if you use the DNS Scanner heavily |
AbuseIPDB — IP Reputation
Used for: Flags IP addresses that have been reported for abuse, spam, or attacks. Adds a signal layer to network-based detectors.
ABUSEIPDB_API_KEY=your-key
| Step | Detail |
|---|---|
| Sign up | abuseipdb.com → Sign Up (free) |
| Get key | Account → API → Create Key |
| Free tier | 1,000 checks/day. More than adequate for family deployments. |
PhishTank — Phishing URL Database
Used for: Cross-checks scanned URLs against a community-maintained database of verified phishing pages. Zero false positives since every entry is human-verified.
PHISHTANK_API_KEY=your-app-key
| Step | Detail |
|---|---|
| Sign up | phishtank.org → Register |
| Get key | phishtank.org/api_info.php → Register your application → copy app key |
| Free tier | 100 requests/day without key, 900/day with a free developer key |
| Note | PhishTank is owned by Cisco/OpenDNS. Database is community-sourced and freely available. |
NextDNS — DNS-level Blocking
Used for: Auto-adds newly blocked domains to your NextDNS denylist, protecting every device on your network at the DNS level — not just those with the app.
NEXTDNS_API_KEY=your-api-key NEXTDNS_PROFILE_ID=4344ac
| Step | Detail |
|---|---|
| Sign up | nextdns.io → Create a free account |
| Get key | nextdns.io/account → API section → copy key |
| Profile ID | nextdns.io → your profile → copy the 6-char ID from the URL (e.g. 4344ac) |
| Free tier | 300,000 queries/month. Paid plan $1.99/mo for unlimited. |
| Router setup | Set your router's DNS to NextDNS servers — all devices on the network are then protected |
Twilio — SMS Alerts
Used for: Sends SMS text message notifications when a phishing block or security alert fires, as an alternative or backup to ntfy push notifications.
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx TWILIO_AUTH_TOKEN=your-auth-token TWILIO_FROM_NUMBER=+15555550100
| Step | Detail |
|---|---|
| Sign up | twilio.com/try-twilio → free trial account |
| Account SID & Token | twilio.com/console → shown on the main dashboard |
| Phone number | Console → Phone Numbers → Buy a Number (~$1/mo for a US number) |
| Free trial | $15 trial credit. Each SMS costs ~$0.0079. Family use = a few cents/month. |
| Trial restriction | Free trial can only SMS verified numbers. Upgrade ($0) to remove this limit. |
ntfy — Push Notifications
Used for: Real-time push alerts to phones. Preferred over SMS for instant delivery with no per-message cost.
NTFY_SERVER=https://ntfy.richeyda.com # or https://ntfy.sh for the public server
NTFY_USERNAME=guarddex
NTFY_PASSWORD=your-ntfy-password
| Option | Detail |
|---|---|
| Self-hosted (recommended) | Run ntfy on any server or Pi. Docker: docker run -p 80:80 binwiederhier/ntfy serve. Free, private, no limits. |
| ntfy.sh (public) | ntfy.sh → free hosted service. No account needed for public topics. Reserved topics require a paid account ($3/mo). |
| Phone app | Android: Play Store or F-Droid. iOS: App Store. Scan QR from /dashboard/provision.html to subscribe automatically. |
ANTHROPIC_API_KEY (enables AI without Ollama) and ntfy (enables push notifications). Everything else is an enhancement you can add later.
Changelog
v2.2 — 2026-05-18
| Component | Change |
|---|---|
| Profile new | Data Export — GET /api/v2/auth/export-data — full JSON download of user's data |
| Profile new | Delete Account — DELETE /api/v2/auth/account — requires password + "DELETE MY ACCOUNT" phrase; cascades 6 tables |
| Profile new | Security Report — GET /api/v2/auth/security-report?days=30 — printable HTML report (URL scans, alerts, breach status, notifications) |
| Profile | 2FA QR fix — GET /api/v2/auth/2fa/setup now returns qr_data_url (base64 PNG); enable endpoint receives secret from frontend |
| Android | APK rebuilt as v2.2 release build (signed v2 scheme, 3.1 MB). dashboard/guarddex.apk serves it for provision page download. |
| Notifications | Device IP in critical DNS block alerts — admin can see which device triggered the block |
| Dashboard | Mobile sidebar — all 7 admin sidebar pages have hamburger nav (<768px) via dashboard/js/sidebar.js |
| Dashboard | Table overflow — DNS scanner and LAN scanner tables use overflow-x:auto for mobile |
| Dashboard | NextDNS service health chip added to admin dashboard health strip |
| Dashboard | Cockpit link added to Admin Settings Quick Links bar |
| DNS Scanner | Auto-block toggle fixed — URL double-path bug (/dns/dns/autoblock) corrected |
| install.sh v1.2 | breach/LAN tables in SQL block, nmap install, HIBP/VT prompts, password + PIN confirmation loops, ExecStartPre auto-IP on boot, keys-preset.env support, PG15+ schema GRANT fix, startup retry loop |
| make-battleship-package.sh | Bundles keys-preset.env with shareable API keys (chmod 600), deleted after .env written |
| verify.sh | 11 checks (added LAN scanner, breach monitor, nmap) |
| hpt430-master-setup.sh | fail2ban SSH jail, zram (lz4, 50% RAM), swap file, dynamic MOTD, TigerVNC/noVNC browser desktop, static IP (Netplan/interfaces), Docker opt-in (default off) |
| Security | Passive network monitor (ARP + DNS hijacking checks every 30 min) in security_api.py |
| Breach Monitor new | Household member slots, HIBP + LeakCheck + Leak-Lookup + Shodan + Hunter.io, history, CSV export |
| Notification History new | Timeline of all push/email/SMS sends, filter by channel/severity/date, export CSV, clear all |
| Python | qrcode[pil] + Pillow added to requirements.txt (required for 2FA QR generation) |
v2.1 — 2026-05-02
| Component | Change |
|---|---|
| Android | BrowserActivity — security gateway browser with 6 privacy extensions (ad block, URL cleaner, HTTPS upgrade, fingerprint reduction, dark mode, credential watchdog) |
| Android | GuardDexNotificationListener — pre-click scanning on all app notifications |
| Android | ServerConfigPlugin — QR deep link server URL config (guarddex://configure) for battleship provisioning |
| Extension new | Chrome/Firefox MV3 WebExtension — URL scanning, 132-domain ad block, HTTPS upgrade, 28-param tracking cleaner, dark mode, fingerprint guard |
| Phase 2.2 | Ollama → Claude Haiku fallback in security_api.py and ai_analyzer.py. ENABLE_OLLAMA=false skips probe entirely. |
| install.sh | v1.1: RAM check (4 GB min), Redis install, ntfy prompts, race condition fix (init_db before service start), admin NotificationPrefs row |
| verify.sh new | 10-check post-install health script with native + Docker PostgreSQL/Redis fallback |
| Notifications | Fixed throttle key mismatch (5 event_type strings aligned to _THROTTLE dict keys) |
| Auth | register() now creates NotificationPrefs row — new users receive notifications immediately |
| Profile UI | ntfy_topic and ntfy_server fields added |
| Login UI | Phone field added to registration form |
| Archive | ~30 v1 root Python files + stale docs moved to archive/ |
v2.0.5 — 2026-02-25
JWT auth, PostgreSQL persistence, 44-brand phishing DB, 15 security detectors, Ollama AI assistant, notification system (ntfy/email/SMS), family dashboard, user profile, TestBench 10 suites.