11 KiB
Database Schema Documentation
BanGUI uses two SQLite databases:
| Database | Purpose | Location |
|---|---|---|
| BanGUI app DB | Own configuration, sessions, blocklist sources, import logs, geo cache | bangui.db |
| fail2ban DB | fail2ban's internal ban/jail data (read-only) | Configured via FAIL2BAN_DB env var |
1. BanGUI Application Schema
Single source of truth: backend/app/db.py.
1.1 settings
Key-value store for application configuration.
| Column | Type | Constraints |
|---|---|---|
id |
INTEGER | PRIMARY KEY AUTOINCREMENT |
key |
TEXT | NOT NULL UNIQUE |
value |
TEXT | NOT NULL |
created_at |
TEXT | NOT NULL DEFAULT ISO 8601 |
updated_at |
TEXT | NOT NULL DEFAULT ISO 8601 |
Indexes: PK only.
Purpose: Stores app-wide settings (e.g., timezone, UI preferences). All settings access goes through settings_repo / settings_service.
1.2 sessions
Session tokens for web authentication.
| Column | Type | Constraints |
|---|---|---|
id |
INTEGER | PRIMARY KEY AUTOINCREMENT |
token_hash |
TEXT | NOT NULL UNIQUE |
created_at |
TEXT | NOT NULL DEFAULT ISO 8601 |
expires_at |
TEXT | NOT NULL |
Indexes: idx_sessions_token_hash (UNIQUE) on token_hash.
Purpose: Web session management. Tokens are SHA-256 hashed before storage. Sessions expire and are cleaned up by session_cleanup task. See auth_service.py.
1.3 blocklist_sources
Blocklist source definitions for the import pipeline.
| Column | Type | Constraints |
|---|---|---|
id |
INTEGER | PRIMARY KEY AUTOINCREMENT |
name |
TEXT | NOT NULL |
url |
TEXT | NOT NULL UNIQUE |
enabled |
INTEGER | NOT NULL DEFAULT 1 (boolean) |
created_at |
TEXT | NOT NULL DEFAULT ISO 8601 |
updated_at |
TEXT | NOT NULL DEFAULT ISO 8601 |
Indexes: PK only.
Purpose: Defines sources for blocklist imports. See blocklist_repo, blocklist_service, blocklist_import_workflow.
1.4 import_log
Audit log of individual blocklist import operations.
| Column | Type | Constraints |
|---|---|---|
id |
INTEGER | PRIMARY KEY AUTOINCREMENT |
source_id |
INTEGER | REFERENCES blocklist_sources(id) ON DELETE RESTRICT |
source_url |
TEXT | NOT NULL |
timestamp |
INTEGER | NOT NULL (UNIX epoch) |
ips_imported |
INTEGER | NOT NULL DEFAULT 0 |
ips_skipped |
INTEGER | NOT NULL DEFAULT 0 |
errors |
TEXT |
Indexes:
idx_import_log_id_descon(id DESC)— cursor paginationidx_import_log_source_id_descon(source_id, id DESC)— filtered pagination
Purpose: Audit trail for imports. source_id RESTRICT prevents source deletion when logs exist. See migration 9.
Migration 8: timestamp migrated from TEXT ISO 8601 to INTEGER UNIX epoch.
1.5 geo_cache
Geo-IP lookup cache for ban IP metadata.
| Column | Type | Constraints |
|---|---|---|
ip |
TEXT | PRIMARY KEY |
country_code |
TEXT | |
country_name |
TEXT | |
asn |
TEXT | |
org |
TEXT | |
cached_at |
TEXT | NOT NULL DEFAULT ISO 8601 |
Additional (migration 3):
| Column | Type | Constraints |
|---|---|---|
last_seen |
TEXT | NOT NULL DEFAULT ISO 8601 |
Indexes: PK only.
Purpose: Caches GeoIP results to reduce third-party API calls. TTL managed by geo_cache_cleanup task. See geo_cache_repo, geo_service.
1.6 history_archive
Archived ban/unban history mirrored from fail2ban DB.
| Column | Type | Constraints |
|---|---|---|
id |
INTEGER | PRIMARY KEY AUTOINCREMENT |
jail |
TEXT | NOT NULL |
ip |
TEXT | NOT NULL |
timeofban |
INTEGER | NOT NULL (UNIX epoch) |
bancount |
INTEGER | NOT NULL |
data |
TEXT | NOT NULL (JSON) |
action |
TEXT | NOT NULL CHECK IN ('ban', 'unban') |
created_at |
TEXT | NOT NULL DEFAULT ISO 8601 |
Constraints: UNIQUE(ip, jail, action, timeofban) prevents duplicate archive rows.
Indexes:
idx_history_archive_jail_timeofbanon(jail, timeofban DESC)— dashboard filter by jail + time orderingidx_history_archive_timeofban_jail_actionon(timeofban DESC, jail, action)— timeline filtersidx_history_archive_ipon(ip)— IP prefix/exact searchesidx_history_archive_actionon(action)— ban/unban filtering
Purpose: Long-term ban history. Synced from fail2ban DB by history_sync task. See history_archive_repo, history_service.
1.7 scheduler_lock
Database-backed mutex for multi-worker scheduler safety.
| Column | Type | Constraints |
|---|---|---|
id |
INTEGER | PRIMARY KEY CHECK (id = 1) — singleton row |
pid |
INTEGER | NOT NULL |
hostname |
TEXT | NOT NULL |
created_at |
REAL | NOT NULL (UNIX epoch) |
heartbeat_at |
REAL | NOT NULL (UNIX epoch) |
Indexes: PK only (singleton constraint).
Purpose: Only one worker process holds the scheduler lock at a time. Lock is heartbeat-renewed by scheduler_lock_heartbeat task. Uses BEGIN IMMEDIATE transaction to acquire atomically. See scheduler_lock.py.
1.8 import_runs
Tracks unique blocklist imports for idempotent retries.
| Column | Type | Constraints |
|---|---|---|
id |
INTEGER | PRIMARY KEY AUTOINCREMENT |
source_id |
INTEGER | NOT NULL REFERENCES blocklist_sources(id) ON DELETE CASCADE |
content_hash |
TEXT | NOT NULL |
status |
TEXT | NOT NULL CHECK IN ('pending', 'completed', 'failed') |
imported_count |
INTEGER | NOT NULL DEFAULT 0 |
skipped_count |
INTEGER | NOT NULL DEFAULT 0 |
error_message |
TEXT | |
created_at |
TEXT | NOT NULL DEFAULT ISO 8601 |
updated_at |
TEXT | NOT NULL DEFAULT ISO 8601 |
Constraints: UNIQUE(source_id, content_hash) — same source + content = same import run.
Indexes: idx_import_runs_source_status on (source_id, status) — lookup completed imports by source.
Purpose: Prevents duplicate IP bans on import crash/retry. See migration 6 and blocklist_import_workflow.
1.9 schema_migrations
Tracks applied schema versions.
| Column | Type | Constraints |
|---|---|---|
version |
INTEGER | PRIMARY KEY |
migrated_at |
TEXT | NOT NULL DEFAULT ISO 8601 |
Indexes: PK only.
Purpose: Idempotent schema migration tracker. Records each applied version number. See init_db() and _migrate_schema().
2. Fail2ban Database Schema
Read-only access via fail2ban_db_repo. Fail2ban manages this DB; BanGUI mirrors data into history_archive.
2.1 fail2banDb
| Column | Type | Constraints |
|---|---|---|
version |
INTEGER |
Single row tracking DB schema version.
2.2 jails
| Column | Type | Constraints |
|---|---|---|
name |
TEXT | NOT NULL UNIQUE |
enabled |
INTEGER | NOT NULL DEFAULT 1 |
Indexes: jails_name on (name).
2.3 logs
| Column | Type | Constraints |
|---|---|---|
jail |
TEXT | NOT NULL FK → jails(name) ON DELETE CASCADE |
path |
TEXT | |
firstlinemd5 |
TEXT | |
lastfilepos |
INTEGER | DEFAULT 0 |
UNIQUE(jail, path) |
||
UNIQUE(jail, path, firstlinemd5) |
Indexes: logs_path on (path), logs_jail_path on (jail, path).
2.4 bans
| Column | Type | Constraints |
|---|---|---|
jail |
TEXT | NOT NULL FK → jails(name) |
ip |
TEXT | |
timeofban |
INTEGER | NOT NULL |
bantime |
INTEGER | NOT NULL |
bancount |
INTEGER | NOT NULL DEFAULT 1 |
data |
JSON |
Indexes:
bans_jail_timeofban_ipon(jail, timeofban)bans_jail_ipon(jail, ip)bans_ipon(ip)
2.5 bips
Backup IPs table (ban backup).
| Column | Type | Constraints |
|---|---|---|
ip |
TEXT | NOT NULL |
jail |
TEXT | NOT NULL FK → jails(name) |
timeofban |
INTEGER | NOT NULL |
bantime |
INTEGER | NOT NULL |
bancount |
INTEGER | NOT NULL DEFAULT 1 |
data |
JSON | |
| PRIMARY KEY | (ip, jail) |
Indexes: bips_timeofban on (timeofban), bips_ip on (ip).
3. Relationships and Constraints
blocklist_sources (1) ──(id)──→ import_log.source_id [RESTRICT on delete]
└──→ import_runs.source_id [CASCADE on delete]
settings: standalone (key-value, no FK)
sessions: standalone (token hash, no FK)
geo_cache: standalone (IP → geo data, no FK)
history_archive: standalone (archived ban history, no FK)
scheduler_lock: singleton row (id=1), no FK
schema_migrations: standalone (migration tracking, no FK)
Fail2ban tables are separate and read-only from BanGUI's perspective.
4. Indexes Summary
| Table | Index | Columns |
|---|---|---|
sessions |
idx_sessions_token_hash |
token_hash UNIQUE |
import_log |
idx_import_log_id_desc |
id DESC |
import_log |
idx_import_log_source_id_desc |
source_id, id DESC |
import_runs |
idx_import_runs_source_status |
source_id, status |
history_archive |
idx_history_archive_jail_timeofban |
jail, timeofban DESC |
history_archive |
idx_history_archive_timeofban_jail_action |
timeofban DESC, jail, action |
history_archive |
idx_history_archive_ip |
ip |
history_archive |
idx_history_archive_action |
action |
jails |
jails_name |
name |
logs |
logs_path |
path |
logs |
logs_jail_path |
jail, path |
bans |
bans_jail_timeofban_ip |
jail, timeofban |
bans |
bans_jail_ip |
jail, ip |
bans |
bans_ip |
ip |
bips |
bips_timeofban |
timeofban |
bips |
bips_ip |
ip |
5. Migration History
| Version | Description |
|---|---|
| 1 | Initial schema: settings, sessions, blocklist_sources, import_log, geo_cache, history_archive, schema_migrations |
| 2 | Hash session tokens (token_hash column). Invalidates all existing sessions. |
| 3 | Add last_seen to geo_cache for retention policy. |
| 4 | Add scheduler_lock table for multi-worker scheduler mutex. |
| 5 | Add indexes to history_archive for query performance (4 indexes). |
| 6 | Add import_runs table for idempotent import tracking. |
| 7 | Add indexes to import_log for cursor-based pagination. |
| 8 | Migrate import_log.timestamp from TEXT ISO 8601 → INTEGER UNIX epoch. |
| 9 | Change import_log.source_id FK to ON DELETE RESTRICT (prevents orphaned logs). Recreate table with new FK semantics. |
Current schema version: 9 (_CURRENT_SCHEMA_VERSION in db.py).
6. Performance Notes
- WAL mode (
PRAGMA journal_mode=WAL) — concurrent reads allowed, better write performance under concurrency. - Foreign keys enforced (
PRAGMA foreign_keys=ON) — data integrity at DB level. - Busy timeout 5000 ms — prevents "database is locked" errors under contention.
history_archiveindexes — tuned for dashboard filter + time ordering + pagination. See migration 5 andPERFORMANCE.md.import_logindexes — tuned for cursor-based pagination (newest-first by id). See migration 7.geo_cachePK onip— O(1) lookup for geo enrichment on ban events.scheduler_locksingleton (CHECK (id = 1)) — trivial lock existence check.
For detailed query patterns and benchmarks, see Docs/PERFORMANCE.md.