Database Tables Reference¶
Complete reference of Mailborder's MariaDB database schema.
Overview¶
Mailborder uses MariaDB to store:
- User accounts and authentication
- System configuration
- Email policies and rules
- Quarantined messages metadata
- Audit logs and statistics
- Internationalization strings
Database: mailborder Engine: InnoDB Charset: utf8mb4 Collation: utf8mb4_unicode_ci
Schema Organization¶
Tables are organized by function:
| Prefix | Purpose | Example |
|---|---|---|
mb_users_* | User management | mb_users, mb_users_meta |
mb_auth_* | Authentication | mb_auth_totp, mb_auth_passkeys |
mb_policy_* | Email policies | mb_policy_rules, mb_policy_whitelist |
mb_quarantine_* | Quarantine | mb_quarantine, mb_quarantine_attachments |
mb_log_* | Logging | mb_log_audit, mb_log_email |
mb_stats_* | Statistics | mb_stats_spam, mb_stats_virus |
mb_settings_* | Configuration | mb_settings, mb_settings_defaults |
mb_i18n_* | Languages | mb_i18n_strings, mb_i18n_languages |
User Management Tables¶
mb_users¶
Primary user accounts table.
Purpose: Store administrator user accounts.
CREATE TABLE `mb_users` (
`user_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`email` VARCHAR(255) NOT NULL,
`password_hash` VARCHAR(255) NOT NULL,
`display_name` VARCHAR(255) DEFAULT NULL,
`privilege_level` TINYINT(2) UNSIGNED NOT NULL DEFAULT 5,
`enabled` TINYINT(1) NOT NULL DEFAULT 1,
`locked` TINYINT(1) NOT NULL DEFAULT 0,
`failed_login_attempts` INT(11) UNSIGNED NOT NULL DEFAULT 0,
`last_login` DATETIME DEFAULT NULL,
`last_login_ip` VARCHAR(45) DEFAULT NULL,
`password_changed_at` DATETIME DEFAULT NULL,
`must_change_password` TINYINT(1) NOT NULL DEFAULT 0,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`user_id`),
UNIQUE KEY `email` (`email`),
KEY `enabled` (`enabled`),
KEY `locked` (`locked`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Columns:
user_id- Unique user identifieremail- Login email (unique)password_hash- Argon2id password hashdisplay_name- Full name for displayprivilege_level- Access level (1-10, 10=highest)enabled- Account active statuslocked- Account locked after failed loginsfailed_login_attempts- Counter for lockoutlast_login- Most recent successful loginlast_login_ip- IP address of last loginpassword_changed_at- Password age trackingmust_change_password- Force password change on next login
mb_users_meta¶
WordPress-style metadata for users.
Purpose: Store flexible user preferences and settings.
CREATE TABLE `mb_users_meta` (
`meta_id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` INT(11) UNSIGNED NOT NULL,
`meta_key` VARCHAR(255) NOT NULL,
`meta_value` LONGTEXT DEFAULT NULL,
PRIMARY KEY (`meta_id`),
KEY `user_id` (`user_id`),
KEY `meta_key` (`meta_key`),
CONSTRAINT `mb_users_meta_user_id` FOREIGN KEY (`user_id`)
REFERENCES `mb_users` (`user_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Common meta keys:
language- User's preferred language codetimezone- User's timezonetheme- UI theme preferencenotifications_enabled- Email notifications on/offtwo_factor_backup_codes- Encrypted 2FA backup codessession_fingerprint- Browser fingerprint for security
Authentication Tables¶
mb_auth_sessions¶
Active user sessions.
CREATE TABLE `mb_auth_sessions` (
`session_id` VARCHAR(64) NOT NULL,
`user_id` INT(11) UNSIGNED NOT NULL,
`ip_address` VARCHAR(45) NOT NULL,
`user_agent` VARCHAR(500) DEFAULT NULL,
`fingerprint` VARCHAR(64) DEFAULT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`last_activity` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`expires_at` DATETIME NOT NULL,
PRIMARY KEY (`session_id`),
KEY `user_id` (`user_id`),
KEY `expires_at` (`expires_at`),
CONSTRAINT `mb_auth_sessions_user_id` FOREIGN KEY (`user_id`)
REFERENCES `mb_users` (`user_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Session lifecycle: 1. Created on login 2. Validated on each request 3. Expired after inactivity or logout 4. Cleaned up by cron
mb_auth_totp¶
TOTP (Time-based One-Time Password) 2FA configuration.
CREATE TABLE `mb_auth_totp` (
`totp_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` INT(11) UNSIGNED NOT NULL,
`secret` VARCHAR(32) NOT NULL,
`enabled` TINYINT(1) NOT NULL DEFAULT 0,
`verified` TINYINT(1) NOT NULL DEFAULT 0,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`last_used` DATETIME DEFAULT NULL,
PRIMARY KEY (`totp_id`),
UNIQUE KEY `user_id` (`user_id`),
CONSTRAINT `mb_auth_totp_user_id` FOREIGN KEY (`user_id`)
REFERENCES `mb_users` (`user_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
mb_auth_passkeys¶
WebAuthn/FIDO2 passkey credentials.
CREATE TABLE `mb_auth_passkeys` (
`credential_id` VARCHAR(255) NOT NULL,
`user_id` INT(11) UNSIGNED NOT NULL,
`public_key` TEXT NOT NULL,
`counter` INT(11) UNSIGNED NOT NULL DEFAULT 0,
`device_name` VARCHAR(255) DEFAULT NULL,
`device_type` VARCHAR(50) DEFAULT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`last_used` DATETIME DEFAULT NULL,
PRIMARY KEY (`credential_id`),
KEY `user_id` (`user_id`),
CONSTRAINT `mb_auth_passkeys_user_id` FOREIGN KEY (`user_id`)
REFERENCES `mb_users` (`user_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Passkey data: - credential_id - Unique WebAuthn credential ID - public_key - CBOR-encoded public key - counter - Signature counter (anti-replay) - device_name - User-friendly name ("YubiKey 5", "iPhone 13") - device_type - platform or cross-platform
Email Policy Tables¶
mb_policy_rules¶
Custom email policy rules.
CREATE TABLE `mb_policy_rules` (
`rule_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`description` TEXT DEFAULT NULL,
`enabled` TINYINT(1) NOT NULL DEFAULT 1,
`priority` INT(11) NOT NULL DEFAULT 100,
`conditions` TEXT NOT NULL,
`actions` TEXT NOT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`match_count` INT(11) UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`rule_id`),
UNIQUE KEY `name` (`name`),
KEY `enabled` (`enabled`),
KEY `priority` (`priority`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
JSON format for conditions/actions:
{
"conditions": [
{"type": "from", "value": "@external.com"},
{"type": "score", "operator": ">", "value": 5.0}
],
"actions": [
{"type": "quarantine"},
{"type": "notify", "email": "admin@example.com"}
]
}
mb_policy_whitelist¶
Sender whitelist entries.
CREATE TABLE `mb_policy_whitelist` (
`whitelist_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`entry_type` ENUM('email','domain','ip') NOT NULL,
`entry_value` VARCHAR(255) NOT NULL,
`reason` TEXT DEFAULT NULL,
`created_by` INT(11) UNSIGNED DEFAULT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`expires_at` DATETIME DEFAULT NULL,
`match_count` INT(11) UNSIGNED NOT NULL DEFAULT 0,
`last_match` DATETIME DEFAULT NULL,
PRIMARY KEY (`whitelist_id`),
UNIQUE KEY `entry` (`entry_type`, `entry_value`),
KEY `expires_at` (`expires_at`),
KEY `created_by` (`created_by`),
CONSTRAINT `mb_policy_whitelist_created_by` FOREIGN KEY (`created_by`)
REFERENCES `mb_users` (`user_id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
mb_policy_blacklist¶
Sender blacklist entries.
CREATE TABLE `mb_policy_blacklist` (
`blacklist_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`entry_type` ENUM('email','domain','ip') NOT NULL,
`entry_value` VARCHAR(255) NOT NULL,
`action` ENUM('reject','discard','quarantine') NOT NULL DEFAULT 'reject',
`reason` TEXT DEFAULT NULL,
`created_by` INT(11) UNSIGNED DEFAULT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`expires_at` DATETIME DEFAULT NULL,
`match_count` INT(11) UNSIGNED NOT NULL DEFAULT 0,
`last_match` DATETIME DEFAULT NULL,
PRIMARY KEY (`blacklist_id`),
UNIQUE KEY `entry` (`entry_type`, `entry_value`),
KEY `expires_at` (`expires_at`),
KEY `created_by` (`created_by`),
CONSTRAINT `mb_policy_blacklist_created_by` FOREIGN KEY (`created_by`)
REFERENCES `mb_users` (`user_id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Quarantine Tables¶
mb_quarantine¶
Quarantined email messages.
CREATE TABLE `mb_quarantine` (
`quarantine_id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`message_id` VARCHAR(255) NOT NULL,
`envelope_from` VARCHAR(255) DEFAULT NULL,
`envelope_to` VARCHAR(255) DEFAULT NULL,
`header_from` VARCHAR(255) DEFAULT NULL,
`header_to` VARCHAR(255) DEFAULT NULL,
`subject` VARCHAR(500) DEFAULT NULL,
`reason` ENUM('spam','virus','policy','geoip','auth','other') NOT NULL,
`spam_score` DECIMAL(5,2) DEFAULT NULL,
`virus_name` VARCHAR(255) DEFAULT NULL,
`file_path` VARCHAR(500) NOT NULL,
`file_size` INT(11) UNSIGNED NOT NULL,
`quarantined_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`sender_ip` VARCHAR(45) DEFAULT NULL,
`sender_country` VARCHAR(2) DEFAULT NULL,
`spf_result` VARCHAR(20) DEFAULT NULL,
`dkim_result` VARCHAR(20) DEFAULT NULL,
`dmarc_result` VARCHAR(20) DEFAULT NULL,
`status` ENUM('quarantined','released','deleted') NOT NULL DEFAULT 'quarantined',
`released_at` DATETIME DEFAULT NULL,
`released_by` INT(11) UNSIGNED DEFAULT NULL,
PRIMARY KEY (`quarantine_id`),
UNIQUE KEY `message_id` (`message_id`),
KEY `envelope_to` (`envelope_to`),
KEY `quarantined_at` (`quarantined_at`),
KEY `reason` (`reason`),
KEY `status` (`status`),
CONSTRAINT `mb_quarantine_released_by` FOREIGN KEY (`released_by`)
REFERENCES `mb_users` (`user_id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
mb_quarantine_attachments¶
Attachment metadata for quarantined emails.
CREATE TABLE `mb_quarantine_attachments` (
`attachment_id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`quarantine_id` BIGINT(20) UNSIGNED NOT NULL,
`filename` VARCHAR(255) NOT NULL,
`mime_type` VARCHAR(100) DEFAULT NULL,
`file_size` INT(11) UNSIGNED NOT NULL,
`file_hash` VARCHAR(64) DEFAULT NULL,
PRIMARY KEY (`attachment_id`),
KEY `quarantine_id` (`quarantine_id`),
KEY `file_hash` (`file_hash`),
CONSTRAINT `mb_quarantine_attachments_quarantine_id` FOREIGN KEY (`quarantine_id`)
REFERENCES `mb_quarantine` (`quarantine_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Logging Tables¶
mb_log_audit¶
Audit trail for administrative actions.
CREATE TABLE `mb_log_audit` (
`log_id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` INT(11) UNSIGNED DEFAULT NULL,
`action` VARCHAR(100) NOT NULL,
`resource_type` VARCHAR(50) DEFAULT NULL,
`resource_id` VARCHAR(255) DEFAULT NULL,
`details` TEXT DEFAULT NULL,
`ip_address` VARCHAR(45) DEFAULT NULL,
`user_agent` VARCHAR(500) DEFAULT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`log_id`),
KEY `user_id` (`user_id`),
KEY `action` (`action`),
KEY `created_at` (`created_at`),
CONSTRAINT `mb_log_audit_user_id` FOREIGN KEY (`user_id`)
REFERENCES `mb_users` (`user_id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Common actions: - LOGIN, LOGOUT, LOGIN_FAILED - USER_CREATE, USER_UPDATE, USER_DELETE - POLICY_CREATE, POLICY_UPDATE, POLICY_DELETE - QUARANTINE_RELEASE, QUARANTINE_DELETE - SETTINGS_UPDATE, BACKUP_CREATE
mb_log_email¶
Email processing log.
CREATE TABLE `mb_log_email` (
`log_id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`message_id` VARCHAR(255) NOT NULL,
`envelope_from` VARCHAR(255) DEFAULT NULL,
`envelope_to` VARCHAR(255) DEFAULT NULL,
`subject` VARCHAR(500) DEFAULT NULL,
`verdict` ENUM('pass','quarantine','reject','discard') NOT NULL,
`spam_score` DECIMAL(5,2) DEFAULT NULL,
`virus_detected` TINYINT(1) NOT NULL DEFAULT 0,
`processing_time` INT(11) UNSIGNED DEFAULT NULL,
`sender_ip` VARCHAR(45) DEFAULT NULL,
`received_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`log_id`),
KEY `message_id` (`message_id`),
KEY `envelope_to` (`envelope_to`),
KEY `verdict` (`verdict`),
KEY `received_at` (`received_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Statistics Tables¶
mb_stats_daily¶
Daily aggregated statistics.
CREATE TABLE `mb_stats_daily` (
`stat_id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`stat_date` DATE NOT NULL,
`emails_received` INT(11) UNSIGNED NOT NULL DEFAULT 0,
`emails_passed` INT(11) UNSIGNED NOT NULL DEFAULT 0,
`emails_quarantined` INT(11) UNSIGNED NOT NULL DEFAULT 0,
`emails_rejected` INT(11) UNSIGNED NOT NULL DEFAULT 0,
`spam_detected` INT(11) UNSIGNED NOT NULL DEFAULT 0,
`virus_detected` INT(11) UNSIGNED NOT NULL DEFAULT 0,
`avg_spam_score` DECIMAL(5,2) DEFAULT NULL,
`avg_processing_time` INT(11) UNSIGNED DEFAULT NULL,
PRIMARY KEY (`stat_id`),
UNIQUE KEY `stat_date` (`stat_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Settings Tables¶
mb_settings¶
System configuration key-value store.
CREATE TABLE `mb_settings` (
`setting_key` VARCHAR(255) NOT NULL,
`setting_value` TEXT DEFAULT NULL,
`setting_type` ENUM('string','integer','boolean','json') NOT NULL DEFAULT 'string',
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`updated_by` INT(11) UNSIGNED DEFAULT NULL,
PRIMARY KEY (`setting_key`),
KEY `updated_by` (`updated_by`),
CONSTRAINT `mb_settings_updated_by` FOREIGN KEY (`updated_by`)
REFERENCES `mb_users` (`user_id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Common settings: - system.name - System name - system.hostname - FQDN - spam.threshold.pass - Spam pass threshold - antivirus.enabled - AV scanning enabled - geoip.enabled - GeoIP filtering enabled
Internationalization Tables¶
mb_i18n_languages¶
Supported languages.
CREATE TABLE `mb_i18n_languages` (
`language_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`code` VARCHAR(10) NOT NULL,
`name` VARCHAR(100) NOT NULL,
`native_name` VARCHAR(100) NOT NULL,
`enabled` TINYINT(1) NOT NULL DEFAULT 1,
`progress` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`language_id`),
UNIQUE KEY `code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
mb_i18n_strings¶
Translatable strings.
CREATE TABLE `mb_i18n_strings` (
`string_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`string_key` VARCHAR(255) NOT NULL,
`language_code` VARCHAR(10) NOT NULL,
`string_value` TEXT NOT NULL,
`context` VARCHAR(255) DEFAULT NULL,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`string_id`),
UNIQUE KEY `translation` (`string_key`, `language_code`),
KEY `language_code` (`language_code`),
CONSTRAINT `mb_i18n_strings_language` FOREIGN KEY (`language_code`)
REFERENCES `mb_i18n_languages` (`code`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Database Maintenance¶
Optimize Tables¶
Analyze Tables¶
Check Table Sizes¶
SELECT
table_name,
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS size_mb,
table_rows
FROM information_schema.TABLES
WHERE table_schema = 'mailborder'
ORDER BY (data_length + index_length) DESC;
Cleanup Old Data¶
-- Delete old audit logs (>90 days)
DELETE FROM mb_log_audit WHERE created_at < DATE_SUB(NOW(), INTERVAL 90 DAY);
-- Delete old email logs (>30 days)
DELETE FROM mb_log_email WHERE received_at < DATE_SUB(NOW(), INTERVAL 30 DAY);
-- Delete old quarantine (>30 days)
DELETE FROM mb_quarantine
WHERE status = 'deleted'
AND quarantined_at < DATE_SUB(NOW(), INTERVAL 30 DAY);
Backup and Restore¶
Backup Database¶
sudo mysqldump mailborder > /tmp/mailborder-$(date +%Y%m%d).sql
# With compression
sudo mysqldump mailborder | gzip > /tmp/mailborder-$(date +%Y%m%d).sql.gz
Restore Database¶
sudo mysql mailborder < /tmp/mailborder-20250113.sql
# From compressed
gunzip < /tmp/mailborder-20250113.sql.gz | sudo mysql mailborder
Backup Specific Tables¶
# Only user data
sudo mysqldump mailborder mb_users mb_users_meta mb_auth_totp mb_auth_passkeys \
> /tmp/mailborder-users-$(date +%Y%m%d).sql
# Only configuration
sudo mysqldump mailborder mb_settings mb_policy_rules \
> /tmp/mailborder-config-$(date +%Y%m%d).sql
Indexes and Performance¶
Key Indexes¶
All tables have optimized indexes for: - Primary keys (always indexed) - Foreign keys (indexed for join performance) - Frequently queried columns - Date/time columns for range queries
Query Performance¶
Check slow queries:
Explain query:
Add missing index:
See Also¶
- Configuration Files - Configuration reference
- CLI Commands - Database management commands
- Maintenance - Database maintenance procedures