# Firewall

<a id="get-api-v1-accounts-username-firewall-blocked-ips"></a>
#### `GET /api/v1/accounts/{username}/firewall/blocked-ips`

*List IPs blocked from one account*

The server-wide blocked list; there is no per-account scoping, so it is identical for every account. An unknown account answers 404 ACCOUNT_NOT_FOUND.

**Path parameters:**

| Name | Type | Required | Notes |
|------|------|----------|-------|
| `username` | string | yes | pattern=`^[a-z][a-z0-9]{2,15}$` |

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_list_BlockedIPResponse__` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "data": [
    {
      "comment": "...",
      "id": "...",
      "ip": "..."
    }
  ],
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X GET \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/accounts/{username}/firewall/blocked-ips
```

---

<a id="post-api-v1-accounts-username-firewall-blocked-ips"></a>
#### `POST /api/v1/accounts/{username}/firewall/blocked-ips`

*Block IP for one account*

Blocks the address on the server firewall. There is no per-account scoping: the block affects every site on the server and the response says so in `warnings`. An unknown account answers 404 ACCOUNT_NOT_FOUND.

**Path parameters:**

| Name | Type | Required | Notes |
|------|------|----------|-------|
| `username` | string | yes | pattern=`^[a-z][a-z0-9]{2,15}$` |

**Body fields:**

| Field | Type | Required | Notes |
|-------|------|----------|-------|
| `comment` | string | no | — |
| `ip` | string | yes | IP address or CIDR block to block |

**Request body example:**

```json
{
  "comment": "string",
  "ip": "string"
}
```

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_dict_str__Any__` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "data": {},
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X POST \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  -H "Content-Type: application/json" \
  -d @body.json \
  https://your-server:2000/api/v1/accounts/{username}/firewall/blocked-ips
```

---

<a id="delete-api-v1-accounts-username-firewall-blocked-ips-ip"></a>
#### `DELETE /api/v1/accounts/{username}/firewall/blocked-ips/{ip}`

*Unblock IP for one account*

Removes the block from the server firewall — the same rule the server-wide endpoint manages. An unknown account answers 404 ACCOUNT_NOT_FOUND, an address that is not blocked 404 NOT_FOUND.

**Path parameters:**

| Name | Type | Required | Notes |
|------|------|----------|-------|
| `username` | string | yes | pattern=`^[a-z][a-z0-9]{2,15}$` |
| `ip` | string | yes | — |

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `MessageResponse` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "message": "string",
  "status": "success"
}
```

**cURL example:**

```bash
curl -X DELETE \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/accounts/{username}/firewall/blocked-ips/{ip}
```

---

<a id="get-api-v1-firewall-attack-mode"></a>
#### `GET /api/v1/firewall/attack-mode`

*Attack mode status*

DDoS-mitigation mode state + the layers currently applied + auto-mode config.

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_AttackModeStatusExtended_` | Successful Response |

**Response example (200):**

```json
{
  "data": {
    "active": false,
    "auto_mode": "...",
    "enabled_at": "...",
    "optimizations": [
      "..."
    ]
  },
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X GET \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/attack-mode
```

---

<a id="post-api-v1-firewall-attack-mode-auto-disable"></a>
#### `POST /api/v1/firewall/attack-mode/auto-disable`

*Disable auto-attack-mode*

