WEB APPLICATION & API PROTECTION · SELF-HOSTED

apPosture WAAP Documentation

The complete reference for every module, function, setting and API endpoint — from the OWASP-CRS data plane to the real-time AI detection engine, API security, deployment, licensing and operations.

OWASP CRS WAFOWASP API Top 10 Real-time AI engineBot & DDoS On-prem · domain-licensedOWASP CRS v4 · self-hosted

Overview & positioning

apPosture is a self-hosted WAAP — Web Application & API Protection platform. It combines, in one on-prem deployment, an OWASP-CRS web application firewall, deep API security (discovery, OWASP API Top 10, posture), bot & DoS defense, and a real-time AI/ML detection engine — all controlled from a single console and licensed by the number of protected domains.

WAFOWASP CRS v4, signature + anomaly scoring
API SecurityDiscovery, OWASP API Top 10, posture, data-flow
AI engine4-layer fusion → explainable verdict + enforcement
Bot & DoSScoring, rDNS, flood, adaptive attack mode

What sets it apart from a commodity WAF: every request flows through a layered pipeline (signatures → anomaly baselines → API-abuse heuristics → bot fingerprinting) whose scores are fused into a single explainable 0–100 risk verdict, and the platform continuously discovers and classifies your APIs rather than only matching attack signatures.

Architecture

Two planes, clean separation:

Internetclients · attackers Edge / CDNscrubbing (optional) DATA PLANEapPosture WAF engine:8080 WAF · :8443 JA3rate-limit · bot · AI verdicts Your apps / APIsprotected upstreams CONTROL PLANEcontrol-plane API · dashboardevaluator loop (~15s): autoban,AI engine, alerts, feeds hot-reload :2019 Adminbrowser → dashboard PostgreSQLinventory · audit · AI Redisdistributed rate-limit
Two planes: a stateless WAF data plane inspects & enforces; the control plane configures it (hot-reload) and runs the evaluator loop. Browser talks only to the dashboard; /api/* is proxied server-side.
Internet ─▶ (edge/CDN scrubbing — optional, for volumetric DDoS)
         ─▶ apPosture WAF engine (OWASP CRS) :8080   ── protected app/API traffic
              │  hot-reload ▲ admin :2019
         ─▶ Dashboard :3010  ──▶  Control-plane API :8090
              └── Postgres · Redis
PortServiceExposure
8080WAF data plane (protected traffic)Public — send app traffic here
8443JA3/JA4 TLS-fingerprint proxyPublic (optional HTTPS entry)
3010Dashboard (management UI)Host-only by default (ADMIN_BIND)
8095Control-plane APIHost-only by default
2019the data plane admin (hot-reload)Internal network only

Installation

One file to edit per deployment: .env (copied from .env.example). The one-command installer generates secrets, builds/loads images, starts the stack and prints the URL + credentials.

PreflightDocker · ports Secretsauto-generate Imagesbuild / load Startcompose up Health wait+ auto-TLS URL + loginprinted sudo ./install.sh --domain waap.example.com -y
One command → full stack with generated secrets and automatic HTTPS. Re-running upgrades in place; --offline installs from a prebuilt bundle.

Single VM / on-prem

sudo ./install.sh                              # online: build from source, localhost-bound
sudo ./install.sh --bind 0.0.0.0 -y            # reachable on the LAN, non-interactive
sudo ./install.sh --domain waap.example.com -y # automatic HTTPS (Let's Encrypt, no certbot)

The installer runs preflight (Docker present, ports free), auto-generates AUTH_SESSION_SECRET, admin password and DB password, waits for health, and prints the dashboard URL, login, and WAF entry point. Re-running ./install.sh performs an in-place upgrade (keeps .env + data). Stop with ./uninstall.sh (add --purge to also delete data volumes).

Air-gapped / no internet

# on an internet-connected machine:
./scripts/build-offline-bundle.sh              # → dist/apposture-offline-*.tar.gz (images + installer)
# on the customer host (offline):
tar xzf apposture-offline-*.tar.gz && cd apposture-offline-* && sudo ./install.sh --offline

Kubernetes

helm install apposture deploy/helm/apposture \
  --namespace apposture --create-namespace \
  --set image.registry=ghcr.io/your-org --set image.tag=1.0.0 \
  --set auth.adminInitialPassword='ChangeMe!' \
  --set auth.sessionSecret="$(openssl rand -base64 36)"

A Terraform module (deploy/terraform) wraps the Helm release. See Scaling & HA.

Automatic HTTPS

install.sh --domain <fqdn> enables a bundled the data plane edge (compose profile tls) that auto-provisions and renews a Let's Encrypt certificate for the dashboard domain — no certbot, no manual nginx. Requires the domain's DNS A/AAAA record to point at the host and ports 80/443 reachable. ACME account/certs persist in the edge-data volume across restarts. Email is taken from ACME_EMAIL (defaults to the admin email).

Configuration reference (.env)

Every environment-specific value is read by docker-compose.yml via ${VAR:-default}.

VariableDefaultPurpose
ADMIN_EMAILSeed admin login email
ADMIN_INITIAL_PASSWORD(generated)First-login password (rotate after)
AUTH_SESSION_SECRET(generated)HMAC key signing session tokens — keep stable
ADMIN_BIND127.0.0.1Host bind for dashboard/API (use 0.0.0.0 for LAN)
WAF_PORT / UI_PORT / API_PORT / JA3_PORT8080/3010/8095/8443Host port mappings
POSTGRES_USER/PASSWORD/DBappostureDatabase credentials
UPSTREAM_CUSTOMER / UPSTREAM_PAYMENTmock-*:8000Default protected upstreams
APP_BRAND / APP_BRAND_SUB / APP_TAGLINEapPosture / WAAPBuild-time white-label branding
DASHBOARD_DOMAIN / ACME_EMAILTLS edge domain + ACME email
Branding is build-time: rebrand = edit .env and rebuild the frontend image.

Licensing

apPosture is licensed by number of protected domains, verified offline (no phone-home). The vendor signs a license with an RSA private key; the product embeds only the public key and verifies the signature locally (pure-stdlib RSA + PKCS#1 v1.5/SHA-256). Customers cannot forge a license or raise their own limit.

Vendor signsprivate key · domains+expiry Customer activatespaste key → /license Verify offlineembedded public key Enforce≤ N domains forged / tampered / expired → drops to trial limit (existing protection keeps running)
No phone-home: the customer's deployment verifies the signed license entirely offline and enforces the domain count locally.

License payload (signed)

{ "customer": "Acme Bank", "max_domains": 5, "issued": …, "expires": …|null, "edition": "enterprise" }

Vendor: issue a license

python3 scripts/license_tool.py keygen                       # one-time: private key + public key to embed
python3 scripts/license_tool.py issue --customer "Acme Bank" --domains 5 --days 365

Customer: activate

Paste the key into Settings → License (or POST /api/license). Status, used/allowed domains, edition and expiry are shown on the License page.

StateBehaviour
ValidUp to max_domains protected sites may be configured
Trial (no key)1 domain allowed
ExpiredDrops to the trial add-limit, but existing protection keeps running
Forged / tamperedRejected (signature fails) → trial limit
Enforcement point: saving more protected Sites than the license permits returns 403. A security product is never bricked by a lapsed license — it just won't add new domains.

How a request is processed

Every request flows through an ordered inspection pipeline at the data-plane edge. Cheap, decisive checks run first (IP reputation, bans) so malicious traffic is dropped before expensive inspection; the AI engine fuses the upstream signals into the final verdict. A request reaches your application only if it survives every stage.

Request 1 · IP rules / bansblocklist · auto-ban · TI 2 · Auth gateper-endpoint · JWT 3 · Rate / floodzones · burst · attack 4 · Bot scoringsignals · rDNS · JA3 5 · WAF / OWASP CRSmulti-phase score 6 · API-abuse checksBOLA · BFLA · stuffing 7 · AI risk fusion → verdictALLOW · RATE_LIMIT · CHALLENGE · BLOCK ALLOW → your app / API BLOCK / CHALLENGE / 429
Ordered enforcement: a ban or signature match short-circuits early; survivors are fused by the AI engine into a graduated verdict. Each stage is independently configurable.

WAF — OWASP CRS v4

apPosture's data-plane WAF is a high-performance embedded inspection engine running the full OWASP Core Rule Set v4. Every request is inspected across multiple rule phases and accumulates a weighted anomaly score; the request is blocked when the score crosses the inbound/outbound threshold — so a single weak signal won't false-positive, while a real attack trips several rules at once.

Core settings

SettingValuesMeaning
modeOn | DetectionOnlyOn blocks; DetectionOnly logs without blocking (tuning)
paranoia_level1–4Higher = stricter rules, more potential false positives
inbound_thresholdint (default 5)Anomaly score at which an inbound request is blocked
outbound_thresholdint (default 4)Outbound (response) block threshold
In DetectionOnly mode the WAF does not block — verify the mode if attacks are passing with 200.

Rule Builder & CRS categories

The Rules page exposes the 13 OWASP CRS protection categories — each can be toggled on/off and edited with per-category path exclusions (keep the protection global but skip it on specific endpoints that legitimately trip it). Exclusions render as the WAF engine ctl:ruleRemoveByTag rules (ids 1009000+).

Category keyProtects against (CRS group)
sqliSQL injection (942)
xssCross-site scripting (941)
rceRemote code execution / web shells (932)
php / java / genericPHP / Java / generic command & SSTI injection (933/944/934)
lfi / rfiLocal / remote file inclusion & SSRF (930/931)
protocol / methodProtocol enforcement, request smuggling, method enforcement (920/921/911)
scanner / fixation / dataleakScanner detection (913), session fixation (943), outbound data-leakage (950s)

Custom rules — a Visual builder (variable · operator · value · action · phase) and a raw seclang (Advanced) editor. Rules can be enabled/disabled/edited individually; disabled rules are kept but not loaded into the engine. Custom-rule ids start at 1000000+.

Rate limiting

Centralized rate limiting via the data-plane rate limiter, optionally Redis-backed for global limits across multiple nodes.

SettingMeaning
rl_enabledMaster switch
rl_events / rl_windowDefault zone: allowed requests per window (e.g. 100 / 1m)
rl_distributedRedis-backed shared counters (required for multi-replica)
rl_zonesPer-path/method zones: {name, match_path, key, events, window}
rl_trust_proxy / rl_trusted_proxiesResolve the real client IP from X-Forwarded-For behind an edge/CDN
endpoint_limitsTight per-IP caps on expensive endpoints: {path, method, events, window}
Keys: ip (per client — XFF-aware when trust-proxy is on), global (one shared bucket), or header:Name (e.g. per API key).

Flood & DoS protection

Layer-7 flood defense, all default-off so they're opt-in:

FeatureSettingsWhat it does
Global flood limitflood_global_enabled/events/windowAggregate cap across all clients (distributed flood)
Per-IP burst capflood_burst_enabled/events/windowShort-window per-IP burst limit
Slowloris timeoutsflood_timeouts_enabled, flood_read_header/body, flood_write, flood_idleServer read/write/idle timeouts vs slow-request attacks
Auto-banflood_autoban_enabled/threshold/window_sec/duration_secTemp-ban IPs that repeatedly hit 429 (with escalation on repeat offense)
Adaptive under-attack modeattack_mode (off/auto/on), attack_spike_factor, attack_min_rpm, attack_burst_*Auto-tightens per-IP limits when current RPM spikes over the learned baseline
JS challengechallenge_enabled, challenge_pathsJS-cookie interstitial gating selected paths (blocks non-JS bots/scrapers)

Auto-banned IPs are enforced at the data-plane edge via a phase-1 client_ip deny (trusted-proxy-aware, so the real client is banned) and recorded in the threat-intel list.

Per-IP 429 countsliding window ≥ threshold?N per window_sec Banclient_ip deny Escalate×duration on repeat Expireunban Evaluated by the control-plane loop every ~15s · banned IPs also sync to threat-intel + SIEM.
Repeat offenders are temp-banned with escalating duration; bans auto-expire. Adaptive attack mode tightens these limits automatically when traffic spikes over baseline.

DDoS — L3/L4 hardening & the honest limit

scripts/ddos-l34.sh applies host-level nftables connection-rate limits + SYN-cookie/sysctl hardening. Honest scope: L7 flood + abusive-client defense is in-product; true volumetric DDoS (tens-to-hundreds of Gbps) saturates the NIC before the data plane sees it and requires upstream edge scrubbing (Cloudflare Magic Transit, AWS Shield, Akamai). apPosture is the L7 layer behind that edge. See Scaling & HA for measured throughput (~80k req/s/node).

Bot defense

A CRS-style composite scoring engine in the WAF engine: multiple weighted signals accumulate into tx.apb_score; the request is blocked when apb_score ≥ bot_score_threshold (default 5) unless it is a verified good bot.

weighted signals (UA · headers · TLS JA3 · behaviour) scanner +6 automation +5 headless +4 crawler +3 missing headers +1..2 Σ apb_score≥ threshold (5)? Verified good bot? (FCrDNS)rDNS → official domain → forward-confirm · else impostor BLOCK monitor / log action = block → deny ≥ threshold (good bots exempt) · action = monitor → log only · impostors always blocked
No single signal blocks a client — the composite score must cross the threshold, so a legit API client with one odd header isn't a false positive while a real bot trips several signals at once.
SignalWeight
Scanner / offensive tool (sqlmap, nuclei…)+6
Automation / HTTP library (curl, python, go-http…) · empty UA+5
Headless browser (HeadlessChrome, Selenium…)+4
Generic crawler / spider+3
Missing Accept / Accept-Language / Accept-Encoding+2 / +2 / +1

Good-bot verification (anti-spoofing)

A User-Agent claiming to be Googlebot is trivially forged. With bot_verify_rdns enabled, each claimed crawler is checked by forward-confirmed reverse DNS (FCrDNS): reverse-DNS the IP → confirm the hostname is on the crawler's official domain → forward-DNS it back to the same IP. A claim that fails is an impostor. POST /api/bots/verify does an on-demand check; the engine auto-blocks impostors when bot_action=block.

SettingMeaning
bot_actionmonitor (log) | block (deny ≥ threshold)
bot_score_thresholdComposite block threshold (default 5)
bot_allow_verifiedExempt verified good bots (Googlebot/Bingbot…)
bot_verify_rdnsFCrDNS verification (catch spoofed crawlers)
bot_check_headers / bot_block_automationHeader heuristics / score HTTP libraries
bot_ja3_blocklistManual hard-block by JA3 fingerprint hash

The JA3/JA4 TLS-fingerprint proxy (:8443) captures fingerprints so a bot can be blocked even behind a spoofed User-Agent.

Real-time AI detection engine

The engine (app/aiengine.py) scores the live request stream server-side and fuses four detection layers into a single explainable verdict. It maintains online EWMA population baselines (mean+variance per feature, snapshotted to /data/ai_baselines.json so they survive restarts and adapt to drift) and scores each client by z-score against the live population.

Requestlive stream Signature (CRS hits) ML anomaly (z-score/EWMA) API abuse (BOLA/BFLA/stuffing) Bot / automation Weighted fusionΣ wᵢ·layerᵢ (normalized)→ risk 0–100 Verdict+ confidence+ explainablesignals
Every client is scored by four independent layers in parallel; their scores are fused (with tunable, normalized weights) into one explainable 0–100 risk verdict.

Detection layers

LayerWhat it scores
signatureRequests already blocked by OWASP-CRS (deterministic ground truth)
anomalyz-score of request-rate, payload entropy, parameter cardinality, error rate, 404 scanning
api_abuseBOLA (object-ID enumeration), BFLA (admin w/o auth), credential stuffing
botAutomation/scanner UA, empty UA, User-Agent rotation

Fusion → verdict ladder

0456080100 ALLOW RATE_LIMIT CHALLENGE BLOCK tight per-IPthrottle 15/10s JS interstitial(bots stall) auto-ban(deny) Cutoffs derive from the configurable block threshold (default 80); enforced when ai_action = block.
The fused risk score maps to a graduated, enforced response — friction scales with confidence instead of a single allow/deny.

Risk = weighted sum of the four layers (weights normalized) → 0–100, with a confidence value and a list of contributing signals (layer, name, points, evidence) so every score is explainable. The verdict maps to a graduated, enforced response:

VerdictThresholdEnforcement (when ai_action=block)
BLOCKai_risk_threshold (default 80)a data-plane client_ip deny (auto-ban)
CHALLENGE≥ threshold−20JS interstitial for the IP on all paths (real browsers pass, bots stall)
RATE_LIMIT≥ threshold−35Tight per-IP throttle zone (15 req / 10s)
ALLOWbelowPass
SettingMeaning
ai_enabledMaster switch (scores in monitor mode too)
ai_actionmonitor (score/log) | block (enforce the ladder)
ai_risk_thresholdBLOCK cutoff 0–100; challenge/rate-limit derive from it
ai_window_secLive scoring window (default 300s)
ai_w_signature/anomaly/api_abuse/botFusion weights (tunable via sliders; normalized live)
ai_block_secondsEnforcement duration per offender

SOC console & attack simulator

The /soc page is a live SOC command-center: RPS / clients / high-risk KPIs, a verdict-distribution donut, attack-type (dominant layer) bars, the live client risk stream (click any row for the full explainability drawer with per-layer scores, signals and OWASP-API/MITRE ATT&CK mapping), an adaptive policy panel (engine on/off, action, risk threshold, layer-weight sliders) and a persisted detection history timeline.

The attack simulator injects real malicious request waves through the WAF (POST /api/ai/simulate — sqli, xss, cred_stuffing, bola, l7_flood, anomaly) so the engine + the WAF engine visibly react; verdicts are recorded in the history (deduped per IP ~5 min, pruned by the retention policy).

A public, no-auth marketing demo of the engine (in-browser simulation) runs at /demo.

API discovery & inventory

1 · Discoverendpoints from traffic 2 · Classifydata sensitivity + risk 3 · Posture-gapshadow · zombie · spec 4 · ProtectOWASP API · auth · AI continuous loop — newly discovered endpoints are classified, posture-checked and protected automatically
apPosture's API-security lifecycle: it continuously discovers and classifies your APIs, not just matches attack signatures.

Endpoints are discovered automatically from WAF traffic (and from gateway connectors / agentless ingestion). Each endpoint is templatized (/orders/42/orders/{id}) and assigned a risk score (0–5) + band from signals: reachable-without-auth, data sensitivity, state-changing method, sensitive path keyword, traffic volume. The Inventory page shows access type, auth type, hits, first/last seen, shadow flag and data classification, with CSV export. GET /api/inventory · GET /api/inventory/stats.

Per-endpoint data classification

Each endpoint is classified by the sensitivity of data it handles, derived from PII detection:

LevelData categoriesRegulations
RestrictedPayment card (PAN), National ID/SSN, auth token/secretPCI-DSS, Secret
ConfidentialEmail, phone, IBANGDPR, PII, PCI-DSS
InternalNo PII but a sensitive path keyword (admin/auth/account)
PublicNone detected

Classification feeds the risk score (restricted data weights highest) and the Inventory "unprotected sensitive" KPI (sensitive endpoint reachable without auth).

Shadow / zombie & spec posture-gap

Upload an OpenAPI spec (Inventory page or POST /api/spec) to enable spec posture analysis (GET /api/posture-gaps):

OWASP API Top 10 — real-time BOLA/BFLA

The API-threats detector finds and (optionally) blocks business-logic abuse in real time:

SettingMeaning
api_protect_modemonitor | block
api_bola_threshold / api_bola_window_secDistinct objects / admin hits within the window to trigger
api_block_secondsHow long to block an offender

In block mode the offender's real client IP is auto-blocked via the data-plane client_ip deny used by flood auto-ban. Findings surface on the API Threats page and via GET /api/api-threats.

Data-flow mapping & east-west

The Data Flow page (GET /api/data-flow) maps which sensitive data categories flow through which endpoints and protected sites, and flags unprotected flows (sensitive endpoint reachable without auth). The East-West page (GET /api/east-west, POST /api/ingest/mesh) ingests service-to-service (internal) traffic from a sidecar / service mesh, builds a caller→callee map, and flags unauthenticated internal calls (a zero-trust gap) — internal endpoints also flow into the inventory.

web orders checkout paymentsPAN · restricted users-db APIPII · confidential auth ✓ no auth ✕ unauth → PII Solid = authenticated call · red = unauthenticated internal call (flagged). Node colour = data classification of the callee.
East-west visibility: which services call which, over which endpoints, carrying what data — and where internal calls reach sensitive data without authentication.

Auth enforcement (JWT & per-endpoint)

Enforces authentication on protected paths at the WAF edge:

SettingMeaning
auth_enabledMaster switch
auth_protected_pathsPath prefixes requiring a credential
auth_alg / auth_secret / auth_jwks_urlJWT algorithm, HS256 key, or JWKS endpoint

Shift-left API testing (CI/CD)

Run an API security gate before deploy. POST /api/ci-scan statically scans an OpenAPI spec (no live traffic) and returns a pass/fail verdict against gates (max_high / max_medium) plus a posture score and per-finding rules: sensitive-no-auth, unprotected-write, bola-risk, missing-auth, bola-review.

# pipeline gate — exit 1 on breach:
APPOSTURE_URL=https://waap.example.com APPOSTURE_TOKEN=<token> \
  ./scripts/apposture-scan.sh openapi.json 0 5     # max_high=0, max_medium=5

A ready-to-use GitHub Actions step ships in scripts/apposture-scan.github-actions.yml (needs only curl + python3).

Gateway connectors & ingestion

Pull traffic into discovery/detection without inline proxying. POST /api/gateway/{type} normalizes each gateway's access-log schema into the engine:

TypeSource
kongKong HTTP Log plugin
awsAWS API Gateway access log
nginxnginx JSON access log
apigeeApigee analytics record
genericGeneric {method,uri,status,ip,user_agent}

Generic agentless ingestion: POST /api/ingest accepts an events array or a HAR archive.

Schema enforcement & sensitive data

Schema enforcement (schema_action: ignore/alert/block) — positive security: a request whose method+path is not in the uploaded OpenAPI spec can be alerted or blocked. Sensitive datapii_detect flags PII (card/email/token/IBAN/SSN/phone) in responses; pii_mask masks card numbers in responses (the response-rewrite filter); privacy_mode masks PII in stored data (metadata-only). GraphQLgraphql_protect blocks introspection (__schema/__type).

Threat intelligence feed

Auto-pull IP reputation and block at the WAF edge. Built-in TOR exit-node blocking (ti_tor_enabled) plus an optional custom bad-IP feed URL (ti_feed_url, newline or JSON), refreshed on ti_feed_interval_hours. Synced IPs render as a phase-1 deny (rule 1006000). Manual import + one-click block of live-traffic matches is on the Threat Intel page (GET/POST /api/threat-intel).

Credential abuse / ATO

The Credentials page scores account-takeover risk per source: login attempts, failure rate, IP/UA spread, distinct accounts targeted. Password policy integrates HIBP (policy_block_pwned) via k-anonymity to reject known-breached passwords at user create/update.

Virtual patching & managed feed

Virtual patching — a CVE catalog (rule ids 1007xxx) lets you apply a virtual patch (block a known exploit pattern) without touching the app; GET /api/vpatch/catalog, POST /api/vpatch. Managed rule/threat feed (MRF) — scheduled puller for a curated rule + IP-reputation feed (mrf_enabled, mrf_url, mrf_interval_hours); empty URL uses the bundled apPosture baseline.

Protected Sites

Path-prefix → upstream routing. sites = [{name, prefix, upstream, protected}]; empty falls back to the bundled customer/payment demo upstreams. The number of configured sites is the licensed "domain" count — see Licensing. Subset-editing pages preserve the full settings object (spread ...loaded before overriding) so unrelated fields aren't wiped on save.

Integrations — SIEM, SSO, MRF

SIEM connectors (siem_connectors) — multi-target export: Splunk HEC, Elasticsearch, Microsoft Sentinel, syslog (RFC5424), CEF, generic webhook. Security events (auto-bans, AI blocks, API blocks) emit asynchronously. SSO / OIDC (sso_*) — authorization-code login ("Sign in with SSO"): issuer discovery, client id/secret, redirect URI, default role, allowed email domain. Notifications — legacy single webhook (siem_webhook_url) kept for compatibility.

Alerting

Threshold rules over live traffic → multi-channel notifications. Each rule: {metric, threshold, window_min, type, target, cooldown_min}. Metrics: blocked, requests, errors, block-rate %. Channels: Slack, generic webhook, PagerDuty Events v2. Evaluated continuously by the worker loop with per-rule cooldown. POST /api/alerts/test fires a test.

Compliance reporting

The Compliance page reports live posture against PCI-DSS, GDPR, SOC 2 and OWASP frameworks — each control's status is derived from real config signals (WAF on, TLS, auth enforcement, retention, audit, PII handling, etc.) so the report reflects the actual running configuration. GET /api/compliance.

Governance — audit, config versioning, policy, tuning

Users & RBAC

Real login: stdlib-only PBKDF2-HMAC-SHA256 passwords + HMAC-SHA256 signed session tokens; TOTP 2FA (RFC 6238). Roles: admin (full — settings, IAM, governance), operator (tune WAF/flood/rules; no governance/IAM), viewer (read-only). The role comes from the signed token, never a header. Mutating endpoints enforce role via _require(request, *roles); governance fields (IAM, integrations, license…) are admin-only. Users page: provisioning, inline role change, suspend/activate, per-user activity, roles & permissions matrix, password reset, 2FA enrollment.

Analytics & traffic

Dashboard, Analytics and Traffic pages provide Cloudflare/Swetrix-style telemetry: requests-over-time (1h/6h/24h/7d), block rate, top attacked endpoints, top attacker sources, attack-type distribution, live request stream with cursor pagination. All absolute times display in Baku time (GMT+4); storage stays UTC.

API reference (selected)

Method · PathPurpose
POST/api/loginAuthenticate → session token (OTP step if 2FA)
GET/api/settings · PUTRead / update configuration (role-gated, license-enforced)
POST/api/applyRender & push config to the data plane (hot-reload)
GET/api/ai/soc · /api/ai/historyLive AI scoring + persisted verdict timeline
POST/api/ai/simulateInject an attack wave through the WAF
GET/api/inventory · /api/posture-gaps · /api/data-flow · /api/east-westAPI security views
POST/api/ci-scanShift-left OpenAPI security scan (pass/fail)
POST/api/gateway/{type} · /api/ingest · /api/ingest/meshConnector / agentless / east-west ingestion
GET/api/license · POSTLicense status / activation (admin)
GET/api/audit · /api/compliance · /api/config/versionsGovernance & compliance
All mutating endpoints require a Bearer session token; many GET analytics endpoints are read through the dashboard's server-side proxy. Audit attribution comes from the signed token.

Scaling & HA

Internet Edge / CDN scrubbingvolumetric L3/4 (Gbps) — required for true DDoS Load Balancer WAF data plane #1stateless · ~80k req/s WAF data plane #2stateless · ~80k req/s WAF data plane #N→ ~80k×N req/s Redisshared rate-limit Control plane ×1 (single-writer evaluators) · PostgreSQL
The stateless WAF data plane scales horizontally behind the LB (≈80k×N req/s) with Redis-shared rate limits; one control plane owns the evaluator loops. Volumetric DDoS is handled upstream at the edge.

Measured single-node throughput (16 vCPU, the full WAF inspection path): ~80,000 req/s, ~1.1 ms p50 / ~6 ms p99 — and a WAF block costs the same as a pass (the limit is CPU, not rule evaluation). Scale L7 capacity horizontally: the stateless data plane runs N replicas behind the LoadBalancer for ~80k × N req/s, with Redis-distributed rate limiting (rl_distributed) for global limits and a single control plane (the evaluator loops are single-writer). Volumetric (Gbps) DDoS needs upstream edge scrubbing. Full detail in deploy/SCALING.md.

Backup & migration

Full-state migration carries DB + config volume + .env: scripts/backup.ps1 (Windows) → apposture-backup/ (db.sql via pg_dump --clean --if-exists, control-data.tgz, env.bak); scripts/restore.sh (on the VM) builds, starts and restores it in one command. DB restore is idempotent. Runbook: MIGRATION.md.

Threat coverage matrix

Which module catches which threat, and the default response. Most attacks are caught by several layers — the AI engine fuses them so a multi-signal attacker scores higher than any single check would show.

ThreatPrimary module(s)Default actionOWASP
SQL injectionWAF (CRS 942) · AI signature layerBlock (anomaly ≥ threshold)A03
XSSWAF (CRS 941)BlockA03
RCE / command injection / web shellsWAF (CRS 932/934)BlockA03
Path traversal / LFI / RFI / SSRFWAF (CRS 930/931)BlockA01/A10
BOLA / IDOR (object enumeration)API-abuse detector · AI api_abuse layerMonitor → auto-block offenderAPI1
BFLA (admin without auth)API-abuse detector · auth gateMonitor → block · 401API5
Broken auth / unauthenticated sensitive endpointPer-endpoint auth · posture-gap401 · flaggedAPI2/API3
Credential stuffing / ATOCredential-abuse · AI api_abuse · HIBPScore → block · reject pwnedAPI2
Scrapers / automation / scannersBot scoring · JA3/JA4Block ≥ thresholdAPI6
Spoofed good bot (fake Googlebot)FCrDNS verificationBlock impostor
L7 flood / slow-lorisFlood limits · timeouts · auto-ban · attack mode429 → temp-ban
Volumetric DDoS (Gbps)Edge/CDN scrubbing (upstream)Out of scope for the node
Shadow / undocumented APIDiscovery vs OpenAPI specFlagged (shadow)API9
Sensitive-data exposurePII detection · data classification · maskingDetect · mask · flagAPI3
Zero-day / novel anomalyAI anomaly layer (EWMA z-score)Risk verdict → challenge/block
Known CVE exploit patternVirtual patching · managed feedBlock (virtual patch)

Glossary

TermMeaning
WAAPWeb Application & API Protection — the category combining WAF, API security, bot & DDoS defense in one platform.
Data planeThe inline component that terminates and inspects traffic and enforces verdicts.
Control planeThe management component (dashboard + API + evaluator loop) that configures the data plane.
OWASP CRSOWASP Core Rule Set — the industry-standard signature rule set for web attacks.
Paranoia levelCRS strictness 1–4; higher catches more but may raise false positives.
Anomaly scoreWeighted sum of matched rules; a request blocks when it crosses the threshold (no single weak signal trips it).
BOLA / IDORBroken Object Level Authorization — a client accessing objects it shouldn't, e.g. enumerating IDs (API1).
BFLABroken Function Level Authorization — reaching privileged/admin functions without authorization (API5).
EWMAExponentially-Weighted Moving Average — how the AI engine keeps adaptive, drift-aware behavioural baselines.
z-scoreHow many standard deviations a client's behaviour is from the live population baseline.
FCrDNSForward-Confirmed reverse DNS — verifies a crawler is genuine (rDNS → domain → forward-DNS back to the IP).
JA3 / JA4TLS-handshake fingerprints that identify a client even behind a spoofed User-Agent.
Shadow / Zombie APIShadow = live but undocumented; Zombie = documented but never seen in traffic.
Virtual patchA WAF rule that blocks a known exploit pattern without changing the application.
Verdict ladderThe graduated AI response: ALLOW → RATE_LIMIT → CHALLENGE → BLOCK by risk.
East-west trafficInternal service-to-service API calls (vs north-south = external edge traffic).
apPosture WAAP — Web Application & API Protection. Self-hosted · domain-licensed.
This documentation reflects the running platform — a self-hosted WAAP built on the OWASP Core Rule Set v4, with PostgreSQL + Redis.