> ## Documentation Index
> Fetch the complete documentation index at: https://docs.verbex.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Web Application Firewall Guideline

Verbex uses a **Web Application Firewall (WAF)** to protect platform APIs and services from common web threats and malicious traffic.

### Organization-Specific Protection

* WAF rules are applied **per organization** to ensure isolated and customizable protection.
* Each organization can define its own WAF policy based on its security requirements.

### Policy Modes

Organizations can configure their WAF policy in one of the following modes:

* **Default Deny with Allow Rules**
  * All traffic is denied by default.
  * Only explicitly allowed requests are permitted.
* **Default Allow with Deny Rules**
  * All traffic is allowed by default.
  * Only explicitly denied requests are blocked.

 

## **Quick Start**

### **1. Configure WAF for Your Organization**

```

# Create WAF configuration (ALLOW mode - blocklist)curl -X POST /v1/waf/config \
  -H "X-User-Org-Id: your-org-id" \
  -H "Content-Type: application/json" \
  -d '{
    "default_policy": "ALLOW",
    "status": "ACTIVE"
  }'
```

### **2. Add IP Rules**

```

# Block a malicious IPcurl -X POST /v1/waf/rules \
  -H "X-User-Org-Id: your-org-id" \
  -H "Content-Type: application/json" \
  -d '{
    "ip_address": "198.51.100.50",
    "action": "BLOCK",
    "reason": "Suspicious activity detected"
  }'
```

### **3. Check IP Access**

```

# Check if an IP should be blockedcurl -X POST /v1/waf/check \
  -H "X-User-Org-Id: your-org-id" \
  -H "Content-Type: application/json" \
  -d '{"ip_address": "198.51.100.50"}'
```

**Response:**

```

{  "ip_address": "198.51.100.50",  "is_blocked": true,  "action": "BLOCK",  "matched_rule": "rule-id-here"}
```

***

## **Core Concepts**

### **Default Policy**

 

| Policy  | Mode      | Behavior                                             |
| :------ | :-------- | :--------------------------------------------------- |
| `ALLOW` | Blocklist | Allow all IPs by default, block specific threats     |
| `DENY`  | Allowlist | Block all IPs by default, allow specific trusted IPs |

 

### **IP Types**

 

| Type    | Format    | Example          | Description              |
| :------ | :-------- | :--------------- | :----------------------- |
| `EXACT` | Single IP | `192.168.1.100`  | Match one specific IP    |
| `CIDR`  | IP Range  | `192.168.1.0/24` | Match IP range (256 IPs) |

 

### **CIDR Notation**

```

IP_ADDRESS / PREFIX_LENGTH
```

 

| CIDR  | Subnet Mask     | IP Range                | Total IPs  |
| :---- | :-------------- | :---------------------- | :--------- |
| `/32` | 255.255.255.255 | Single IP               | 1          |
| `/24` | 255.255.255.0   | x.x.x.0 - x.x.x.255     | 256        |
| `/16` | 255.255.0.0     | x.x.0.0 - x.x.255.255   | 65,536     |
| `/8`  | 255.0.0.0       | x.0.0.0 - x.255.255.255 | 16,777,216 |

 

### **Rule Priority (Specificity)**

When multiple rules match, the most specific wins:

```

/32 (exact) > /24 > /16 > /8 (broadest)
```

**Example:**

* Rule 1: `BLOCK 10.0.0.0/8`
* Rule 2: `ALLOW 10.1.2.3/32`

Result: IP `10.1.2.3` is **ALLOWED** (`/32` beats `/8`)

### **WAF Status**

 

| Status     | Behavior                                          |
| :--------- | :------------------------------------------------ |
| `ACTIVE`   | WAF rules are enforced                            |
| `INACTIVE` | All IPs allowed (bypass mode for troubleshooting) |

 

***

## **API Reference**

### **Endpoints**

 

