npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

wafio-client

v1.0.0

Published

Node.js/Bun client for Wafio WAF TCP mTLS (analyze requests, check block)

Readme

wafio-client

A production-ready TypeScript/JavaScript client for Wafio WAF (Web Application Firewall) over TCP mTLS. Analyze incoming HTTP requests for malicious patterns and check if clients are currently blocked.

Works with:

  • ✅ Node.js 18+
  • ✅ Bun
  • ✅ TypeScript (full type support)
  • ✅ All major frameworks: Express, Fastify, Hono, and any custom HTTP server

Features:

  • Fail-open by default – if the Wafio server is unreachable, requests are allowed (circuit breaker pattern)
  • Connection pooling – efficiently handle concurrent requests
  • Auto-reconnect with cooldown to prevent hammering a down server
  • Keepalive ping – maintains long-lived connections
  • Framework-agnostic – works with any HTTP framework via RequestSnapshot
  • Full feature parity with Go and PHP client libraries

Installation

npm

npm install wafio-client

Monorepo (local link)

{
  "dependencies": {
    "wafio-client": "file:../packages/wafio-client"
  }
}

Then run npm install.

Quick Start

1. Get mTLS Credentials

Call the Wafio API to generate credentials:

curl -X POST "http://localhost:9087/api/projects/{PROJECT_ID}/mtls-keys" \
  -H "Authorization: Bearer {JWT_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"name":"my-app"}'

Save the response JSON to a file (e.g., mtls-credentials.json). File format:

{
  "ca_pem": "-----BEGIN CERTIFICATE-----\nMIIC8zCCAdugAwIBAgIRAIQVaAhpNJps+mtxouyf8gowDQYJKoZIhvcNAQELBQAw\n...(full CA certificate)...\n-----END CERTIFICATE-----\n",
  "client_cert_pem": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIQKJvrt86e9fY3QOe2wQJgADANBgkqhkiG9w0BAQsFADAT\n...(full client certificate)...\n-----END CERTIFICATE-----\n",
  "client_key_pem": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCocinxcsZMEJT4\n...(full private key)...\n-----END PRIVATE KEY-----\n",
  "tcp_url": "tcp.wafio.cloud:9443",
  "created_at": "2026-02-15T04:51:12.376946+07:00",
  "id": "ee528f42-c207-49fa-8e04-6d660b765588",
  "name": "My MTLS Key",
  "project_id": "5c83e212-1f07-4915-b086-066d361ed193",
  "secret": "ebe63f6c1c053b1ddfed72d6b86e76fdfc79d848c3413336b60740542ef9fc99"
}

Field Descriptions:

  • ca_pem (string): CA certificate in PEM format (needed to verify the Wafio server)
  • client_cert_pem (string): Client certificate in PEM format (identifies your application to the server)
  • client_key_pem (string): Client private key in PEM format (must keep secret)
  • tcp_url (string, optional): TCP endpoint from server (for example tcp.wafio.cloud:9443). If present, JS client uses this automatically when host/port are not set.
  • id (string): Unique key ID (use as username for certificate renewal)
  • secret (string): Renewal secret (use as password for certificate renewal via Basic Auth)
  • project_id (string): The project this key belongs to
  • name (string): Human-readable name you gave to this key
  • created_at (string): ISO 8601 timestamp when the key was created

2. Analyze Requests

import { WafioClient, loadMtlsCredentialsFile } from 'wafio-client';

// Load credentials from file
const credentials = loadMtlsCredentialsFile('./mtls-credentials.json');

// Create client
const client = new WafioClient({
  credentials, // tcp_url in credentials file is used automatically
});

// Connect to Wafio server
await client.connect();

// Analyze an incoming request
const result = await client.analyze({
  method: 'GET',
  uri: '/search?q=<script>alert(1)</script>',
  remote_addr: '192.168.1.100',
  host: 'example.com',
  headers: {
    'user-agent': ['Mozilla/5.0'],
    'referer': ['https://google.com'],
  },
  user_agent: 'Mozilla/5.0',
  request_id: 'req-12345',
});

// Check the response
if (result.action === 'block') {
  console.log(`REQUEST BLOCKED: ${result.message} (categories: ${result.categories}, score: ${result.score})`);
  // Return 403 Forbidden to client
} else if (result.action === 'log') {
  console.log(`REQUEST LOGGED: ${result.message} (score: ${result.score})`);
  // Allow request but log it
} else {
  console.log(`REQUEST ALLOWED: ${result.message}`);
  // Allow request
}

