sentinel-agent-js

Crates.iosentinel-agent-js
lib.rssentinel-agent-js
version0.1.0
created_at2026-01-13 16:15:33.864461+00
updated_at2026-01-13 16:15:33.864461+00
descriptionJavaScript scripting agent for Sentinel reverse proxy - embed custom JS logic
homepagehttps://sentinel.raskell.io
repositoryhttps://github.com/raskell-io/sentinel-agent-js
max_upload_size
id2040532
size152,541
Raffael Schneider (raffaelschneider)

documentation

https://sentinel.raskell.io/docs/agents/js

README

sentinel-agent-js

JavaScript scripting agent for Sentinel reverse proxy. Write custom request/response processing logic in JavaScript.

Features

  • Execute JavaScript scripts on request/response lifecycle events
  • Fast, lightweight QuickJS engine (via rquickjs)
  • Console API for logging (console.log, console.warn, console.error)
  • Return-based decision model (allow, block, redirect)
  • Header manipulation (add/remove request and response headers)
  • Fail-open mode for graceful error handling

Installation

From crates.io

cargo install sentinel-agent-js

From source

git clone https://github.com/raskell-io/sentinel-agent-js
cd sentinel-agent-js
cargo build --release

Usage

sentinel-js-agent --socket /var/run/sentinel/js.sock \
  --script /etc/sentinel/scripts/handler.js

Command Line Options

Option Environment Variable Description Default
--socket AGENT_SOCKET Unix socket path /tmp/sentinel-js.sock
--script JS_SCRIPT JavaScript script file (required)
--verbose JS_VERBOSE Enable debug logging false
--fail-open FAIL_OPEN Allow requests on script errors false

Writing Scripts

Basic Example

function on_request_headers(request) {
    // Block admin access
    if (request.uri.includes("/admin")) {
        return { decision: "block", status: 403, body: "Forbidden" };
    }

    // Allow all other requests
    return { decision: "allow" };
}

Available Hooks

Hook Description
on_request_headers(request) Called when request headers are received
on_response_headers(response) Called when response headers are received

Request Object

{
    method: "GET",           // HTTP method
    uri: "/api/users",       // Request URI with query string
    client_ip: "192.168.1.1", // Client IP address
    correlation_id: "abc123", // Request correlation ID
    headers: {               // Request headers (name -> value)
        "Content-Type": "application/json",
        "User-Agent": "Mozilla/5.0..."
    }
}

Response Object

{
    status: 200,             // HTTP status code
    correlation_id: "abc123", // Request correlation ID
    headers: {               // Response headers (name -> value)
        "Content-Type": "application/json",
        "X-Custom": "value"
    }
}

Return Values

Scripts should return a decision object:

// Allow the request
return { decision: "allow" };

// Block with custom status and body
return { decision: "block", status: 403, body: "Access Denied" };

// Redirect to another URL
return { decision: "redirect", status: 302, body: "https://example.com/login" };

Header Manipulation

function on_request_headers(request) {
    return {
        decision: "allow",
        add_request_headers: {
            "X-Processed-By": "js-agent",
            "X-Client-IP": request.client_ip
        },
        remove_request_headers: ["X-Debug"],
        add_response_headers: {
            "X-Frame-Options": "DENY"
        }
    };
}

Audit Tags

Add tags for logging and analytics:

function on_request_headers(request) {
    if (request.headers["User-Agent"]?.includes("bot")) {
        return {
            decision: "allow",
            tags: ["bot-detected", "monitoring"]
        };
    }
    return { decision: "allow" };
}

Console Logging

function on_request_headers(request) {
    console.log("Processing request:", request.uri);
    console.warn("Warning message");
    console.error("Error message");
    return { decision: "allow" };
}

Examples

Block Bad User-Agents

function on_request_headers(request) {
    const ua = request.headers["User-Agent"] || "";
    const badBots = ["sqlmap", "nikto", "nessus", "masscan"];

    for (const bot of badBots) {
        if (ua.toLowerCase().includes(bot)) {
            return {
                decision: "block",
                status: 403,
                tags: ["bot-blocked", bot]
            };
        }
    }
    return { decision: "allow" };
}

Rate Limit by Path

function on_request_headers(request) {
    // Add rate limit tier based on path
    let tier = "standard";
    if (request.uri.startsWith("/api/v1/")) {
        tier = "api";
    } else if (request.uri.startsWith("/admin/")) {
        tier = "admin";
    }

    return {
        decision: "allow",
        add_request_headers: {
            "X-Rate-Limit-Tier": tier
        }
    };
}

Require Authentication

function on_request_headers(request) {
    // Skip for public paths
    if (request.uri.startsWith("/public/") || request.uri === "/health") {
        return { decision: "allow" };
    }

    // Check for auth header
    if (!request.headers["Authorization"]) {
        return {
            decision: "block",
            status: 401,
            body: "Authentication required"
        };
    }

    return { decision: "allow" };
}

Add Security Headers

function on_response_headers(response) {
    return {
        decision: "allow",
        add_response_headers: {
            "X-Content-Type-Options": "nosniff",
            "X-Frame-Options": "DENY",
            "X-XSS-Protection": "1; mode=block",
            "Strict-Transport-Security": "max-age=31536000"
        }
    };
}

Sentinel Proxy Configuration

agents {
    agent "js" {
        type "custom"
        transport "unix_socket" {
            path "/var/run/sentinel/js.sock"
        }
        events ["request_headers", "response_headers"]
        timeout-ms 100
        failure-mode "open"
    }
}

Error Handling

When --fail-open is enabled, script errors will:

  • Log the error
  • Allow the request to proceed
  • Add js-error and fail-open tags to audit metadata

When --fail-open is disabled (default), script errors will:

  • Log the error
  • Block the request with 500 status
  • Add js-error tag to audit metadata

Comparison with sentinel-agent-lua

Feature sentinel-agent-js sentinel-agent-lua
Engine QuickJS mlua (Lua 5.4)
Scripting Single script file Multiple scripts with metadata
Hot Reload No Yes
VM Pooling No Yes
Resource Limits Minimal Comprehensive (memory, CPU, time)
Standard Library Basic (console) Rich (JSON, crypto, regex, etc.)
Use Case Simple scripts Production workloads

Use sentinel-agent-js for:

  • Simple request filtering logic
  • Quick prototyping
  • Lightweight deployments

Use sentinel-agent-lua for:

  • Complex processing logic
  • Production environments with strict resource limits
  • Multiple scripts with hot reload

Development

# Run with debug logging
RUST_LOG=debug cargo run -- --socket /tmp/test.sock --script ./test.js --verbose

# Run tests
cargo test

License

Apache-2.0

Commit count: 7

cargo fmt