| Endpoint                       | Method | Description                   |
| :----------------------------- | :----- | :---------------------------- |
| `/v1/waf/check`                | POST   | Check if IP should be blocked |
| `/v1/waf/config`               | GET    | Get WAF configuration         |
| `/v1/waf/config`               | POST   | Create WAF configuration      |
| `/v1/waf/config`               | PUT    | Update WAF configuration      |
| `/v1/waf/rules`                | POST   | Create a rule                 |
| `/v1/waf/rules/paginated`      | GET    | List rules with pagination    |
| `/v1/waf/rules/{ruleId}`       | GET    | Get rule by ID                |
| `/v1/waf/rules/{ruleId}`       | PUT    | Update rule                   |
| `/v1/waf/rules/{ruleId}`       | DELETE | Delete rule                   |
| `/v1/waf/rules/activate-all`   | POST   | Activate all rules            |
| `/v1/waf/rules/deactivate-all` | POST   | Deactivate all rules          |
| `/v1/waf/cache/{orgId}`        | DELETE | Clear cache                   |
| `/v1/waf/cache/{orgId}/sync`   | POST   | Sync cache from database      |

 

### **Request Headers**

 

| Header          | Required | Description                      |
| :-------------- | :------- | :------------------------------- |
| `X-User-Org-Id` | Yes      | Organization identifier          |
| `X-User-ID`     | No       | User ID for audit trail          |
| `X-Client-IP`   | No       | Client IP for auto-rule creation |
| `X-Trace-Id`    | No       | Request correlation ID           |

 

### **Data Models**

**WAF Configuration:**

```
{
  "org_id": "string",
  "default_policy": "ALLOW | DENY",
  "status": "ACTIVE | INACTIVE",
  "created_at": "2026-02-17T10:00:00Z",
  "updated_at": "2026-02-17T10:00:00Z"
}
```

**IP Access Rule:**

```
{
  "id": "string",
  "org_id": "string",
  "ip_address": "192.168.1.100",
  "ip_type": "EXACT | CIDR",
  "action": "ALLOW | BLOCK",
  "reason": "string",
  "is_active": true,
  "expires_at": "2026-03-17T00:00:00Z",
  "notes": "string",
  "created_at": "2026-02-17T10:00:00Z",
  "updated_at": "2026-02-17T10:00:00Z"
}
```

**IP Check Response:**

```
{
  "ip_address": "192.168.1.100",
  "is_blocked": false,
  "action": "ALLOW",
  "matched_rule": "rule-id or null",
  "default_policy": "ALLOW (if no rule matched)"
}
```

***

## **Configuration**

### **Application Properties**

```
waf:
  enabled: true                    # Enable/disable WAF
  default-policy: ALLOW            # System-wide default
  cache-ttl: PT1H                  # Redis cache TTL
  max-rules-per-org: 1000          # Max rules per org
  debug-logging: false             # Verbose logging

  negative-cache:
    enabled: true                  # Cache "no rule" results
    ttl: PT5M                      # Negative cache TTL

  cache-warmup:
    enabled: true                  # Preload on startup
    delay: PT5S                    # Warmup delay
    batch-size: 100                # Orgs per batch
```

### **Limits**

 

| Resource               | Limit     |
| :--------------------- | :-------- |
| Rules per organization | 1,000     |
| Batch operations       | 100 rules |
| Pagination max size    | 100 items |

 

***

## **Usage Examples**

### **Example 1: Blocklist Mode (Default: ALLOW)**

Block specific threats while allowing all other traffic.

```

# 1. Configure ALLOW modecurl -X POST /v1/waf/config \
  -H "X-User-Org-Id: acme-corp" \
  -d '{"default_policy": "ALLOW", "status": "ACTIVE"}'# 2. Block attacker IPcurl -X POST /v1/waf/rules \
  -H "X-User-Org-Id: acme-corp" \
  -d '{"ip_address": "198.51.100.50", "action": "BLOCK", "reason": "Brute force"}'
```

**Result:**

 

| IP              | Rule Match              | Access  |
| :-------------- | :---------------------- | :------ |
| `203.0.113.10`  | No rule → Default ALLOW | Allowed |
| `198.51.100.50` | BLOCK rule              | Blocked |

 

### **Example 2: Allowlist Mode (Default: DENY)**

Only allow specific trusted IPs.

```

# 1. Configure DENY mode (auto-creates rule for your IP)curl -X POST /v1/waf/config \
  -H "X-User-Org-Id: acme-corp" \
  -H "X-Client-IP: 203.0.113.10" \
  -d '{"default_policy": "DENY", "status": "ACTIVE"}'# 2. Allow office networkcurl -X POST /v1/waf/rules \
  -H "X-User-Org-Id: acme-corp" \
  -d '{"ip_address": "192.168.1.0/24", "action": "ALLOW", "reason": "Office network"}'
```

**Result:**

 