await client.close();

Core Concepts

Analyze vs CheckBlock

analyze(req) – Full WAF analysis:

  • Runs the request through the OWASP CRS (Core Rule Set)
  • Scores the request based on matched rules
  • Returns action (allow/log/block), score, categories, and detailed message
  • Use this when you need to understand why a request was blocked

checkBlock(key) – Fast block status check:

  • Checks if a key (e.g., IP address, user ID) is currently in the block window
  • Useful for rate-limited clients (blocks for N seconds after M violations)
  • Returns only blocked (true/false) and block_until timestamp
  • Very fast – no WAF analysis, just a cache lookup

Fail-Open Behavior

By default (failOpenOnUnreachable: true), the client will:

  1. On timeout/unreachable: Return { action: 'allow' } (not blocked) immediately
  2. Set fail-open window: For the next 5 seconds, immediately allow all requests (no server calls)
  3. Resume normal operation: After cooldown, attempt to reconnect

This ensures your application never blocks traffic when Wafio is down.

To disable fail-open (strict mode):

const client = new WafioClient({
  credentials: '.../mtls-credentials.json',
  failOpenOnUnreachable: false, // Strict mode: throw on server unavailable
});

Connection Pooling

For high-traffic applications, use a connection pool instead of a single client:

import { WafioClientPool, loadMtlsCredentialsFile } from 'wafio-client';

const pool = new WafioClientPool({
  credentials: loadMtlsCredentialsFile('./mtls-credentials.json'),
  poolSize: 10, // 10 concurrent connections (default 5)
});

// Initialize the pool (fetches tier limits from server)
await pool.init();

// Use the pool (clients auto-checkout/return)
const result = await pool.analyze({
  method: 'POST',
  uri: '/api/submit',
  remote_addr: '203.0.113.42',
  host: 'api.example.com',
  headers: { 'content-type': ['application/json'] },
  body: '{"user":"alice"}',
});

console.log('Action:', result.action);

await pool.close();

Framework Integration

Express.js

import express from 'express';
import { WafioClient, fromRequest, buildAnalyzeRequest } from 'wafio-client';

const app = express();
const wafioClient = new WafioClient({
  credentials: './mtls-credentials.json',
});

app.use(express.json());
app.use(express.raw({ type: '*/*', limit: '10mb' }));

// WAF middleware
app.use(async (req, res, next) => {
  try {
    // Convert Express request to framework-agnostic snapshot
    const snapshot = fromRequest(req);

    // Build analyze request
    const analyzeReq = buildAnalyzeRequest(snapshot);

    // Analyze with Wafio
    const result = await wafioClient.analyze(analyzeReq);

    if (result.action === 'block') {
      return res.status(403).json({
        error: 'Access Denied',
        message: result.message,
        categories: result.categories,
      });
    }

    // Request is allowed; proceed to next middleware/handler
    next();
  } catch (err) {
    console.error('WAF error:', err);
    // Fail-open: allow the request if there's an error
    next();
  }
});