Stops the velocity watcher. Manually-enabled attack mode is preserved. Answers the auto-mode state.

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_AutoModeStatus_` | Successful Response |

**Response example (200):**

```json
{
  "data": {
    "auto_enabled_at": "...",
    "enabled": false,
    "last_score": 0,
    "last_triggered_at": "..."
  },
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X POST \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/attack-mode/auto-disable
```

---

<a id="post-api-v1-firewall-attack-mode-auto-enable"></a>
#### `POST /api/v1/firewall/attack-mode/auto-enable`

*Enable auto-attack-mode*

Background watcher monitors blocked-IP velocity; attack mode triggers automatically above threshold. Answers the auto-mode state.

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_AutoModeStatus_` | Successful Response |

**Response example (200):**

```json
{
  "data": {
    "auto_enabled_at": "...",
    "enabled": false,
    "last_score": 0,
    "last_triggered_at": "..."
  },
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X POST \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/attack-mode/auto-enable
```

---

<a id="post-api-v1-firewall-attack-mode-disable"></a>
#### `POST /api/v1/firewall/attack-mode/disable`

*Disable attack mode*

Restores the values read before the mode was enabled and removes the applied layers. Existing bans are preserved. Not active answers 409 ATTACK_MODE_INACTIVE.

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_AttackModeStatusExtended_` | Successful Response |

**Response example (200):**

```json
{
  "data": {
    "active": false,
    "auto_mode": "...",
    "enabled_at": "...",
    "optimizations": [
      "..."
    ]
  },
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X POST \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/attack-mode/disable
```

---

<a id="post-api-v1-firewall-attack-mode-enable"></a>
#### `POST /api/v1/firewall/attack-mode/enable`

*Enable attack mode*

Applies the hardening layers (kernel sysctl through systemd-sysctl, a SYN-flood / connlimit chain ahead of the firewall's own chains, the nginx rate-limit snippet, tighter fail2ban thresholds) and reports which ones took. Manual toggle — see auto-enable for automated. Already active answers 409 ATTACK_MODE_ACTIVE.

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_AttackModeStatusExtended_` | Successful Response |

**Response example (200):**

```json
{
  "data": {
    "active": false,
    "auto_mode": "...",
    "enabled_at": "...",
    "optimizations": [
      "..."
    ]
  },
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X POST \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/attack-mode/enable
```

---

<a id="get-api-v1-firewall-blocked-ips"></a>
#### `GET /api/v1/firewall/blocked-ips`

*List blocked IPs (server-wide)*

The plain source denies the packet filter carries, each with the comment it was blocked with. Response data: a list of `{id, ip, comment}`.

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_list_BlockedIPResponse__` | Successful Response |

**Response example (200):**

```json
{
  "data": [
    {
      "comment": "...",
      "id": "...",
      "ip": "..."
    }
  ],
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X GET \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/blocked-ips
```

---

<a id="post-api-v1-firewall-blocked-ips"></a>
#### `POST /api/v1/firewall/blocked-ips`

*Block IP (server-wide)*

Add a manual block on a source IP / CIDR: a source deny in the packet filter plus an entry in the HTTP-layer ban list. A whitelisted address answers 409 IP_WHITELISTED; the caller's own address answers 422 SELF_LOCKOUT.

**Body fields:**

| Field | Type | Required | Notes |
|-------|------|----------|-------|
| `comment` | string | no | — |
| `ip` | string | yes | IP address or CIDR block to block |

**Request body example:**

```json
{
  "comment": "string",
  "ip": "string"
}
```

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_BlockedIPResponse_` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "data": {
    "comment": "...",
    "id": "string",
    "ip": "string"
  },
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X POST \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  -H "Content-Type: application/json" \
  -d @body.json \
  https://your-server:2000/api/v1/firewall/blocked-ips
```

---

<a id="post-api-v1-firewall-blocked-ips-clear-all"></a>
#### `POST /api/v1/firewall/blocked-ips/clear-all`

*Clear every blocked IP*

Bulk unblock: every source deny in the packet filter (with or without a port scope), every HTTP-layer ban, every rate-limit ban set member and every fail2ban ban. The security log is not touched — see `DELETE /firewall/logs`.

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_ClearAllResult_` | Successful Response |

**Response example (200):**

```json
{
  "data": {
    "fail2ban_jails": 0,
    "http_bans": 0,
    "rate_limit_bans": 0,
    "removed": 0,
    "ufw_rules": 0
  },
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X POST \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/blocked-ips/clear-all
```

---

<a id="delete-api-v1-firewall-blocked-ips-ip"></a>
#### `DELETE /api/v1/firewall/blocked-ips/{ip}`

*Unblock IP*

Removes the manual block for the specified IP / CIDR (the source deny and the HTTP-layer entry). An address that is not blocked answers 404 NOT_FOUND; a whitelist rule of the same address is left alone.

**Path parameters:**

| Name | Type | Required | Notes |
|------|------|----------|-------|
| `ip` | string | yes | — |

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `MessageResponse` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "message": "string",
  "status": "success"
}
```

**cURL example:**

```bash
curl -X DELETE \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/blocked-ips/{ip}
```

---

<a id="post-api-v1-firewall-disable"></a>
#### `POST /api/v1/firewall/disable`

*Disable host firewall*

Deactivates UFW / firewalld — all rules become inert.

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `MessageResponse` | Successful Response |

**Response example (200):**

```json
{
  "message": "string",
  "status": "success"
}
```

**cURL example:**

```bash
curl -X POST \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/disable
```

---

<a id="post-api-v1-firewall-enable"></a>
#### `POST /api/v1/firewall/enable`

*Enable host firewall*

Activates UFW / firewalld. Existing rules are applied immediately.

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `MessageResponse` | Successful Response |

**Response example (200):**

```json
{
  "message": "string",
  "status": "success"
}
```

**cURL example:**

```bash
curl -X POST \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/enable
```

---

<a id="delete-api-v1-firewall-logs"></a>
#### `DELETE /api/v1/firewall/logs`

*Clear the security log*

Deletes every block and suspicious event record. Irreversible; the blocks themselves are not touched (see `POST /firewall/blocked-ips/clear-all`).

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_dict_str__int__` | Successful Response |

**Response example (200):**

```json
{
  "data": {},
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X DELETE \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/logs
```

---

<a id="get-api-v1-firewall-logs-blocked"></a>
#### `GET /api/v1/firewall/logs/blocked`

*Blocked-request log*

Block events grouped by address and type, newest first: `{events: BlockedEvent[], total}`. `ip` (exact, CIDR or a partial address), `type` (rate_limit / under_attack / manual / autoban), `since` / `until` (ISO-8601) narrow the page; a filter that cannot be a host, a type outside the list or a value that is not a timestamp answers 422.

**Query parameters:**

| Name | Type | Required | Notes |
|------|------|----------|-------|
| `limit` | integer | no | minimum=1; maximum=1000 |
| `offset` | integer | no | minimum=0 |
| `ip` | string | no | — |
| `type` | string | no | — |
| `since` | string | no | — |
| `until` | string | no | — |

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_dict_str__Any__` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "data": {},
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X GET \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/logs/blocked
```

---

<a id="get-api-v1-firewall-logs-stats"></a>
#### `GET /api/v1/firewall/logs/stats`

*Firewall log statistics*

Counts over the last 24 hours (blocked, suspicious, top addresses, top endpoints), twenty-four hourly buckets (UTC, `YYYY-MM-DDTHH:00`), the threat score and the attack-mode flag. Powers the dashboard badge.

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_dict_str__Any__` | Successful Response |

**Response example (200):**

```json
{
  "data": {},
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X GET \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/logs/stats
```

---

<a id="get-api-v1-firewall-logs-stream"></a>
#### `GET /api/v1/firewall/logs/stream`

*SSE — live firewall log tail*

Server-Sent Events stream of new block events as they are written (one `data: [...]` array per poll, a heartbeat with the threat score otherwise). Binary `text/event-stream`.

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | — | Successful Response |

**cURL example:**

```bash
curl -X GET \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/logs/stream
```

---

<a id="get-api-v1-firewall-logs-suspicious"></a>
#### `GET /api/v1/firewall/logs/suspicious`

*Suspicious-request log*

Pre-block events flagged by heuristics (scan patterns, injection attempts, floods), grouped by address and category, newest first: `{events: SuspiciousEvent[], total}`. The same filters as the blocked log apply (`category` instead of `type`).

**Query parameters:**

| Name | Type | Required | Notes |
|------|------|----------|-------|
| `limit` | integer | no | minimum=1; maximum=1000 |
| `offset` | integer | no | minimum=0 |
| `ip` | string | no | — |
| `category` | string | no | — |
| `since` | string | no | — |
| `until` | string | no | — |

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_dict_str__Any__` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "data": {},
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X GET \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/logs/suspicious
```

---

<a id="get-api-v1-firewall-notifications"></a>
#### `GET /api/v1/firewall/notifications`

*Firewall-specific notifications*

Sub-inbox for firewall events (attack mode triggered, high threat score); the same records the shared inbox holds under the firewall category.

**Query parameters:**

| Name | Type | Required | Notes |
|------|------|----------|-------|
| `limit` | integer | no | minimum=1; maximum=200 |
| `unread_only` | boolean | no | — |

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_list_PanelNotification__` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "data": [
    {
      "category": "...",
      "id": "...",
      "level": "...",
      "message": "...",
      "read": "...",
      "target": "...",
      "timestamp": "...",
      "title": "...",
      "username": "..."
    }
  ],
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X GET \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/notifications
```

---

<a id="post-api-v1-firewall-notifications-read-all"></a>
#### `POST /api/v1/firewall/notifications/read-all`

*Mark all firewall notifications as read*

Bulk operation across the admin-scope inbox.

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `MessageResponse` | Successful Response |

**Response example (200):**

```json
{
  "message": "string",
  "status": "success"
}
```

**cURL example:**

```bash
curl -X POST \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/notifications/read-all
```

---

<a id="post-api-v1-firewall-notifications-notif-id-read"></a>
#### `POST /api/v1/firewall/notifications/{notif_id}/read`

*Mark firewall notification as read*

Flip the read flag for one firewall-scope notification.

**Path parameters:**

| Name | Type | Required | Notes |
|------|------|----------|-------|
| `notif_id` | string | yes | — |

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `MessageResponse` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "message": "string",
  "status": "success"
}
```

**cURL example:**

```bash
curl -X POST \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/notifications/{notif_id}/read
```

---

<a id="get-api-v1-firewall-rate-limits"></a>
#### `GET /api/v1/firewall/rate-limits`

*List rate-limit rules*

Port-based rate-limit rules (`{id, name, port, protocol, threshold, window_seconds, ban_duration, enabled, created_at}`); each enabled rule is a hashlimit rule plus a ban set in the packet filter. An unreadable rule store answers 500 SERVICE_ERROR rather than an empty list.

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_list_RateLimitRuleResponse__` | Successful Response |

**Response example (200):**

```json
{
  "data": [
    {
      "ban_duration": "...",
      "created_at": "...",
      "enabled": "...",
      "id": "...",
      "name": "...",
      "port": "...",
      "protocol": "...",
      "threshold": "...",
      "window_seconds": "..."
    }
  ],
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X GET \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/rate-limits
```

---

<a id="post-api-v1-firewall-rate-limits"></a>
#### `POST /api/v1/firewall/rate-limits`

*Create rate-limit rule*

Define a new rate-limit rule (port / port list / range or any, protocol, max new connections per source address per window, ban duration). The rules run in their own chain ahead of the host firewall; loopback traffic and whitelisted addresses are never counted or banned. The rule is stored only after the packet filter accepted it; a refused rule answers 500 RATE_LIMIT_APPLY_FAILED.

**Body fields:**

| Field | Type | Required | Notes |
|-------|------|----------|-------|
| `ban_duration` | integer | yes | Ban duration in seconds (0 = permanent); minimum=0.0; maximum=604800.0 |
| `name` | string | yes | minLength=1; maxLength=100 |
| `port` | string | no | Port, comma-separated ports, range (e.g. '80', '80,443', '8000:9000'), or 'any'; default `any` |
| `protocol` | string | no | default `tcp`; enum: `tcp`, `udp`, `all` |
| `threshold` | integer | yes | Max new connections one source address may open in the time window; minimum=1.0; maximum=100000.0 |
| `window_seconds` | integer | yes | Time window in seconds; minimum=1.0; maximum=86400.0 |

**Request body example:**

```json
{
  "ban_duration": 0,
  "name": "string",
  "port": "any",
  "protocol": "tcp",
  "threshold": 1.0,
  "window_seconds": 1.0
}
```

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_RateLimitRuleResponse_` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "data": {
    "ban_duration": 0,
    "created_at": "string",
    "enabled": false,
    "id": "string",
    "name": "string",
    "port": "string",
    "protocol": "string",
    "threshold": 0,
    "window_seconds": 0
  },
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X POST \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  -H "Content-Type: application/json" \
  -d @body.json \
  https://your-server:2000/api/v1/firewall/rate-limits
```

---

<a id="get-api-v1-firewall-rate-limits-banned"></a>
#### `GET /api/v1/firewall/rate-limits/banned`

*List IPs currently banned by rate-limit rules*

Aggregated active bans across every rate-limit rule. Each entry is `{ip, rule_id, rule_name}`.

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_list_dict_str__Any___` | Successful Response |

**Response example (200):**

```json
{
  "data": [
    {}
  ],
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X GET \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/rate-limits/banned
```

---

<a id="delete-api-v1-firewall-rate-limits-banned-ip"></a>
#### `DELETE /api/v1/firewall/rate-limits/banned/{ip}`

*Unban IP across every rate-limit rule*

Lifts the IP's rate-limit ban on every rule; the message says how many ban sets carried it. A malformed address answers 422.

**Path parameters:**

| Name | Type | Required | Notes |
|------|------|----------|-------|
| `ip` | string | yes | — |

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `MessageResponse` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "message": "string",
  "status": "success"
}
```

**cURL example:**

```bash
curl -X DELETE \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/rate-limits/banned/{ip}
```

---

<a id="delete-api-v1-firewall-rate-limits-rule-id"></a>
#### `DELETE /api/v1/firewall/rate-limits/{rule_id}`

*Delete rate-limit rule*

Removes the rule + every active ban it produced. An unknown rule answers 404 NOT_FOUND.

**Path parameters:**

| Name | Type | Required | Notes |
|------|------|----------|-------|
| `rule_id` | string | yes | — |

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `MessageResponse` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "message": "string",
  "status": "success"
}
```

**cURL example:**

```bash
curl -X DELETE \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/rate-limits/{rule_id}
```

---

<a id="put-api-v1-firewall-rate-limits-rule-id"></a>
#### `PUT /api/v1/firewall/rate-limits/{rule_id}`

*Update rate-limit rule*

Patch name, port, protocol, threshold, window, ban duration or enabled. The kernel objects are rebuilt; when the new shape is refused the old one is put back. An unknown rule answers 404 NOT_FOUND.

**Path parameters:**

| Name | Type | Required | Notes |
|------|------|----------|-------|
| `rule_id` | string | yes | — |

**Body fields:**

| Field | Type | Required | Notes |
|-------|------|----------|-------|
| `ban_duration` | integer | no | — |
| `enabled` | boolean | no | — |
| `name` | string | no | — |
| `port` | string | no | — |
| `protocol` | string | no | — |
| `threshold` | integer | no | — |
| `window_seconds` | integer | no | — |

**Request body example:**

```json
{
  "ban_duration": 0,
  "enabled": false,
  "name": "string",
  "port": "string",
  "protocol": "tcp",
  "threshold": 1.0,
  "window_seconds": 1.0
}
```

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_RateLimitRuleResponse_` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "data": {
    "ban_duration": 0,
    "created_at": "string",
    "enabled": false,
    "id": "string",
    "name": "string",
    "port": "string",
    "protocol": "string",
    "threshold": 0,
    "window_seconds": 0
  },
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X PUT \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  -H "Content-Type: application/json" \
  -d @body.json \
  https://your-server:2000/api/v1/firewall/rate-limits/{rule_id}
```

---

<a id="post-api-v1-firewall-rate-limits-rule-id-unban-ip"></a>
#### `POST /api/v1/firewall/rate-limits/{rule_id}/unban/{ip}`

*Unban IP from one rate-limit rule*

Targeted unban — only the named rule is affected, other rules' bans remain. An unknown rule answers 404 NOT_FOUND.

**Path parameters:**

| Name | Type | Required | Notes |
|------|------|----------|-------|
| `rule_id` | string | yes | — |
| `ip` | string | yes | — |

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `MessageResponse` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "message": "string",
  "status": "success"
}
```

**cURL example:**

```bash
curl -X POST \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/rate-limits/{rule_id}/unban/{ip}
```

---

<a id="get-api-v1-firewall-rules"></a>
#### `GET /api/v1/firewall/rules`

*List firewall rules*

Every port rule the backend carries — global and source-scoped — with id (`{port}-{protocol}` or `{port}-{protocol}--{source_ip}`), action (allow/deny/limit/reject), port, protocol (`any` when the rule names none), source_ip and comment. Plain source allows (the whitelist) and plain source denies (the blocked list) are not port rules.

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_list_FirewallRuleResponse__` | Successful Response |

**Response example (200):**

```json
{
  "data": [
    {
      "action": "...",
      "comment": "...",
      "id": "...",
      "port": "...",
      "protocol": "...",
      "source_ip": "..."
    }
  ],
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X GET \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/rules
```

---

<a id="post-api-v1-firewall-rules"></a>
#### `POST /api/v1/firewall/rules`

*Create firewall rule*

Add a new allow/deny rule. Triggers immediate `ufw reload` / `firewall-cmd --reload`.

**Body fields:**

| Field | Type | Required | Notes |
|-------|------|----------|-------|
| `action` | string | no | default `allow`; enum: `allow`, `deny` |
| `port` | string | yes | Port number or range (e.g. '80', '8000:8080') |
| `protocol` | string | no | default `tcp`; enum: `tcp`, `udp` |
| `source_ip` | string | no | Restrict rule to a specific source IP or CIDR (optional) |

**Request body example:**

```json
{
  "action": "allow",
  "port": "string",
  "protocol": "tcp",
  "source_ip": "string"
}
```

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_FirewallRuleResponse_` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "data": {
    "action": "string",
    "comment": "...",
    "id": "string",
    "port": "string",
    "protocol": "string",
    "source_ip": "..."
  },
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X POST \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  -H "Content-Type: application/json" \
  -d @body.json \
  https://your-server:2000/api/v1/firewall/rules
```

---

<a id="delete-api-v1-firewall-rules-rule-id"></a>
#### `DELETE /api/v1/firewall/rules/{rule_id}`

*Delete firewall rule*

Remove the rule and reload the firewall. A rule that does not exist answers 404 NOT_FOUND.

**Path parameters:**

| Name | Type | Required | Notes |
|------|------|----------|-------|
| `rule_id` | string | yes | — |

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `MessageResponse` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "message": "string",
  "status": "success"
}
```

**cURL example:**

```bash
curl -X DELETE \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/rules/{rule_id}
```

---

<a id="put-api-v1-firewall-rules-rule-id"></a>
#### `PUT /api/v1/firewall/rules/{rule_id}`

*Update firewall rule*

Replace an existing rule's action / source / port. A rule id that does not exist answers 404 NOT_FOUND; when the new rule is refused the old one is put back.

**Path parameters:**

| Name | Type | Required | Notes |
|------|------|----------|-------|
| `rule_id` | string | yes | — |

**Body fields:**

| Field | Type | Required | Notes |
|-------|------|----------|-------|
| `action` | string | no | default `allow`; enum: `allow`, `deny` |
| `port` | string | yes | Port number or range (e.g. '80', '8000:8080') |
| `protocol` | string | no | default `tcp`; enum: `tcp`, `udp` |
| `source_ip` | string | no | Restrict rule to a specific source IP or CIDR (optional) |

**Request body example:**

```json
{
  "action": "allow",
  "port": "string",
  "protocol": "tcp",
  "source_ip": "string"
}
```

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_FirewallRuleResponse_` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "data": {
    "action": "string",
    "comment": "...",
    "id": "string",
    "port": "string",
    "protocol": "string",
    "source_ip": "..."
  },
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X PUT \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  -H "Content-Type: application/json" \
  -d @body.json \
  https://your-server:2000/api/v1/firewall/rules/{rule_id}
```

---

<a id="get-api-v1-firewall-status"></a>
#### `GET /api/v1/firewall/status`

*UFW / firewalld status*

Host firewall state: backend (UFW or firewalld), enabled flag, the number of port rules and of blocked addresses (plain source denies).

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_FirewallStatusResponse_` | Successful Response |

**Response example (200):**

```json
{
  "data": {
    "active": false,
    "backend": "string",
    "blocked_count": 0,
    "output": "",
    "rules_count": 0
  },
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X GET \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/status
```

---

<a id="get-api-v1-firewall-whitelist"></a>
#### `GET /api/v1/firewall/whitelist`

*Firewall whitelist*

Addresses let through on every port and exempt from auto-bans. Response data: a list of `{ip, comment, applied}` — `applied` says whether the packet filter carries the allow rule right now.

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_list_WhitelistEntryResponse__` | Successful Response |

**Response example (200):**

```json
{
  "data": [
    {
      "applied": "...",
      "comment": "...",
      "ip": "..."
    }
  ],
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X GET \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/whitelist
```

---

<a id="post-api-v1-firewall-whitelist"></a>
#### `POST /api/v1/firewall/whitelist`

*Add IP to firewall whitelist*

Idempotent — an entry already present keeps its record, and its allow rule is re-installed when the firewall lost it.

**Body fields:**

| Field | Type | Required | Notes |
|-------|------|----------|-------|
| `comment` | string | no | — |
| `ip` | string | yes | IP address or CIDR block to block |

**Request body example:**

```json
{
  "comment": "string",
  "ip": "string"
}
```

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `ApiSuccess_dict_str__Any__` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "data": {},
  "message": "",
  "status": "success",
  "warnings": [
    "string"
  ]
}
```

**cURL example:**

```bash
curl -X POST \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  -H "Content-Type: application/json" \
  -d @body.json \
  https://your-server:2000/api/v1/firewall/whitelist
```

---

<a id="delete-api-v1-firewall-whitelist-ip"></a>
#### `DELETE /api/v1/firewall/whitelist/{ip}`

*Remove IP from whitelist*

The IP returns to normal firewall enforcement.

**Path parameters:**

| Name | Type | Required | Notes |
|------|------|----------|-------|
| `ip` | string | yes | — |

**Responses:**

| Status | Schema | Description |
|--------|--------|-------------|
| `200` | `MessageResponse` | Successful Response |
| `422` | `HTTPValidationError` | Validation Error |

**Response example (200):**

```json
{
  "message": "string",
  "status": "success"
}
```

**cURL example:**

```bash
curl -X DELETE \
  -H "X-WHost-Key: $WHOST_API_KEY" \
  -H "X-WHost-Timestamp: $(date +%s)" \
  -H "X-WHost-Nonce: $(openssl rand -hex 16)" \
  -H "X-WHost-Signature: $(compute_hmac)" \
  https://your-server:2000/api/v1/firewall/whitelist/{ip}
```

---