| IP             | Rule Match             | Access  |
| :------------- | :--------------------- | :------ |
| `203.0.113.10` | Auto-created ALLOW     | Allowed |
| `192.168.1.50` | CIDR ALLOW             | Allowed |
| `10.0.0.99`    | No rule → Default DENY | Blocked |

 

### **Example 3: Temporary Contractor Access**

```

# Create rule with expirationcurl -X POST /v1/waf/rules \
  -H "X-User-Org-Id: acme-corp" \
  -d '{
    "ip_address": "45.67.89.100",
    "action": "ALLOW",
    "reason": "Contractor - Project Alpha",
    "expires_at": "2026-03-17T00:00:00Z",
    "notes": "Contract #CON-2026-042"
  }'
```

Rule auto-expires and is cleaned up by hourly job.

### **Example 4: CIDR with Exception**

Block a range but allow specific IP within it.

```

# Block suspicious rangecurl -X POST /v1/waf/rules \
  -H "X-User-Org-Id: acme-corp" \
  -d '{"ip_address": "198.51.100.0/24", "action": "BLOCK", "reason": "Malicious range"}'# Allow trusted partner in that rangecurl -X POST /v1/waf/rules \
  -H "X-User-Org-Id: acme-corp" \
  -d '{"ip_address": "198.51.100.75", "action": "ALLOW", "reason": "Trusted partner"}'
```

**Result:** `198.51.100.75` is ALLOWED (`/32` beats `/24`)

***

## **Architecture**

### **Multi-Tenant Design**

WAF operates at the organization level with complete isolation.

```

flowchart TB
    subgraph Platform["Platform"]
        subgraph Org1["Organization A"]
            WAF1["WAF Config + Rules"]
        end
        subgraph Org2["Organization B"]
            WAF2["WAF Config + Rules"]
        end
    end
```

### **IP Check Flow**

### **Redis Key Patterns**

 

| Key                   | Type   | Purpose        |
| :-------------------- | :----- | :------------- |
| `waf:exact:{orgId}`   | Hash   | Exact IP rules |
| `waf:cidr:{orgId}`    | Hash   | CIDR rules     |
| `waf:config:{orgId}`  | String | Default policy |
| `waf:nc:{orgId}:{ip}` | String | Negative cache |

 

### **Storage**

* **MongoDB**: Source of truth for config and rules
* **Redis**: High-performance cache layer

***

## **Performance**

### **Response Times**

 

| Scenario                   | Latency |
| :------------------------- | :------ |
| Redis hit (exact/CIDR)     | \~1ms   |
| Redis hit (negative cache) | \~1ms   |
| MongoDB fallback           | \~15ms  |

 

### **Negative Cache**

Caches "no rule found" results to prevent repeated MongoDB queries.

**Without negative cache:** Every unknown IP → MongoDB query **With negative cache:** First query → MongoDB, subsequent → Redis (\~93% faster)

### **Cache Invalidation**

 

| Operation         | Invalidation Scope         |
| :---------------- | :------------------------- |
| Create exact rule | Single IP negative cache   |
| Create CIDR rule  | All negative cache for org |
| Update policy     | All negative cache for org |
| Sync cache        | All cache for org          |

 

***

## **Troubleshooting**

### **Enable Debug Logging**

```

waf:  debug-logging: true
```

 

### **Common Issues**

 

| Issue                      | Solution                                                    |
| :------------------------- | :---------------------------------------------------------- |
| Rule not taking effect     | Clear cache: `DELETE /v1/waf/cache/{orgId}`                 |
| Locked out after DENY mode | Auto-rule should be created; check with `GET /v1/waf/rules` |
| High latency               | Check Redis connectivity; ensure cache warmup completed     |
| Rules not expiring         | Verify cleanup scheduler is running                         |

 

### **Bypass WAF for Troubleshooting**

Set status to `INACTIVE` to allow all traffic:

```

curl -X PUT /v1/waf/config \
  -H "X-User-Org-Id: your-org-id" \
  -d '{"status": "INACTIVE"}'
```

**Remember to re-enable after troubleshooting!**

***

## **Safety Features**

### **Auto-Rule Creation**

When switching to `DENY` mode, an `ALLOW` rule is automatically created for the requesting client IP to prevent admin lockout.

### **Policy Change Protection**

Changing between `ALLOW` and `DENY` clears existing rules to ensure consistent behavior.

***
