Step 0 — check core before you build. Before writing any mechanism, grep
../tiger-core/CAPABILITIES.md— a generated, CI-checked index of everyTiger_*class and module. Core probably already has it. TigerImage once reimplementedTiger_View_Helper_I18nby hand because this hop was skipped, and lost a property core had designed in. Assume a capability exists until you have grepped the index and confirmed it doesn't.
Orientation for AI agents (and humans) working in this repo. Read this first, then
FEATURES.md — the design-of-record. If you change a decision, update FEATURES.md in the
same change; the "why" is the most perishable part.
A Tiger module — a plugin-layer web application firewall. It installs as
application/modules/tigershield/ inside a Tiger app and self-registers via the module scan. It is
pure PHP at the front-controller layer: no root, no daemon, no build step (built for shared
hosting). See README.md for the pitch, module.json for the manifest.
- Fail-OPEN, always. Any error, outage, missing data, or bug allows the request (and logs it).
Blocking is a positive decision from fresh, local data — never a side effect of a failure. The
whole gate is wrapped in a
Throwablecatch (plugins/Firewall.php). A security layer must never take the site down. - Learn mode is the default. A fresh install logs (
observed) but blocks nothing until an operator flipstiger.tigershield.modetoenforce. Never change the shipped default toenforce. - No per-request network. The hot path is a cache/DB-audit lookup, never a live API call. CrowdSec CAPI is touched only by an out-of-band refresh.
- No heavy dependencies. No CrowdSec SDK — CAPI pull and push (
POST /signals) are plain REST via a built-in lightweight client (FEATURES.md §5).module.jsoncarries nocrowdsec/*requirement. Keep the shared-hosting footprint tiny and pure-PHP. Modules never phone home to third parties; the disclosed, opt-in exception is CrowdSec's CAPI (a security feed), documented in Settings. - Kill-switch / break-glass.
tiger.tigershield.enabled = 0(config, live) disables the plugin instantly; astorage/.tigershield-offfile is the break-glass for a locked-out operator.
- Migrations use timestamp versions (
YYYYMMDDHHMMSS_*.php), never0001— thetiger_migrationledger is one shared bare-version namespace across core + app + all modules, so0001collides with core's and silently no-ops. Tiger_Model_Tablesubclasses must declareprotected $_primary = '<pk>'or the UUID mint targets the wrong column and the insert throws.- Services validate → transaction; save over
/apiwriting to theconfigtier ($config->set('global', '', $key, $value)— 4 args). Hot per-IP counters live in APCu / the lazyoptiontier, never the eagerconfigtable. - Forms are
Zend_Formbuilt from array config; i18n keys are semantic (tigershield.*). - ACL: only the admin surface (controller +
/apiservices) is an ACL resource; the firewall gate is a front-controller plugin that runs for everyone before the ACL and needs no rule.
It's a plain module — drop it into a Tiger app's application/modules/tigershield/, run
vendor/bin/tiger migrate, and it self-registers (the Security screen appears under admin Settings).
The dev/test host is tiger-dev (deploy code there only; operate as ec2-user, no root/sudo). Keep
the box in learn mode and clean up any seeded login / tigershield_event rows after testing.
Bootstrap.php registers the firewall plugin + admin Settings entry + dashboard widget
plugins/Firewall.php Tigershield_Plugin_Firewall — the front-controller gate (fail-open)
services/ RateLimit (APCu), Settings (/api save), Events (/api datatable),
Crowdsec (CAPI client — no SDK), Blocklist (local decision cache the gate reads),
Challenge (captcha interstitial + signed IP-bound clearance cookie),
Waf (request-signature engine over path/query/UA/method — no body scan in v1)
rules/default-waf.php the shipped, curated WAF ruleset (9 categories; data, admin-togglable)
models/Event.php the event log store (UUID PK, standard columns)
widgets/Shield.php the dashboard widget (blocked on the platform widget API — see FEATURES §15.6)
bin/tigershield.php module CLI (cron): refresh | status | provision | enroll — no module cmd registry
migrations/ timestamp-versioned schema
configs/ acl.ini module.ini
views/scripts/admin/ settings + events screens
languages/en/ semantic tigershield.* keys
media/ store/marketing art (icon, banner, screenshots)
storage/cache/tigershield/ runtime cache (blocklist.json, token.json, refresh.lock) — NOT committed