app.get('/', (req, res) => {
  res.json({ message: 'Hello, World!' });
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Fastify

import Fastify from 'fastify';
import { WafioClient, fromRequest, buildAnalyzeRequest } from 'wafio-client';

const fastify = Fastify();
const wafioClient = new WafioClient({
  credentials: './mtls-credentials.json',
});

// WAF hook
fastify.addHook('preValidation', async (request, reply) => {
  try {
    const snapshot = fromRequest(request);
    const analyzeReq = buildAnalyzeRequest(snapshot);
    const result = await wafioClient.analyze(analyzeReq);

    if (result.action === 'block') {
      return reply.status(403).send({
        error: 'Access Denied',
        message: result.message,
      });
    }
  } catch (err) {
    console.error('WAF error:', err);
  }
});

fastify.get('/', async () => {
  return { message: 'Hello from Fastify!' };
});

fastify.listen({ port: 3000 }, (err) => {
  if (err) throw err;
  console.log('Server running on port 3000');
});

Hono

import { Hono } from 'hono';
import { WafioClient, fromRequest, buildAnalyzeRequest } from 'wafio-client';

const app = new Hono();
const wafioClient = new WafioClient({
  credentials: './mtls-credentials.json',
});

// WAF middleware
app.use(async (c, next) => {
  try {
    const snapshot = fromRequest(c.req);
    const analyzeReq = buildAnalyzeRequest(snapshot);
    const result = await wafioClient.analyze(analyzeReq);

    if (result.action === 'block') {
      return c.json(
        {
          error: 'Access Denied',
          message: result.message,
        },
        403
      );
    }
  } catch (err) {
    console.error('WAF error:', err);
  }
  await next();
});

app.get('/', (c) => {
  return c.json({ message: 'Hello from Hono!' });
});

export default app;

Configuration Options

When creating a client, you can customize timeouts and behaviors. All have sensible defaults:

const client = new WafioClient({
  // === Required ===
  credentials: WafioCredentials | string; // Credentials object or path to file

  // === Optional endpoint override ===
  host?: string; // Default: host parsed from credentials.tcp_url, fallback "localhost"
  port?: number; // Default: port parsed from credentials.tcp_url, fallback 9089

  // === Timeouts (all in milliseconds; shown with defaults) ===
  requestTimeoutMs?: number; // Timeout for a single request
                               // Default: 300ms
                               // 0 = no timeout
                               // On timeout, client returns allow (if failOpenOnUnreachable=true)

  connectTimeoutMs?: number; // Timeout when establishing TLS connection
                               // Default: 2000ms (2s)
                               // Only used for initial connect or reconnect

  reconnectCooldownMs?: number; // Wait time after failed connection before retrying
                                  // Default: 2000ms (2s)
                                  // Prevents hammering a down server

  failOpenCooldownMs?: number; // Duration to immediately allow requests after a timeout
                                 // Default: 5000ms (5s)
                                 // Works with failOpenOnUnreachable=true (circuit breaker)

  keepaliveIntervalMs?: number; // Send checkBlock("__keepalive__") every N milliseconds
                                  // Default: 25000ms (25s)
                                  // Keeps TCP connection alive, detects dead connections early

  // === Behavior ===
  failOpenOnUnreachable?: boolean; // If Wafio server is unreachable, return allow (true)
                                    // Default: true (fail-open, circuit breaker pattern)
                                    // Set to false for strict mode (throw if server down)

  maxPayloadSize?: number; // Maximum request body size in bytes (0 = no limit)
                             // Default: 0 (no limit)
                             // Rejects requests larger than this

  onRequestTimeout?: (info: { timeoutMs: number }) => void; // Callback on timeout
                                                              // Useful for logging/metrics

  logRequestTimeout?: boolean; // Log timeout to console
                                // Default: false
                                // Only used if onRequestTimeout is not provided
});

Web Server Integration (Advanced)

Using RequestSnapshot

For framework-agnostic code, build a RequestSnapshot manually:

import { buildAnalyzeRequest, type RequestSnapshot } from 'wafio-client';

// You can build a snapshot from any HTTP context
const snapshot: RequestSnapshot = {
  method: 'POST',
  url: '/api/users',
  headers: {
    'content-type': 'application/json',
    'x-forwarded-for': '203.0.113.1',
  },
  body: '{"name":"alice"}',
  remoteAddress: '10.0.0.5', // Behind proxy; real IP in headers
  host: 'api.example.com',
  requestId: 'req-12345',
  userAgent: 'MyApp/1.0',
};

const analyzeReq = buildAnalyzeRequest(snapshot);
const result = await client.analyze(analyzeReq);

Custom Client IP Resolution

By default, buildAnalyzeRequest() resolves the client IP in this order:

  1. X-Forwarded-For header (first comma-separated value)
  2. X-Real-IP header
  3. Forwarded header (RFC 7239; extract for= parameter)
  4. remoteAddress (socket address)
  5. Fallback: "127.0.0.1"

To override:

import { resolveClientIp, buildAnalyzeRequest } from 'wafio-client';

const snapshot: RequestSnapshot = {
  method: 'GET',
  url: '/',
  headers: { 'x-forwarded-for': '203.0.113.1' },
};

// Automatically resolves to 203.0.113.1
const clientIp = resolveClientIp(snapshot);
console.log(clientIp); // => "203.0.113.1"

Header Normalization

The server expects Record<string, string[]> (array per header). Normalize headers:

import { normalizeHeaders } from 'wafio-client';

const raw = {
  'content-type': 'application/json',
  'x-forwarded-for': '203.0.113.1',
};

const normalized = normalizeHeaders(raw);
// => { 'content-type': ['application/json'], 'x-forwarded-for': ['203.0.113.1'] }

Credentials from Environment

import { WafioClient } from 'wafio-client';

const client = new WafioClient({
  host: process.env.WAFIO_HOST || 'localhost',
  port: parseInt(process.env.WAFIO_PORT || '9089'),
  credentials: {
    client_cert_pem: process.env.WAFIO_CLIENT_CERT!,
    client_key_pem: process.env.WAFIO_CLIENT_KEY!,
    ca_pem: process.env.WAFIO_CA_CERT!,
  },
});

Custom Timeouts

Use withWafioTimeout() to add an application-level timeout:

import { withWafioTimeout } from 'wafio-client';

const analyzePromise = client.analyze({
  method: 'POST',
  uri: '/api/heavy-work',
  remote_addr: '192.168.1.1',
  // ...
});

// Timeout after 100ms at application level (separate from requestTimeoutMs)
const result = await withWafioTimeout(analyzePromise, 100, () => {
  console.log('Application timeout – allowing request');
});

API Reference

WafioClient

constructor(options: WafioClientOptions)

Creates a new Wafio client.

Parameters:

  • host (string): Optional endpoint override. Default: host parsed from credentials.tcp_url, fallback localhost
  • port (number): Optional endpoint override. Default: port parsed from credentials.tcp_url, fallback 9089
  • credentials (WafioCredentials | string): Credentials object or path to file. Required.
  • requestTimeoutMs (number): Timeout per request (ms). Default: 300
  • connectTimeoutMs (number): Timeout for TLS connection (ms). Default: 2000
  • reconnectCooldownMs (number): Delay before reconnect (ms). Default: 2000
  • failOpenCooldownMs (number): Circuit breaker cooldown (ms). Default: 5000
  • keepaliveIntervalMs (number): Ping interval (ms). Default: 25000
  • failOpenOnUnreachable (boolean): Return allow if server down. Default: true
  • maxPayloadSize (number): Max request body size (bytes). Default: 0 (no limit)
  • onRequestTimeout (function): Callback on timeout
  • logRequestTimeout (boolean): Log timeout to console. Default: false

async connect(): Promise<void>

Connect to the Wafio server (mTLS). Optional – analyze() and checkBlock() will auto-connect if needed.

Throws:

  • Error if connection fails (credentials invalid, server unreachable, timeout, etc.)

Fail-Open Behavior: If failOpenOnUnreachable=true and connection fails, connect() resolves (does not throw). Subsequent analyze() calls return "allow".

async analyze(req: AnalyzeRequest): Promise<AnalyzeResponse>

Analyze an HTTP request for malicious patterns.

Parameters:

  • req.method (string): HTTP method (GET, POST, etc.)
  • req.uri (string): Request URI with query string
  • req.remote_addr (string): Client IP address
  • req.host (string): Host header value
  • req.headers (Record<string, string[]>): HTTP headers (array per header name)
  • req.body (string): Request body as string
  • req.body_b64 (string): Request body as base64 (if binary)
  • req.body_size (number): Original body size in bytes (if truncated)
  • req.user_agent (string): User-Agent header
  • req.request_id (string): Unique request ID for logging

Returns:

  • AnalyzeResponse:
    • action (string): "allow", "log", or "block"
    • score (number): Overall threat score (0-1000+)
    • categories (string[]): Attack categories detected (SQLi, XSS, etc.)
    • message (string): Human-readable description
    • analyze_method (string): How request was analyzed (rule-based, fastscan, bot, etc.)
    • block_until (string): Timestamp until IP is blocked (if action == "block")
    • error (string): Error message (if any)

Fail-Open Behavior: If failOpenOnUnreachable=true and a timeout/connection error occurs or server-side limits are hit:

  • Returns { action: 'allow' } immediately
  • Sets fail-open window – subsequent requests return "allow" for 5s without retrying
  • After cooldown expires, resumes normal operation (attempts to reconnect)

async checkBlock(key: string): Promise<CheckBlockResponse>

Check if a client (IP, user ID, etc.) is currently in the block window.

Parameters:

  • key (string): The key to check (e.g., "192.168.1.1", "user-123")

Returns:

  • CheckBlockResponse:
    • blocked (boolean): true if the key is blocked, false otherwise
    • block_until (string): ISO timestamp when the block expires (if blocked)
    • error (string): Error message (if any)

Use Case: Before running expensive operations (login, payment, file upload), quickly check if the client is blocked.

close(): void

Close the TCP connection and stop keepalive.

get connected(): boolean

Returns true if the client is currently connected to the Wafio server.

async getTierLimits(): Promise<number | undefined>

Fetch tier limits from the server (max concurrent connections allowed for your project).

Returns:

  • number: Maximum TCP connections allowed (0 if no limit)
  • undefined if request fails or tier limits not supported

Use Case: Called automatically by WafioClientPool.init() to cap the pool size.

WafioClientPool

constructor(options: WafioClientPoolOptions)

Create a new connection pool.

Parameters:

  • poolSize (number): Number of TCP connections to maintain. Default: 5
  • All WafioClientOptions parameters (host, port, credentials, timeouts, etc.)

async init(): Promise<void>

Initialize the pool:

  1. Connects once to fetch tier limits from the server
  2. Caps pool size to server limit (if applicable)
  3. Creates and connects all pool clients

Idempotent – safe to call multiple times.

Returns:

  • Promise that resolves when pool is ready
  • Rejects if init fails (credentials invalid, server unreachable, etc.)

async analyze(req: AnalyzeRequest): Promise<AnalyzeResponse>

Analyze using a client from the pool. Automatically calls init().

async checkBlock(key: string): Promise<CheckBlockResponse>

Check block status using a client from the pool. Automatically calls init().

async withClient<T>(fn: (client: WafioClient) => Promise<T>): Promise<T>

Generic helper: get a client from the pool, run a function, and return the client.

Useful for custom operations:

const customResult = await pool.withClient(async (client) => {
  const resp1 = await client.analyze(req1);
  const resp2 = await client.analyze(req2);
  return { resp1, resp2 };
});

close(): void

Close all connections and clean up the pool.

Helper Functions

loadMtlsCredentialsFile(filePath: string): WafioCredentials

Load and normalize mTLS credentials from a JSON file.

Parameters:

  • filePath (string): Path to mtls-credentials.json

Returns:

  • WafioCredentials object with client_cert_pem, client_key_pem, ca_pem

Throws:

  • Error if file not found, JSON invalid, or required fields missing

loadMtlsCredentialsFileFull(filePath: string): MtlsCredentialsFile & WafioCredentials

Load the full credentials file (includes project_id, secret, id, etc.).

Returns:

  • Full credentials object with all fields from the API response

Useful for:

  • Storing/managing the secret (needed to renew certificates later)
  • Logging project_id for audit trails

loadCredentialsFromFile(filePath: string): WafioCredentials

Alias to loadMtlsCredentialsFile().

normalizeHeaders(headers: Record<string, string | string[] | undefined>): Record<string, string[]>

Convert headers to the format expected by the server (array per header).

Parameters:

  • headers: Object with string or array values

Returns:

  • Normalized headers with array values

Example:

const headers = normalizeHeaders({
  'content-type': 'application/json',
  'x-custom': ['a', 'b'], // Already an array
});
// => { 'content-type': ['application/json'], 'x-custom': ['a', 'b'] }

fromRequest(req: IncomingRequestLike): RequestSnapshot

Convert a framework request object to a framework-agnostic RequestSnapshot.

Parameters:

  • req: An object with Express/Fastify/Hono-like properties:
    • method (string): HTTP method
    • url | originalUrl (string): Request URL
    • headers (Record<string, string | string[]>): HTTP headers
    • body (unknown): Request body
    • socket?.remoteAddress (string): Socket IP
    • ip (string): Client IP
    • hostname (string): Host
    • get(name: string) (function): Get header by name

Returns:

  • RequestSnapshot ready for buildAnalyzeRequest()

Note: This function is framework-agnostic: it doesn't import Express, Fastify, etc. Any object with the right shape can be passed.

buildAnalyzeRequest(snapshot: RequestSnapshot): AnalyzeRequest

Convert a request snapshot to an AnalyzeRequest for WAF analysis.

Parameters:

  • snapshot: Framework-agnostic request snapshot

Returns:

  • AnalyzeRequest ready for client.analyze()

What it does:

  • Normalizes headers (array per header)
  • Resolves client IP from proxy headers (X-Forwarded-For, X-Real-IP, Forwarded) or remoteAddress
  • Defaults empty fields (method → "GET", uri → "/", etc.)

resolveClientIp(snapshot: RequestSnapshot): string

Extract the real client IP from the request.

Algorithm (in order):

  1. X-Forwarded-For header (first comma-separated value – the original client)
  2. X-Real-IP header (used by some proxies)
  3. Forwarded header (RFC 7239; extract for= parameter)
  4. remoteAddress (socket address)
  5. Fallback: "127.0.0.1"

When to use: Your application is behind a reverse proxy or load balancer. Ensure the proxy populates X-Forwarded-For or X-Real-IP.

withWafioTimeout<T>(promise: Promise<T>, ms: number, onTimeout?: () => void): Promise<T>

Add an application-level timeout to a Wafio operation.

Parameters:

  • promise: Promise from client.analyze(), client.checkBlock(), etc.
  • ms: Timeout in milliseconds. 0/negative = no timeout
  • onTimeout: Optional callback invoked before rejecting

Returns:

  • Promise that rejects with "Wafio timeout" if time elapses

Note: This is separate from requestTimeoutMs (which is per-request at the TCP level). Use this for application-level timeouts (e.g., "don't wait more than 100ms for WAF").

Default Configuration (Recommended)

These defaults are production-tested:

| Setting | Default | Rationale | |---------|---------|-----------| | requestTimeoutMs | 300ms | Fast response; timeout triggers fail-open | | connectTimeoutMs | 2000ms (2s) | Reasonable; prevents long hangs on connect | | reconnectCooldownMs | 2000ms (2s) | Prevents hammering a down server | | failOpenCooldownMs | 5000ms (5s) | Circuit breaker; allows recovery | | keepaliveIntervalMs | 25000ms (25s) | Keeps TCP alive; detects dead conns early | | failOpenOnUnreachable | true | Resilience; app never blocks due to WAF outage |

Override only if you have specific requirements (e.g., stricter timeout, different proxy setup).

Best Practices

1. Use Fail-Open in Production

Always set failOpenOnUnreachable: true (default). This ensures your application stays online even if Wafio is temporarily unavailable. Use monitoring/alerting to detect when Wafio is down, not blocking traffic.

2. Log & Monitor

Use onRequestTimeout callback for metrics:

const client = new WafioClient({
  credentials: '...',
  onRequestTimeout: (info) => {
    console.warn(`Wafio timeout after ${info.timeoutMs}ms`);
    // Send to observability platform
    metrics.incrementCounter('wafio.timeout', {
      timeout_ms: info.timeoutMs,
    });
  },
});

3. Set Appropriate Timeouts

  • Development: Higher timeout (e.g., 1s) for slower networks
  • Production: Lower timeout (e.g., 300ms, default) for responsiveness
  • Load testing: Use a pool with sufficient size to avoid queuing

4. Handle Server Limits

If you receive code: "RATE_LIMIT" or "CONCURRENCY_LIMIT":

  • Server is busy or your app exceeded connection limits
  • Response is still action: "allow" (fail-open)
  • Reduce request rate or increase pool size

5. Client IP Behind Proxies

Always ensure your reverse proxy (nginx, ALB, etc.) sets:

X-Forwarded-For: <client-ip>

Or:

X-Real-IP: <client-ip>

Wafio will extract the real client IP automatically.

Troubleshooting

| Issue | Solution | |-------|----------| | "CA PEM is required" | Ensure ca_pem is in the credentials JSON | | "Cannot connect" | Check host, port, and firewall. Verify credentials are valid. | | All requests return "allow" | Client is likely in fail-open cooldown (Wafio unreachable). Check server logs. | | Requests timing out | Increase requestTimeoutMs. Check network latency. | | Getting "block" for normal requests | Check WAF rule configuration on the server side. | | Pool is slow | Increase poolSize. Monitor pool.init() time. | | Can't connect behind corporate proxy | TCP mTLS doesn't support HTTP proxies. Contact your network team. |

Examples

Complete examples are in package-level examples/ folders:

  • packages/wafio-client/examples/client.js – basic usage (Node.js)
  • packages/wafio-client/examples/web-sample/ – complete Express.js middleware
  • packages/wafio-client-go/examples/go-web-sample/ – complete Go middleware sample
  • packages/wafio-client-php/examples/laravel-sample/ – Laravel integration

See Also

  • docs/README.md – documentation index
  • docs/API_ENDPOINTS.md – WAFio management API reference
  • docs/TCP_MTLS_READINESS.md – TCP mTLS runtime guide