This page lists the storage the module uses, for administrators who script against the database or Omeka's settings.
Settings keys
All settings are stored in Omeka's global setting table with the prefix traffic_guard_.
| Key | Type | Default | Notes |
|---|---|---|---|
traffic_guard_enabled |
bool | true |
Master switch. |
traffic_guard_mode |
string | normal |
One of normal, elevated, under_attack. Any other value is treated as normal. |
traffic_guard_auto_ban |
bool | true |
Ban on block decisions. |
traffic_guard_auto_ban_ttl_seconds |
int | 3600 |
Auto-ban duration. Values below 60 are raised to 60. The Settings page edits this in hours. |
traffic_guard_trust_xff |
bool | false |
Use X-Forwarded-For. |
traffic_guard_retention_days |
int | 30 |
Event retention; 0 keeps forever. |
traffic_guard_auto_allowlist_private |
bool | true |
Exempt private and reserved ranges. |
traffic_guard_log_allow |
bool | false |
Log allowed requests. |
traffic_guard_per_page |
int | 50 |
Dashboard page size; minimum 10. |
traffic_guard_rate_limits |
JSON string | see below | Limits per mode and category. |
traffic_guard_thresholds |
JSON string | see below | Throttle and block scores per mode. |
traffic_guard_window_seconds |
JSON string | {"global":60,"search":60,"api":60,"item":60} |
Window length per category. Written at install time; there is no admin form for it. |
Default traffic_guard_rate_limits:
{
"normal": {"global": 120, "search": 20, "api": 60, "item": 80},
"elevated": {"global": 80, "search": 10, "api": 40, "item": 50},
"under_attack": {"global": 40, "search": 5, "api": 20, "item": 25}
}
Default traffic_guard_thresholds:
{
"normal": {"throttle": 40, "block": 70},
"elevated": {"throttle": 30, "block": 55},
"under_attack": {"throttle": 20, "block": 40}
}
If a mode or category is missing from the stored JSON, the built-in default for that entry is used.
Database tables
All tables use InnoDB with utf8mb4_unicode_ci. Timestamps are stored in UTC.
tg_rate_windows
| Column | Type | Notes |
|---|---|---|
ip_address |
VARCHAR(45) | Client IP (IPv4 or IPv6). |
category |
VARCHAR(20) | global, search, api or item. |
window_start |
DATETIME | Start of the 60-second window. |
hit_count |
INT UNSIGNED | Requests counted in the window. |
Primary key (ip_address, category, window_start).
tg_bans
| Column | Type | Notes |
|---|---|---|
id |
INT UNSIGNED | Auto-increment. |
ip_address |
VARCHAR(45) | Banned IP. |
reason |
VARCHAR(128) | auto_score or manual. |
banned_at |
DATETIME | When the ban was placed. |
expires_at |
DATETIME NULL | NULL means permanent. |
unbanned_at |
DATETIME NULL | Set when a ban is lifted. |
banned_by |
VARCHAR(128) NULL | system for automatic bans. |
notes |
TEXT NULL | Notes entered on a manual ban. |
A ban is active when unbanned_at IS NULL AND (expires_at IS NULL OR expires_at > NOW()).
tg_events
| Column | Type | Notes |
|---|---|---|
id |
BIGINT UNSIGNED | Auto-increment. |
occurred_at |
DATETIME | Inspection time. |
ip_address |
VARCHAR(45) | Client IP. |
user_agent |
VARCHAR(512) NULL | Truncated to 512 characters. |
request_path |
VARCHAR(512) | Path without query string. |
category |
VARCHAR(20) | Path category, or banned. |
risk_score |
TINYINT UNSIGNED | 0 to 100. |
decision |
VARCHAR(12) | allow, throttle or block. |
signals |
JSON NULL | Map of signal name to weight, for example {"bot_ua_pattern":30,"rate_exceeded_hard":30}. |
Indexed on ip_address, occurred_at and decision.
tg_ua_rules
| Column | Type | Notes |
|---|---|---|
id |
INT UNSIGNED | Auto-increment. |
pattern |
VARCHAR(512) | Substring or regular expression without delimiters. |
match_type |
VARCHAR(10) | substring or regex. |
action |
VARCHAR(10) | allow or block. |
priority |
SMALLINT | Lower is checked first; default 100. |
label |
VARCHAR(128) NULL | Optional description. |
enabled |
TINYINT(1) | Only enabled rules are evaluated. The admin pages always create rules enabled; set to 0 in SQL to disable one without deleting it. |
created_at |
DATETIME | Creation time. |
Enabled rules are cached in memory per PHP worker. The cache is cleared when a rule is added or deleted through the admin pages; after editing the table directly, restart PHP-FPM (or wait for workers to recycle) for the change to apply everywhere.
tg_ip_allowlist
| Column | Type | Notes |
|---|---|---|
id |
INT UNSIGNED | Auto-increment. |
ip_cidr |
VARCHAR(50) | Single IP or CIDR; unique. |
label |
VARCHAR(128) NULL | Optional description. |
added_at |
DATETIME | When added. |
added_by |
VARCHAR(128) NULL | Who added it. |
The allowlist is read from the database on every inspected request, so changes apply immediately.
Useful queries
Blocked requests per hour for the last day:
SELECT DATE_FORMAT(occurred_at, '%Y-%m-%d %H:00') AS hour, COUNT(*) AS blocked
FROM tg_events
WHERE decision = 'block' AND occurred_at >= NOW() - INTERVAL 1 DAY
GROUP BY hour ORDER BY hour;
Most common user agents among throttled and blocked requests:
SELECT LEFT(user_agent, 80) AS ua, COUNT(*) AS hits
FROM tg_events
WHERE decision <> 'allow'
GROUP BY ua ORDER BY hits DESC LIMIT 20;
Services for developers
The module registers four services in the service manager: TrafficGuard\Service\IpInspector (IP extraction, CIDR matching, path classification), TrafficGuard\Service\RateLimiter, TrafficGuard\Service\RiskScorer and TrafficGuard\Service\RequestGuard. RequestGuard::inspect($request, $settings) returns allow, throttle or block, and RequestGuard::issueBan($ip, $reason, $ttlSeconds) and liftBan($banId) can be called from other modules. The RequestGuard class declares two protected stub methods, applyCloudflareHeaders() and lookupExternalReputation(), but the inspection pipeline does not call them yet, so overriding them has no effect in this version.