# Futuristic Webload (BeReplay) - AI Agent Spec (Customer Edition)

## Purpose
This document is a **copy/paste spec** for customer AI assistants (DevOps / QA / Dev) to operate Futuristic Webload reliably.

It is written to be:
- **Exact** (real CLI flags and contracts)
- **Actionable** (includes examples and common workflows)
- **Conservative** (do not invent missing features; if something is unknown, ask for logs/files)

Scope includes:
- CLI usage for `load-test.exe`, `runs-manager.exe`, `shard-run.exe`
- Sharding contract (`shards.json`) and runtime environment variables
- Captcha bypass integration contract
- Database import contract and audit columns

Non-goals:
- Do not describe unfinished features as "supported".
- Do not assume Linux/Docker is supported unless explicitly stated for a specific command/script.

## How to Use This Spec (for AI Assistants)
When a user asks for help, start by requesting the minimum artifacts needed to be accurate:
1. The **exact command line** used (including all flags).
2. The **output folder** (`-outDir`) or a **zip** of the run artifacts.
3. The **script file** (`recording.json`) and, if used, the **databank file** (`-databankFile`).
4. If sharded: the **`shards.json`** and the shard id.

Never guess. If something is ambiguous, ask for the file/log.

## 1. Core Binaries & CLI Contracts

### 1.1 load-test.exe (The Engine)
Primary browser replay and visual script editor.

**Authoritative usage (implemented):**
```text
load-test.exe
load-test.exe -AIKey <key>
load-test.exe -edit <script.json> [-AIKey <key>]
load-test.exe -replay [<script.json>] [-zipInput <file.zip>] [-unzipFolder <folder>] [-databankFile <file.json>] [-identitySerialNo <N>] [-identitiesFolder <folder>] [--dry-run] [-withUX] [-useDatabank <sel>] [-runXUser <N>] [-recycleUsers] [-replaySpeed <X|random>] [-outDir <path>] [-chromePath <path>] [-lighthouse] [-lighthouseUrl <url>] [-baseUrlOverride <url>] [-startUrlOverride <url>] [--video] [-noBrowserSnapshot] [-noVitals] [-maxVitalsPerMinute <N>] [-runtimeMetricsIntervalMs <N>] [-noMousePointer] [-clientCertPassword <password>] [-captchaBypassLicense <token>] [-noDialogRecording] [-orchestrationServer <url>|-saasBaseUrl <url>] [-appShellBaseUrl <url>] [-saasNgrokHeader <value>] [-networkPolicy <path>] [-networkPolicyDefaultAction <allow|block>]

Selectors for -useDatabank:
  1 | random
```

#### 1.1.1 Replay Flag Reference (Implemented)
The following flags exist in the current `load-test.exe` build:

- **Input**
  - `-replay [<script.json>]`: replay mode.
  - `-zipInput <file.zip>`: replay from a zip bundle; the tool extracts and tries to locate `recording.json` (or, as a fallback, the only JSON file in the bundle).
  - `-unzipFolder <folder>`: where to extract `-zipInput` (default: zip folder).
  - `--dry-run`: validate JSON only (requires `-replay <script.json>`; zip mode not supported in dry-run).
  - `-databankFile <file.json>`: override databank users (`parameters.users`) from another JSON file.

- **Execution**
  - `-runXUser <N>`: concurrency / number of users (isolated contexts).
  - `-withUX`: headful browser (visual).
  - `-replaySpeed <X|random>`: scales recorded waits (and can add jitter for `random`).
  - `-outDir <path>`: output root for all artifacts/reports.
  - `-chromePath <path>`: override Chromium/Chrome binary path.
  - `-baseUrlOverride <url>`: override base URL for replay navigation.
  - `-startUrlOverride <url>`: override start URL if a recording has one.

- **Databank selection**
  - `-useDatabank 1`: start at first user (1-based).
  - `-useDatabank random`: randomize the start user (resolved once at run start).
  - `-recycleUsers`: wrap user selection when reaching databank end (soak runs).

- **Orchestration / cloud plumbing**
  - `-runSlot <N>`: sets `RUN_SLOT=<N>` and derives `FWL_RUN_ID=run-<N>`.
  - `-cloud`: enables cloud-style sync logic (used by shard/cloud orchestration).
  - `-cloudTaskKey <key>`: cloud/shard correlation key.
  - `-orchestrationServer <url>`: orchestration server base URL.
  - `-saasBaseUrl <url>` / `-appShellBaseUrl <url>` / `-saasNgrokHeader <value>`: SaaS/AppShell integration hooks (only use if your deployment requires them).

- **Performance / artifacts**
  - `-noVitals`: disable vitals collection.
  - `-maxVitalsPerMinute <N>`: cap vitals sampling rate.
  - `-runtimeMetricsIntervalMs <N>`: sampling interval for runtime metrics.
  - `-noBrowserSnapshot`: disable heavy snapshot artifacts.
  - `--video`: enable video capture (if supported in build/runtime).
  - `-noMousePointer`: hide pointer overlay (if enabled by default).
  - `-noDialogRecording`: disable dialog recording (if enabled).

- **Network policy**
  - `-networkPolicy <path>`: path to network policy file.
  - `-networkPolicyDefaultAction <allow|block>`: default action when no rule matches.

- **Captcha bypass / certificate injection**
  - `-captchaBypassLicense <token>`: Futuristic license token (JWT).
  - `-clientCertPassword <password>`: passphrase for encrypted PEM key (client certificate mode).
  - `-identitySerialNo <N>` / `-identitiesFolder <folder>`: identity injection selectors (when used by your script/policy).

**Key behaviors (do not mis-state):**
- `--dry-run` validates JSON structure **without launching browsers**. It requires `-replay <script.json>` (zip mode is not supported in dry-run).
- `-zipInput` mode extracts the zip, searches for `recording.json` (or if exactly one `.json` exists, it may accept that).
- `-databankFile` is a JSON file and is applied as an **override** into the scriptג€™s `parameters.users`.

**Concurrency / databank selection**
- `-runXUser <N>`: maximum concurrent users (isolated contexts).
- `-useDatabank 1`: start at user #1 (1-based) when a databank exists.
- `-useDatabank random`: choose a random start user (resolved once at start, then deterministic for that run).
- `-recycleUsers`: when databank range is exhausted, wrap around instead of failing.

**Run identity**
- `-runSlot <N>`: sets orchestration slot identity for the current process. When used, runtime also derives:
  - `RUN_SLOT=<N>`
  - `FWL_RUN_ID=run-<N>`

**Instrumentation controls**
- `-noVitals`: disables browser vitals collection.
- `-noBrowserSnapshot`: disables heavy snapshot artifacts (screenshots/trace snapshots).
- `--video`: enables video capture (if supported by the current build/runtime).

**Precedence rule (implemented reality)**
- Many settings have **CLI flags**.
- Some have **environment-variable fallbacks** when CLI value is not provided (examples: `FWL_CLIENT_CERT_PASSWORD`, `FWL_CAPTCHA_BYPASS_LICENSE`, `FWL_NETWORK_POLICY_PATH`, `FWL_NETWORK_POLICY_DEFAULT_ACTION`, identity injection env vars).
- Treat precedence as: **CLI flag overrides env fallback**. Do not claim a universal `CLI > JSON > env` rule for every option.

**Common examples**
```text
# Edit (UX)
load-test.exe
load-test.exe -edit recording.json

# Replay headless (single user)
load-test.exe -replay recording.json -runXUser 1 -outDir "C:\Runs\run-1"

# Replay headful (visual) for troubleshooting (not a separate "debug mode")
load-test.exe -replay recording.json -withUX -runXUser 1 -replaySpeed 0.5

# Validate JSON only
load-test.exe -replay recording.json --dry-run

# Replay from a zip bundle (advanced)
load-test.exe -replay -zipInput "C:\Artifacts\bundle.zip"
```

### 1.2 runs-manager.exe (Control Hub)
Local API server (Kestrel) and bulk database importer.
- **API Defaults**: `http://127.0.0.1:5057`. Enforces localhost-only access.

**Local-only security**
- Runs-Manager rejects any request that does not originate from loopback (local machine). Even if `--urls` binds to a LAN/WAN address, it returns `403` for non-loopback clients.

#### 1.2.1 Running the UI (Interactive)
```text
# Default (binds to http://127.0.0.1:5057)
runs-manager.exe

# Override bind URL/port (still rejects non-loopback clients)
runs-manager.exe --urls "http://127.0.0.1:8080"
```

**DB Import CLI (implemented flags)**
```text
# Windows / PowerShell (line continuation with backtick)
runs-manager.exe --noUX --ImportToDatabase `
  --Database <mysql|mssql|oracle> `
  --ConnectionString "<provider string>" `
  --WebLoadDir "<path to run root>" `
  [--TableConflictPolicy <FailAllOnAnyBlockedTable|ContinueWithValidTables>] `
  [--RuntimeErrorPolicy <StopOnFirstDatasetError|ContinueOnDatasetError>] `
  [--TableMappingsJSON "<json object>"] `
  [--SelectedDatasetsJSON "<json array>"]
```

Important: `--TableMappingsJSON` and `--SelectedDatasetsJSON` are **inline JSON strings**, not file paths.

**Examples**
```text
# Import everything using default table naming (FWL_<dataset>)
runs-manager.exe --noUX --ImportToDatabase `
  --Database mysql `
  --ConnectionString "server=localhost;user=root;password=...;database=fwl_results" `
  --WebLoadDir "C:\Runs\run-001" `
  --TableConflictPolicy FailAllOnAnyBlockedTable `
  --RuntimeErrorPolicy StopOnFirstDatasetError

# Map selected datasets to specific tables
runs-manager.exe --noUX --ImportToDatabase `
  --Database mysql `
  --ConnectionString "server=localhost;user=root;password=...;database=fwl_results" `
  --WebLoadDir "C:\Runs\run-001" `
  --TableMappingsJSON "{\"http_report.tsv\":\"Production_Traffic_Logs\",\"steps_report.tsv\":\"QA_Test_Steps_Historical\"}"

# Import only a subset of datasets
runs-manager.exe --noUX --ImportToDatabase `
  --Database mysql `
  --ConnectionString "server=localhost;user=root;password=...;database=fwl_results" `
  --WebLoadDir "C:\Runs\run-001" `
  --SelectedDatasetsJSON "[\"http_report.tsv\",\"steps_report.tsv\",\"js_errors.tsv\"]"
```

#### 1.2.2 Import Exit Codes (Implemented)
Runs-Manager import returns these process exit codes:
- `0`: success (or nothing to import, and nothing blocked).
- `10`: partial success / blocked datasets exist (for example: some missing files or tables blocked).
- `20`: preflight blocked by conflict policy (for example: tables exist but policy forbids continuing).
- `30`: runtime dataset import error (policy-dependent).
- `40`: invalid request / missing required inputs.

#### 1.2.3 Import Output (What to Parse)
In `--ImportToDatabase` mode, Runs-Manager writes a JSON result object to stdout (includes `exitCode`, `batchId`, `preflight[]`, `execution[]`). In CI pipelines:
- Treat the **process exit code** as the primary success signal.
- Use the JSON stdout to produce human-readable logs or dashboards.

#### 1.2.4 Default Datasets (When Not Using SelectedDatasetsJSON)
If `--SelectedDatasetsJSON` is not provided, the importer attempts a default dataset set including (not exhaustive here, but common):
- `http_report.tsv`, `http_grouped.tsv`, `steps_report.tsv`
- `js_errors.tsv`, `navigation_vitals.tsv`, `step_vitals.tsv`
- `browser_runtime_metrics.tsv`, `host_runtime_metrics.tsv`
- snapshot metadata datasets like `snapshot_html.meta`, `snapshot_image.meta`, `snapshot_zip.meta`

### 1.3 shard-run.exe (Orchestrator)
Distributed wrapper for horizontal scaling.

**Authoritative usage (implemented):**
```text
shard-run.exe -shard <shards.json> -input <input.zip> -shard-id <N> [-outRoot <folder>] [-dryrun] [-- <extra load-test args>]
```

Notes:
- `-shard-id` is **1-based**.
- Shard-Run launches `load-test.exe` with:
  - `-replay -zipInput <input.zip>`
  - `-runXUser <usersInShard>`
  - `-useDatabank <userStart>` (1-based)
  - `-outDir <outRoot>\<outDirRelative>` (defaults to `run-<runGroupId>` when not provided)
- Shard-Run sets env vars for the child process:
  - `FWL_CLOUD_TASK_KEY` (from `shards.json`)
  - `FWL_RUN_ID=run-<runGroupId>`
  - `RUN_SLOT=run-<runGroupId>` (compat)
  - `FWL_RECYCLE_USERS=1|0`

**Example**
```text
shard-run.exe -shard shards.json -input input.zip -shard-id 1 -- -noVitals -noBrowserSnapshot
```

Tip: `-dryrun` prints the derived environment variables and the `load-test.exe` command line without executing it.

---

## 2. JSON Data Contracts

### 2.1 recording.json (Script Model)

At minimum, `load-test.exe --dry-run` expects:
- `scripts`: an array
- Optional: `parameters.users`: an array (databank)

**Minimal shape (validated by dry-run)**
```json
{
  "scripts": [
    {
      "scriptId": "AnyUniqueId",
      "steps": []
    }
  ],
  "parameters": {
    "users": []
  }
}
```

#### 2.1.1 Full Script File Shape (Key Fields)
`recording.json` is deserialized as a `ReplayScriptFile` with these top-level fields:
- `scripts[]`: the list of scripts (`scriptId`, `steps[]`).
- `parameters.users[]`: databank user rows (`userId`, `data{ key: value }`).
- `runs[]`: optional run-level configuration (`runId`, `scriptId`, `baseUrl`, output settings, captcha settings, etc.).

**Common pattern**
```json
{
  "meta": { "name": "Checkout smoke", "createdAt": "2026-05-01T12:00:00Z" },
  "scripts": [
    { "scriptId": "Checkout", "description": "Happy path", "steps": [ /* ... */ ] }
  ],
  "parameters": {
    "users": [
      { "userId": "1", "data": { "email": "a@acme.com", "password": "REDACTED" } }
    ]
  },
  "runs": [
    {
      "runId": "Default",
      "scriptId": "Checkout",
      "baseUrl": "https://staging.acme.com",
      "headless": true,
      "concurrency": 1,
      "outDir": "C:\\Runs\\run-001"
    }
  ]
}
```

### 2.2 shards.json (Distribution Config)
Shard-Run expects a root object with:
- `cloudTaskKey` (string, required)
- `outRoot` (string, required)
- `orchestrationServer` (string, optional)
- `recycleUsers` (bool, optional)
- `shards` (array, required), each shard contains:
  - `shardId` (number, required)
  - `runGroupId` (number, optional; defaults to shardId)
  - `userStart` (number, optional; defaults to 1)
  - `usersInShard` (number, required; must be > 0)
  - `outDirRelative` (string, optional; defaults to `run-<runGroupId>`)

```json
{
  "cloudTaskKey": "your-cloud-task-key",
  "outRoot": "C:\\Runs\\Out",
  "orchestrationServer": "http://127.0.0.1:5057",
  "recycleUsers": true,
  "shards": [
    {
      "shardId": 1,
      "runGroupId": 1,
      "userStart": 1,
      "usersInShard": 100,
      "outDirRelative": "run-1"
    }
  ]
}
```

### 2.3 TableMappingsJSON (DB Mapping)
Runs-Manager import accepts **inline JSON** mapping dataset filenames to table names:
```json
{
  "http_report.tsv": "FWL_Raw_Traffic",
  "js_errors.tsv": "Production_Regressions"
}
```

---

## 3. Captcha Bypass Integration

### 3.1 Protocol Contract
- **Header name**: `X-FWL-Captcha-Bypass`
- **Token type**: JWT signed as **RS256** (public-key validation via JWKS).
- **JWKS endpoint**: `https://www.futuristicwebload.com/api/v1/licensing/captcha/jwks.json`
- **JWT Claims (expected)**:
    - `iss`: `FuturisticWebLoad`
    - `aud`: `futuristic-webload` (or your configured audience)
    - `allowedHosts`: list of allowed hostnames (your backend should enforce host match)
    - `alg`: `RS256`

### 3.2 Token Delivery (What load-test.exe Uses)
When a script is configured for Futuristic captcha bypass, `load-test.exe` can obtain the bypass token from:
1. `-captchaBypassLicense <token>` (CLI)
2. `FWL_CAPTCHA_BYPASS_LICENSE` (environment variable)
3. License files (checked in both the app base folder and the script folder):
   - `.\Licenses\captcha_bypass.lic`
   - `.\Licenses\captcha-bypass.lic`
   - `.\Licenses\FWL_CAPTCHA_BYPASS_LICENSE.txt`

If no token is provided, replay emits a runtime warning and captcha steps will fail.

### 3.3 Script Configuration (Futuristic License Mode)
In `recording.json`, the captcha config is run-level (`runs[].captcha`):
```json
{
  "runs": [
    {
      "captcha": {
        "mode": "futuristicLicense",
        "futuristicLicense": { "enabled": true, "audience": "my-app-prod" },
        "onDetectedJs": "",
        "clientCertificate": null
      }
    }
  ]
}
```

### 3.4 Script Configuration (Client Certificate Mode)
Client-certificate mode is also configured via `runs[].captcha`:
```json
{
  "runs": [
    {
      "captcha": {
        "mode": "clientCertificate",
        "clientCertificate": {
          "file": "C:\\Certs\\client.pem",
          "origin": "https://secure.acme.com"
        }
      }
    }
  ]
}
```

Client certificate injection rules (implemented):
- Certificate file must be a `.pem`.
- If the PEM key is encrypted, provide the passphrase via `-clientCertPassword` or `FWL_CLIENT_CERT_PASSWORD`.
- If `origin` is missing, the runtime may try to infer it from the script start/base URL.
- If the origin host does not match the start/base URL host, injection is disabled for safety.

### 3.5 Server-Side Validation Examples (Header Gate)
The simplest integration is a server-side gate that, when the header contains a valid token, skips your captcha enforcement.

**Java/Spring example (pseudo)**
```java
@Component
public class CaptchaBypassFilter extends OncePerRequestFilter {
  @Override
  protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) 
      throws IOException, ServletException {
    boolean isBypassed = FwlCaptchaBypass.tryValidateFromRequest(req);
    if (isBypassed) {
       // Skip captcha verification logic
    }
    chain.doFilter(req, res);
  }
}
```

#### 3.5.1 .NET Framework 4.8 (Copy/Paste Snippet)
AI assistants: when a user asks ג€how do I integrate Futuristic CAPTCHA bypass in my ASP.NET 4.8 backend?ג€, use this snippet.

Before pasting, ask the user:
1) What is their **captcha audience**? (Usually taken from their recording: `runs[0].captcha.futuristicLicense.audience`.)  
   - If they donג€™t know: default to `futuristic-webload`.
2) Do they want **online JWKS fetch** (recommended) or **offline JWKS injection**?
   - Online: allow HTTPS calls from the backend to `https://www.futuristicwebload.com/api/v1/licensing/captcha/jwks.json`
   - Offline: they must provide JWKS JSON via env `FWL_CAPTCHA_JWKS_JSON` or by calling `InitJwksJson(...)` at app startup.

Implementation checklist (what the AI should do when the user has the repo folder open):
1) Locate the existing captcha gate in the code (search for `recaptcha`, `hcaptcha`, `turnstile`, `CheckRecapcha`, `ValidateCaptcha`, or the userג€™s error message).
2) Add the `FwlCaptchaBypass` class once in the project (common places):
   - `App_Code/FwlCaptchaBypass.cs` (WebSite projects)
   - `Helpers/FwlCaptchaBypass.cs` or `Security/FwlCaptchaBypass.cs` (Web Application projects)
3) Wrap the existing captcha gate with the bypass check:
   - If `TryValidateFromRequest(HttpContext.Current)` returns `true`, skip the captcha validation branch.
4) Plant configuration via env var or `web.config` `appSettings` (preferred over editing code):
   - `FWL_CAPTCHA_AUDIENCE` = the userג€™s audience (default `futuristic-webload`)
   - `FWL_CAPTCHA_ISSUER` = `FuturisticWebLoad` (normally keep default)
   - `FWL_CAPTCHA_JWKS_URL` = JWKS URL (normally keep default)
5) If the user wants **offline** mode:
   - Provide JWKS JSON via env var `FWL_CAPTCHA_JWKS_JSON` (recommended) OR call `InitJwksJson(jwksJson)` at application startup (e.g., `Global.asax Application_Start`).
   - Important: if `FWL_CAPTCHA_JWKS_JSON` is missing and `InitJwksJson(...)` is not called, the snippet will attempt HTTPS JWKS fetch at runtime.

How to ג€plantג€ user-specific values:
- Prefer **configuration** (env var or `appSettings`) over editing code.
  - `FWL_CAPTCHA_AUDIENCE` (or `appSettings["FWL_CAPTCHA_AUDIENCE"]`) should equal the userג€™s audience string.
  - `FWL_CAPTCHA_ISSUER` should usually remain `FuturisticWebLoad`.
  - `FWL_CAPTCHA_JWKS_URL` can override the JWKS URL if their network uses a proxy/mirror.
  - `FWL_CAPTCHA_JWKS_JSON` is the full JWKS JSON string for offline validation (no network calls needed if provided).

```csharp
/*
Futuristic CAPTCHA bypass (RS256 + JWKS) for ASP.NET .NET Framework 4.8

- This class validates the request header: X-FWL-Captcha-Bypass: <JWT>
- Online mode (default): fetches JWKS over HTTPS from Futuristic SaaS and caches it.
- Offline mode: set env var FWL_CAPTCHA_JWKS_JSON or call FwlCaptchaBypass.InitJwksJson(jwksJson) at app startup.

Usage example:

    if (!FwlCaptchaBypass.TryValidateFromRequest(HttpContext.Current))
    {
        if (!UserAPI.CheckRecapcha(Capcha))
        {
            Result.DisplayMessage = "Please confirm that you are not a robot.";
            Result.ActionCode = "Captcha";
            return false;
        }
    }
*/

using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Web;

public static class FwlCaptchaBypass
{
    private const string HeaderName = "X-FWL-Captcha-Bypass";
    private const string DefaultJwksUrl = "https://www.futuristicwebload.com/api/v1/licensing/captcha/jwks.json";
    private const string DefaultIssuer = "FuturisticWebLoad";
    private const int DefaultClockSkewSeconds = 60;

    private static readonly object CacheLock = new object();
    private static string _cachedJwksUrl = string.Empty;
    private static Dictionary<string, RSA> _cachedKeys = null;
    private static DateTime _cachedKeysValidUntilUtc = DateTime.MinValue;
    private static string _injectedJwksJson = null;

    // Offline: call at app startup (Global.asax) to avoid HTTPS JWKS fetch at runtime.
    public static void InitJwksJson(string jwksJson)
    {
        lock (CacheLock)
        {
            _injectedJwksJson = string.IsNullOrWhiteSpace(jwksJson) ? null : jwksJson;
            _cachedJwksUrl = string.Empty;
            _cachedKeys = null;
            _cachedKeysValidUntilUtc = DateTime.MinValue;
        }
    }

    public static bool TryValidateFromRequest(HttpContext ctx)
    {
        try
        {
            if (ctx == null || ctx.Request == null) return false;

            var token = (ctx.Request.Headers[HeaderName] ?? string.Empty).Trim();
            if (string.IsNullOrWhiteSpace(token)) return false;

            var requestHost = string.Empty;
            try { requestHost = (ctx.Request.Url != null ? (ctx.Request.Url.Host ?? string.Empty) : string.Empty) ?? string.Empty; } catch { }

            var jwksUrl = GetSetting("FWL_CAPTCHA_JWKS_URL");
            if (string.IsNullOrWhiteSpace(jwksUrl)) jwksUrl = DefaultJwksUrl;

            var issuer = GetSetting("FWL_CAPTCHA_ISSUER");
            if (string.IsNullOrWhiteSpace(issuer)) issuer = DefaultIssuer;

            var audience = GetSetting("FWL_CAPTCHA_AUDIENCE");
            if (string.IsNullOrWhiteSpace(audience)) audience = "futuristic-webload";

            var keys = GetJwksKeys(jwksUrl);
            if (keys == null || keys.Count == 0) return false;

            return ValidateJwtRs256(token, keys, issuer, audience, requestHost);
        }
        catch
        {
            return false;
        }
    }

    private static string GetSetting(string key)
    {
        try { var v = (Environment.GetEnvironmentVariable(key) ?? string.Empty).Trim(); if (!string.IsNullOrWhiteSpace(v)) return v; } catch { }
        try { var v = (System.Configuration.ConfigurationManager.AppSettings[key] ?? string.Empty).Trim(); if (!string.IsNullOrWhiteSpace(v)) return v; } catch { }
        return string.Empty;
    }

    private static Dictionary<string, RSA> GetJwksKeys(string jwksUrl)
    {
        var url = (jwksUrl ?? string.Empty).Trim();
        if (string.IsNullOrWhiteSpace(url)) return new Dictionary<string, RSA>(StringComparer.Ordinal);

        var injected = _injectedJwksJson;
        if (string.IsNullOrWhiteSpace(injected))
        {
            try { injected = (Environment.GetEnvironmentVariable("FWL_CAPTCHA_JWKS_JSON") ?? string.Empty).Trim(); } catch { injected = string.Empty; }
        }

        lock (CacheLock)
        {
            if (_cachedKeys != null && _cachedKeys.Count > 0 &&
                string.Equals(_cachedJwksUrl, url, StringComparison.OrdinalIgnoreCase) &&
                DateTime.UtcNow < _cachedKeysValidUntilUtc)
            {
                return _cachedKeys;
            }
        }

        var json = !string.IsNullOrWhiteSpace(injected) ? injected : DownloadString(url);
        if (string.IsNullOrWhiteSpace(json)) return new Dictionary<string, RSA>(StringComparer.Ordinal);

        var keys = ParseJwks(json);

        lock (CacheLock)
        {
            _cachedJwksUrl = url;
            _cachedKeys = keys;
            _cachedKeysValidUntilUtc = DateTime.UtcNow.AddMinutes(30);
        }

        return keys;
    }

    private static string DownloadString(string url)
    {
        if (string.IsNullOrWhiteSpace(url)) return string.Empty;
        try
        {
            var req = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url);
            req.Method = "GET";
            req.Timeout = 5000;
            req.ReadWriteTimeout = 5000;

            using (var res = (System.Net.HttpWebResponse)req.GetResponse())
            using (var stream = res.GetResponseStream())
            using (var reader = new System.IO.StreamReader(stream))
            {
                return reader.ReadToEnd();
            }
        }
        catch
        {
            return string.Empty;
        }
    }

    private static Dictionary<string, RSA> ParseJwks(string jwksJson)
    {
        var result = new Dictionary<string, RSA>(StringComparer.Ordinal);
        try
        {
            var ser = new System.Web.Script.Serialization.JavaScriptSerializer();
            var root = ser.DeserializeObject(jwksJson) as Dictionary<string, object>;
            if (root == null || !root.ContainsKey("keys")) return result;

            var keysObj = root["keys"] as object[];
            if (keysObj == null) return result;

            foreach (var item in keysObj)
            {
                var dict = item as Dictionary<string, object>;
                if (dict == null) continue;

                var kty = GetString(dict, "kty");
                if (!string.Equals(kty, "RSA", StringComparison.OrdinalIgnoreCase)) continue;

                var kid = GetString(dict, "kid");
                var n = GetString(dict, "n");
                var e = GetString(dict, "e");
                if (string.IsNullOrWhiteSpace(kid) || string.IsNullOrWhiteSpace(n) || string.IsNullOrWhiteSpace(e)) continue;

                var rsa = RSA.Create();
                rsa.ImportParameters(new RSAParameters { Modulus = Base64UrlDecode(n), Exponent = Base64UrlDecode(e) });
                result[kid] = rsa;
            }
        }
        catch
        {
            // ignore
        }
        return result;
    }

    private static string GetString(Dictionary<string, object> dict, string key)
    {
        try { if (dict != null && dict.ContainsKey(key) && dict[key] != null) return Convert.ToString(dict[key]) ?? string.Empty; } catch { }
        return string.Empty;
    }

    private static long GetLong(Dictionary<string, object> dict, string key)
    {
        try
        {
            if (dict != null && dict.ContainsKey(key) && dict[key] != null)
            {
                var o = dict[key];
                if (o is long) return (long)o;
                if (o is int) return (int)o;
                if (o is double) return (long)(double)o;
                if (o is string) { long v; if (long.TryParse((string)o, out v)) return v; }
            }
        }
        catch { }
        return 0;
    }

    private static bool PayloadAudienceMatches(Dictionary<string, object> payload, string expectedAud)
    {
        try
        {
            if (payload == null || !payload.ContainsKey("aud") || payload["aud"] == null) return true;

            var audObj = payload["aud"];
            if (audObj is string) return string.Equals(expectedAud, (string)audObj, StringComparison.Ordinal);

            var arr = audObj as object[];
            if (arr != null)
            {
                foreach (var a in arr)
                {
                    var s = a != null ? a.ToString() : string.Empty;
                    if (string.Equals(expectedAud, s, StringComparison.Ordinal)) return true;
                }
                return false;
            }
        }
        catch { }
        return true;
    }

    private static bool AllowedHostsMatch(Dictionary<string, object> payload, string requestHost)
    {
        try
        {
            if (payload == null || !payload.ContainsKey("allowedHosts") || payload["allowedHosts"] == null) return true;

            var host = (requestHost ?? string.Empty).Trim().TrimEnd('.').ToLowerInvariant();
            if (string.IsNullOrWhiteSpace(host)) return false;

            var allowed = payload["allowedHosts"];
            if (allowed is string)
            {
                var s = ((string)allowed).Trim().TrimEnd('.');
                return string.Equals(host, s, StringComparison.OrdinalIgnoreCase);
            }

            var arr = allowed as object[];
            if (arr != null)
            {
                foreach (var a in arr)
                {
                    var s = (a != null ? a.ToString() : string.Empty).Trim().TrimEnd('.');
                    if (string.Equals(host, s, StringComparison.OrdinalIgnoreCase)) return true;
                }
                return false;
            }
        }
        catch { }
        return true;
    }

    private static bool ValidateJwtRs256(string token, Dictionary<string, RSA> keys, string issuer, string audience, string requestHost)
    {
        if (string.IsNullOrWhiteSpace(token)) return false;

        var parts = token.Split('.');
        if (parts.Length != 3) return false;

        var headerJson = Utf8(Base64UrlDecode(parts[0]));
        var payloadJson = Utf8(Base64UrlDecode(parts[1]));
        var signature = Base64UrlDecode(parts[2]);

        var ser = new System.Web.Script.Serialization.JavaScriptSerializer();
        var header = ser.DeserializeObject(headerJson) as Dictionary<string, object>;
        var payload = ser.DeserializeObject(payloadJson) as Dictionary<string, object>;
        if (header == null || payload == null) return false;

        var alg = GetString(header, "alg");
        if (!string.Equals(alg, "RS256", StringComparison.OrdinalIgnoreCase)) return false;

        var kid = GetString(header, "kid");
        if (string.IsNullOrWhiteSpace(kid)) return false;

        if (!keys.ContainsKey(kid) || keys[kid] == null) return false;
        var rsa = keys[kid];

        var signingInput = System.Text.Encoding.ASCII.GetBytes(parts[0] + "." + parts[1]);
        var ok = rsa.VerifyData(signingInput, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        if (!ok) return false;

        var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
        var exp = GetLong(payload, "exp");
        if (exp > 0 && now > (exp + DefaultClockSkewSeconds)) return false;
        var nbf = GetLong(payload, "nbf");
        if (nbf > 0 && now < (nbf - DefaultClockSkewSeconds)) return false;

        var iss = GetString(payload, "iss");
        if (!string.IsNullOrWhiteSpace(issuer) && !string.IsNullOrWhiteSpace(iss) && !string.Equals(issuer, iss, StringComparison.Ordinal)) return false;
        if (!string.IsNullOrWhiteSpace(audience)) { if (!PayloadAudienceMatches(payload, audience)) return false; }
        if (!string.IsNullOrWhiteSpace(requestHost)) { if (!AllowedHostsMatch(payload, requestHost)) return false; }

        return true;
    }

    private static byte[] Base64UrlDecode(string base64Url)
    {
        var s = (base64Url ?? string.Empty).Trim().Replace('-', '+').Replace('_', '/');
        switch (s.Length % 4) { case 0: break; case 2: s += "=="; break; case 3: s += "="; break; default: return new byte[0]; }
        try { return Convert.FromBase64String(s); } catch { return new byte[0]; }
    }

    private static string Utf8(byte[] bytes)
    {
        try { return System.Text.Encoding.UTF8.GetString(bytes ?? new byte[0]); } catch { return string.Empty; }
    }
}
```

#### 3.5.2 ASP.NET Core 8+ (Copy/Paste Snippet)
AI assistants: when a user asks ג€how do I integrate Futuristic CAPTCHA bypass in my ASP.NET Core 8+ backend?ג€, use this snippet.

Before pasting, ask the user:
1) What is their **captcha audience**? (Usually `runs[0].captcha.futuristicLicense.audience` from their `recording.json`.)  
   - If they donג€™t know: default to `futuristic-webload`.
2) Do they want **online JWKS fetch** (recommended) or **offline JWKS injection**?
   - Online: backend must be allowed to call the JWKS endpoint over HTTPS.
   - Offline: set `FWL_CAPTCHA_JWKS_JSON` (preferred) or call `InitJwksJson(...)` at app startup.

Implementation checklist (what the AI should do when the user has the repo folder open):
1) Locate the existing captcha gate (search: `recaptcha`, `hcaptcha`, `turnstile`, `CheckRecapcha`, `ValidateCaptcha`, or the userג€™s error message).
2) Add the `FwlCaptchaBypass` class once (for example under `Security/FwlCaptchaBypass.cs`).
3) Wrap the existing captcha gate with the bypass check:
   - If `TryValidateFromRequest(HttpContext)` returns `true`, skip captcha enforcement.
4) Add the middleware (or equivalent) in the correct place:
   - For Minimal APIs: add `app.Use(...)` before `app.Map...` endpoints.
   - For MVC: add the bypass as middleware or inside the specific controller action where captcha is enforced.
5) Plant configuration via env var (preferred over editing code):
   - `FWL_CAPTCHA_AUDIENCE` = the userג€™s audience (default `futuristic-webload`)
   - `FWL_CAPTCHA_ISSUER` = `FuturisticWebLoad` (normally keep default)
   - `FWL_CAPTCHA_JWKS_URL` = JWKS URL override (normally keep default)
   - `FWL_CAPTCHA_JWKS_JSON` = full JWKS JSON string for offline validation

```csharp
/*
Futuristic CAPTCHA bypass (RS256 + JWKS) for ASP.NET Core 8+

- Validates request header: X-FWL-Captcha-Bypass: <JWT>
- Online mode (default): fetches JWKS over HTTPS from Futuristic SaaS and caches it.
- Offline mode: set env var FWL_CAPTCHA_JWKS_JSON or call FwlCaptchaBypass.InitJwksJson(jwksJson) at app startup.
*/

using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Http;

public static class FwlCaptchaBypass
{
    private const string HeaderName = "X-FWL-Captcha-Bypass";
    private const string DefaultJwksUrl = "https://www.futuristicwebload.com/api/v1/licensing/captcha/jwks.json";
    private const string DefaultIssuer = "FuturisticWebLoad";
    private const int DefaultClockSkewSeconds = 60;

    private static readonly object CacheLock = new();
    private static string _cachedJwksUrl = string.Empty;
    private static Dictionary<string, RSA>? _cachedKeys;
    private static DateTime _cachedKeysValidUntilUtc = DateTime.MinValue;
    private static string? _injectedJwksJson;

    public static void InitJwksJson(string jwksJson)
    {
        lock (CacheLock)
        {
            _injectedJwksJson = string.IsNullOrWhiteSpace(jwksJson) ? null : jwksJson;
            _cachedJwksUrl = string.Empty;
            _cachedKeys = null;
            _cachedKeysValidUntilUtc = DateTime.MinValue;
        }
    }

    public static bool TryValidateFromRequest(HttpContext ctx)
    {
        try
        {
            var token = (ctx.Request.Headers[HeaderName].ToString() ?? string.Empty).Trim();
            if (string.IsNullOrWhiteSpace(token)) return false;

            var requestHost = (ctx.Request.Host.Host ?? string.Empty).Trim();

            var jwksUrl = (Environment.GetEnvironmentVariable("FWL_CAPTCHA_JWKS_URL") ?? string.Empty).Trim();
            if (string.IsNullOrWhiteSpace(jwksUrl)) jwksUrl = DefaultJwksUrl;

            var issuer = (Environment.GetEnvironmentVariable("FWL_CAPTCHA_ISSUER") ?? string.Empty).Trim();
            if (string.IsNullOrWhiteSpace(issuer)) issuer = DefaultIssuer;

            var audience = (Environment.GetEnvironmentVariable("FWL_CAPTCHA_AUDIENCE") ?? string.Empty).Trim();
            if (string.IsNullOrWhiteSpace(audience)) audience = "futuristic-webload";

            var keys = GetJwksKeys(jwksUrl).GetAwaiter().GetResult();
            if (keys.Count == 0) return false;

            return ValidateJwtRs256(token, keys, issuer, audience, requestHost);
        }
        catch { return false; }
    }

    private static async System.Threading.Tasks.Task<Dictionary<string, RSA>> GetJwksKeys(string jwksUrl)
    {
        var url = (jwksUrl ?? string.Empty).Trim();
        if (string.IsNullOrWhiteSpace(url)) return new Dictionary<string, RSA>(StringComparer.Ordinal);

        var injected = _injectedJwksJson;
        if (string.IsNullOrWhiteSpace(injected))
        {
            injected = (Environment.GetEnvironmentVariable("FWL_CAPTCHA_JWKS_JSON") ?? string.Empty).Trim();
        }

        lock (CacheLock)
        {
            if (_cachedKeys != null && _cachedKeys.Count > 0 &&
                string.Equals(_cachedJwksUrl, url, StringComparison.OrdinalIgnoreCase) &&
                DateTime.UtcNow < _cachedKeysValidUntilUtc)
            {
                return _cachedKeys;
            }
        }

        var json = !string.IsNullOrWhiteSpace(injected) ? injected : await DownloadStringAsync(url);
        if (string.IsNullOrWhiteSpace(json)) return new Dictionary<string, RSA>(StringComparer.Ordinal);

        var keys = ParseJwks(json);
        lock (CacheLock)
        {
            _cachedJwksUrl = url;
            _cachedKeys = keys;
            _cachedKeysValidUntilUtc = DateTime.UtcNow.AddMinutes(30);
        }

        return keys;
    }

    private static async System.Threading.Tasks.Task<string> DownloadStringAsync(string url)
    {
        try
        {
            using var http = new System.Net.Http.HttpClient();
            using var res = await http.GetAsync(url);
            if (!res.IsSuccessStatusCode) return string.Empty;
            return await res.Content.ReadAsStringAsync();
        }
        catch { return string.Empty; }
    }

    private static Dictionary<string, RSA> ParseJwks(string jwksJson)
    {
        var result = new Dictionary<string, RSA>(StringComparer.Ordinal);
        try
        {
            using var doc = JsonDocument.Parse(jwksJson);
            if (!doc.RootElement.TryGetProperty("keys", out var keysEl) || keysEl.ValueKind != JsonValueKind.Array) return result;
            foreach (var k in keysEl.EnumerateArray())
            {
                var kty = k.TryGetProperty("kty", out var ktyEl) ? (ktyEl.GetString() ?? string.Empty) : string.Empty;
                if (!string.Equals(kty, "RSA", StringComparison.OrdinalIgnoreCase)) continue;
                var kid = k.TryGetProperty("kid", out var kidEl) ? (kidEl.GetString() ?? string.Empty) : string.Empty;
                var n = k.TryGetProperty("n", out var nEl) ? (nEl.GetString() ?? string.Empty) : string.Empty;
                var e = k.TryGetProperty("e", out var eEl) ? (eEl.GetString() ?? string.Empty) : string.Empty;
                if (string.IsNullOrWhiteSpace(kid) || string.IsNullOrWhiteSpace(n) || string.IsNullOrWhiteSpace(e)) continue;
                var rsa = RSA.Create();
                rsa.ImportParameters(new RSAParameters { Modulus = Base64UrlDecode(n), Exponent = Base64UrlDecode(e) });
                result[kid] = rsa;
            }
        }
        catch { }
        return result;
    }

    private static bool ValidateJwtRs256(string token, Dictionary<string, RSA> keys, string issuer, string audience, string requestHost)
    {
        var parts = token.Split('.');
        if (parts.Length != 3) return false;

        var headerJson = Utf8(Base64UrlDecode(parts[0]));
        var payloadJson = Utf8(Base64UrlDecode(parts[1]));
        var signature = Base64UrlDecode(parts[2]);

        using var headerDoc = JsonDocument.Parse(headerJson);
        using var payloadDoc = JsonDocument.Parse(payloadJson);
        var header = headerDoc.RootElement;
        var payload = payloadDoc.RootElement;

        var alg = header.TryGetProperty("alg", out var algEl) ? (algEl.GetString() ?? string.Empty) : string.Empty;
        if (!string.Equals(alg, "RS256", StringComparison.OrdinalIgnoreCase)) return false;

        var kid = header.TryGetProperty("kid", out var kidEl) ? (kidEl.GetString() ?? string.Empty) : string.Empty;
        if (string.IsNullOrWhiteSpace(kid) || !keys.TryGetValue(kid, out var rsa) || rsa == null) return false;

        var signingInput = Encoding.ASCII.GetBytes(parts[0] + "." + parts[1]);
        if (!rsa.VerifyData(signingInput, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)) return false;

        var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
        var exp = GetLong(payload, "exp");
        if (exp > 0 && now > (exp + DefaultClockSkewSeconds)) return false;
        var nbf = GetLong(payload, "nbf");
        if (nbf > 0 && now < (nbf - DefaultClockSkewSeconds)) return false;

        var iss = payload.TryGetProperty("iss", out var issEl) ? (issEl.GetString() ?? string.Empty) : string.Empty;
        if (!string.IsNullOrWhiteSpace(issuer) && !string.IsNullOrWhiteSpace(iss) && !string.Equals(issuer, iss, StringComparison.Ordinal)) return false;

        if (!string.IsNullOrWhiteSpace(audience) && payload.TryGetProperty("aud", out var audEl))
        {
            if (audEl.ValueKind == JsonValueKind.String && !string.Equals(audience, audEl.GetString() ?? string.Empty, StringComparison.Ordinal)) return false;
        }

        if (!string.IsNullOrWhiteSpace(requestHost) && payload.TryGetProperty("allowedHosts", out var hostsEl) && hostsEl.ValueKind == JsonValueKind.Array)
        {
            var host = requestHost.Trim().TrimEnd('.').ToLowerInvariant();
            var ok = false;
            foreach (var h in hostsEl.EnumerateArray())
            {
                if (h.ValueKind != JsonValueKind.String) continue;
                var s = (h.GetString() ?? string.Empty).Trim().TrimEnd('.').ToLowerInvariant();
                if (string.Equals(host, s, StringComparison.OrdinalIgnoreCase)) { ok = true; break; }
            }
            if (!ok) return false;
        }

        return true;
    }

    private static long GetLong(JsonElement payload, string name)
    {
        try
        {
            if (!payload.TryGetProperty(name, out var el)) return 0;
            if (el.ValueKind == JsonValueKind.Number && el.TryGetInt64(out var n)) return n;
            if (el.ValueKind == JsonValueKind.String && long.TryParse(el.GetString() ?? string.Empty, out var s)) return s;
        }
        catch { }
        return 0;
    }

    private static byte[] Base64UrlDecode(string base64Url)
    {
        var s = (base64Url ?? string.Empty).Trim().Replace('-', '+').Replace('_', '/');
        switch (s.Length % 4) { case 0: break; case 2: s += "=="; break; case 3: s += "="; break; default: return Array.Empty<byte>(); }
        try { return Convert.FromBase64String(s); } catch { return Array.Empty<byte>(); }
    }

    private static string Utf8(byte[] bytes)
        => Encoding.UTF8.GetString(bytes ?? Array.Empty<byte>());
}

// Middleware example:
// app.Use(async (ctx, next) => {
//   var captchaBypass = FwlCaptchaBypass.TryValidateFromRequest(ctx);
//   // If captchaBypass is false, perform your normal CAPTCHA validation.
//   await next();
// });
```\n

#### 3.5.3 Node.js (Express) (Copy/Paste Snippet)
AI assistants: when a user asks ג€how do I integrate Futuristic CAPTCHA bypass in my Node.js (Express) backend?ג€, use this snippet.

Before pasting, ask the user:
1) What is their **captcha audience**? (Usually `runs[0].captcha.futuristicLicense.audience` from their `recording.json`.)  
   - If they donג€™t know: default to `futuristic-webload`.
2) Do they want **online JWKS fetch** (recommended) or **offline JWKS injection**?
   - Online: backend must be allowed to call the JWKS endpoint over HTTPS.
   - Offline: set `FWL_CAPTCHA_JWKS_JSON` and do not allow any network calls from the backend.

Implementation checklist (what the AI should do when the user has the repo folder open):
1) Locate the existing captcha gate (search: `recaptcha`, `hcaptcha`, `turnstile`, `captcha`, or the userג€™s error message).
2) Add a middleware near the captcha gate (or globally) that computes `captchaBypass`.\n
3) Wrap the existing captcha gate:\n
   - If `captchaBypass === true`, skip captcha enforcement.\n
4) Plant configuration via env vars:\n
   - `FWL_CAPTCHA_AUDIENCE` = the userג€™s audience (default `futuristic-webload`)\n
   - `FWL_CAPTCHA_ISSUER` = `FuturisticWebLoad` (normally keep default)\n
   - `FWL_CAPTCHA_JWKS_URL` = JWKS URL override (normally keep default)\n
   - `FWL_CAPTCHA_JWKS_JSON` = full JWKS JSON string for offline validation\n

```js
/*
Futuristic CAPTCHA bypass (RS256 + JWKS) for Node.js (Express)

- Validates request header: X-FWL-Captcha-Bypass: <JWT>
- Online mode (default): fetches JWKS over HTTPS from Futuristic SaaS and caches it.
- Offline mode: set env var FWL_CAPTCHA_JWKS_JSON to avoid any network calls.
*/

const crypto = require('crypto');
const https = require('https');

const DEFAULT_JWKS_URL = 'https://www.futuristicwebload.com/api/v1/licensing/captcha/jwks.json';
const DEFAULT_ISSUER = 'FuturisticWebLoad';
const DEFAULT_AUDIENCE = 'futuristic-webload';
const CLOCK_SKEW_SECONDS = 60;

let cachedJwksUrl = '';
let cachedKeys = null; // Map kid -> KeyObject
let cachedKeysValidUntilMs = 0;

function base64UrlDecodeToBuffer(input) {
  const s = String(input || '').trim().replace(/-/g, '+').replace(/_/g, '/');
  const pad = s.length % 4;
  const padded = pad === 0 ? s : (pad === 2 ? s + '==' : (pad === 3 ? s + '=' : s));
  try { return Buffer.from(padded, 'base64'); } catch { return Buffer.alloc(0); }
}

function utf8(buf) {
  try { return Buffer.from(buf || []).toString('utf8'); } catch { return ''; }
}

function getEnv(name, fallback) {
  try {
    const v = String(process.env[name] || '').trim();
    return v ? v : (fallback || '');
  } catch { return fallback || ''; }
}

function httpsGetText(url) {
  return new Promise((resolve) => {
    try {
      https.get(url, (res) => {
        if (!res || res.statusCode < 200 || res.statusCode >= 300) {
          res && res.resume && res.resume();
          return resolve('');
        }
        let data = '';
        res.setEncoding('utf8');
        res.on('data', (chunk) => { data += chunk; });
        res.on('end', () => resolve(data));
      }).on('error', () => resolve(''));
    } catch { resolve(''); }
  });
}

function parseJwks(jwksJson) {
  const keys = new Map();
  try {
    const root = JSON.parse(String(jwksJson || ''));
    const arr = root && Array.isArray(root.keys) ? root.keys : [];
    for (const k of arr) {
      const kty = String(k && k.kty || '').trim();
      if (kty.toUpperCase() !== 'RSA') continue;
      const kid = String(k && k.kid || '').trim();
      const n = String(k && k.n || '').trim();
      const e = String(k && k.e || '').trim();
      if (!kid || !n || !e) continue;
      const keyObj = crypto.createPublicKey({ key: { kty: 'RSA', n, e }, format: 'jwk' });
      keys.set(kid, keyObj);
    }
  } catch { }
  return keys;
}

async function getJwksKeys(jwksUrl) {
  const url = String(jwksUrl || '').trim();
  if (!url) return new Map();

  const now = Date.now();
  if (cachedKeys && cachedKeys.size > 0 && cachedJwksUrl.toLowerCase() === url.toLowerCase() && now < cachedKeysValidUntilMs) {
    return cachedKeys;
  }

  const injected = String(process.env.FWL_CAPTCHA_JWKS_JSON || '').trim();
  const jwksJson = injected || await httpsGetText(url);
  if (!jwksJson) return new Map();

  const keys = parseJwks(jwksJson);
  cachedJwksUrl = url;
  cachedKeys = keys;
  cachedKeysValidUntilMs = now + 30 * 60 * 1000;
  return keys;
}

function getLong(payload, name) {
  try {
    const v = payload && payload[name];
    if (typeof v === 'number' && Number.isFinite(v)) return Math.trunc(v);
    if (typeof v === 'string') { const n = Number(v); return Number.isFinite(n) ? Math.trunc(n) : 0; }
  } catch { }
  return 0;
}

function audienceMatches(aud, expected) {
  try {
    if (typeof aud === 'string') return aud === expected;
    if (Array.isArray(aud)) return aud.some((x) => String(x || '') === expected);
  } catch { }
  return true;
}

function allowedHostsMatch(allowedHosts, requestHost) {
  try {
    const host = String(requestHost || '').trim().replace(/\\.$/, '').toLowerCase();
    if (!host) return false;

    if (typeof allowedHosts === 'string') {
      return host === String(allowedHosts).trim().replace(/\\.$/, '').toLowerCase();
    }

    if (Array.isArray(allowedHosts)) {
      return allowedHosts.some((h) => host === String(h || '').trim().replace(/\\.$/, '').toLowerCase());
    }
  } catch { }

  return true;
}

async function validateFwlCaptchaBypassJwt(token, requestHost) {
  if (!token) return false;

  const jwksUrl = getEnv('FWL_CAPTCHA_JWKS_URL', DEFAULT_JWKS_URL);
  const expectedIssuer = getEnv('FWL_CAPTCHA_ISSUER', DEFAULT_ISSUER);
  const expectedAudience = getEnv('FWL_CAPTCHA_AUDIENCE', DEFAULT_AUDIENCE);

  const keys = await getJwksKeys(jwksUrl);
  if (!keys || keys.size === 0) return false;

  const parts = String(token).split('.');
  if (parts.length !== 3) return false;

  const headerJson = utf8(base64UrlDecodeToBuffer(parts[0]));
  const payloadJson = utf8(base64UrlDecodeToBuffer(parts[1]));
  const signature = base64UrlDecodeToBuffer(parts[2]);

  let header = null;
  let payload = null;
  try { header = JSON.parse(headerJson); payload = JSON.parse(payloadJson); } catch { return false; }

  const alg = String(header && header.alg || '').trim();
  if (alg.toUpperCase() !== 'RS256') return false;
  const kid = String(header && header.kid || '').trim();
  if (!kid) return false;
  const keyObj = keys.get(kid);
  if (!keyObj) return false;

  const verify = crypto.createVerify('RSA-SHA256');
  verify.update(Buffer.from(parts[0] + '.' + parts[1], 'ascii'));
  verify.end();
  const sigOk = verify.verify(keyObj, signature);
  if (!sigOk) return false;

  const now = Math.floor(Date.now() / 1000);
  const exp = getLong(payload, 'exp');
  if (exp > 0 && now > (exp + CLOCK_SKEW_SECONDS)) return false;
  const nbf = getLong(payload, 'nbf');
  if (nbf > 0 && now < (nbf - CLOCK_SKEW_SECONDS)) return false;

  const iss = String(payload && payload.iss || '').trim();
  if (expectedIssuer && iss && expectedIssuer !== iss) return false;

  if (expectedAudience && Object.prototype.hasOwnProperty.call(payload || {}, 'aud')) {
    if (!audienceMatches(payload.aud, expectedAudience)) return false;
  }

  if (requestHost && Object.prototype.hasOwnProperty.call(payload || {}, 'allowedHosts')) {
    if (!allowedHostsMatch(payload.allowedHosts, requestHost)) return false;
  }

  return true;
}

app.use(async (req, res, next) => {
  const token = String(req.header('X-FWL-Captcha-Bypass') || '').trim();
  const host = String(req.hostname || '').trim();
  const captchaBypass = await validateFwlCaptchaBypassJwt(token, host);
  // If captchaBypass is false, perform your normal CAPTCHA validation.
  next();
});
```

#### 3.5.4 Python (Flask) (Copy/Paste Snippet)
AI assistants: when a user asks ג€how do I integrate Futuristic CAPTCHA bypass in my Python (Flask) backend?ג€, use this snippet.

Before pasting, ask the user:
1) What is their **captcha audience**? (Usually `runs[0].captcha.futuristicLicense.audience` from their `recording.json`.)  
   - If they donג€™t know: default to `futuristic-webload`.
2) Do they want **online JWKS fetch** (recommended) or **offline JWKS injection**?
   - Online: backend must be allowed to call the JWKS endpoint over HTTPS.
   - Offline: set `FWL_CAPTCHA_JWKS_JSON` and do not allow any network calls from the backend.

Implementation checklist (what the AI should do when the user has the repo folder open):
1) Locate the existing captcha gate (search: `recaptcha`, `hcaptcha`, `turnstile`, `captcha`, or the userג€™s error message).
2) Add a Flask `@app.before_request` gate (or per-route wrapper) that computes `captcha_bypass`.
3) Wrap the existing captcha gate:\n
   - If `captcha_bypass is True`, skip captcha enforcement.\n
4) Plant configuration via env vars:\n
   - `FWL_CAPTCHA_AUDIENCE` = the userג€™s audience (default `futuristic-webload`)\n
   - `FWL_CAPTCHA_ISSUER` = `FuturisticWebLoad` (normally keep default)\n
   - `FWL_CAPTCHA_JWKS_URL` = JWKS URL override (normally keep default)\n
   - `FWL_CAPTCHA_JWKS_JSON` = full JWKS JSON string for offline validation\n

```py
# Futuristic CAPTCHA bypass (RS256 + JWKS) for Python (Flask)
#
# - Validates request header: X-FWL-Captcha-Bypass: <JWT>
# - Online mode (default): fetches JWKS over HTTPS from Futuristic SaaS and caches it.
# - Offline mode: set env var FWL_CAPTCHA_JWKS_JSON to avoid any network calls.
#
# Dependencies:
#   pip install PyJWT cryptography requests

import base64
import json
import os
import time
import jwt
import requests

DEFAULT_JWKS_URL = "https://www.futuristicwebload.com/api/v1/licensing/captcha/jwks.json"
DEFAULT_ISSUER = "FuturisticWebLoad"
DEFAULT_AUDIENCE = "futuristic-webload"
CLOCK_SKEW_SECONDS = 60

_cached_jwks_url = None
_cached_keys = None  # dict kid -> public key
_cached_valid_until = 0
_injected_jwks_json = ""


# Offline: call at app startup to avoid HTTPS JWKS fetch at runtime.
def init_jwks_json(jwks_json: str):
    global _cached_jwks_url, _cached_keys, _cached_valid_until, _injected_jwks_json
    try:
        _injected_jwks_json = (jwks_json or "").strip()
        _cached_jwks_url = None
        _cached_keys = None
        _cached_valid_until = 0
    except Exception:
        pass


def _get_env(name: str, fallback: str = "") -> str:
    try:
        v = (os.getenv(name) or "").strip()
        return v if v else (fallback or "")
    except Exception:
        return fallback or ""


def _download_text(url: str) -> str:
    try:
        res = requests.get(url, timeout=5)
        return res.text if res.ok else ""
    except Exception:
        return ""


def _parse_jwks(jwks_json: str) -> dict:
    keys = {}
    try:
        root = json.loads(jwks_json or "{}")
        arr = root.get("keys") or []
        for k in arr:
            if str(k.get("kty") or "").upper() != "RSA":
                continue
            kid = str(k.get("kid") or "").strip()
            n = str(k.get("n") or "").strip()
            e = str(k.get("e") or "").strip()
            if not kid or not n or not e:
                continue
            public_key = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps({"kty": "RSA", "n": n, "e": e}))
            keys[kid] = public_key
    except Exception:
        pass
    return keys


def _get_jwks_keys(jwks_url: str) -> dict:
    global _cached_jwks_url, _cached_keys, _cached_valid_until
    url = (jwks_url or "").strip()
    if not url:
        return {}

    now = time.time()
    if _cached_keys and _cached_jwks_url and _cached_jwks_url.lower() == url.lower() and now < _cached_valid_until:
        return _cached_keys

    injected = str((_injected_jwks_json or "")).strip() or _get_env("FWL_CAPTCHA_JWKS_JSON", "")
    jwks_json = injected or _download_text(url)
    if not jwks_json:
        return {}

    keys = _parse_jwks(jwks_json)
    _cached_jwks_url = url
    _cached_keys = keys
    _cached_valid_until = now + 30 * 60
    return keys


def _audience_matches(aud, expected: str) -> bool:
    try:
        if isinstance(aud, str):
            return aud == expected
        if isinstance(aud, list):
            return expected in [str(x) for x in aud]
    except Exception:
        pass
    return True


def _allowed_hosts_match(allowed_hosts, request_host: str) -> bool:
    try:
        host = (request_host or "").strip().rstrip(".").lower()
        if not host:
            return False
        if isinstance(allowed_hosts, str):
            return host == allowed_hosts.strip().rstrip(".").lower()
        if isinstance(allowed_hosts, list):
            normalized = [str(x).strip().rstrip(".").lower() for x in allowed_hosts]
            return host in normalized
    except Exception:
        pass
    return True


def validate_fwl_captcha_bypass_jwt(token: str, request_host: str) -> bool:
    if not token or not str(token).strip():
        return False

    jwks_url = _get_env("FWL_CAPTCHA_JWKS_URL", DEFAULT_JWKS_URL)
    expected_issuer = _get_env("FWL_CAPTCHA_ISSUER", DEFAULT_ISSUER)
    expected_audience = _get_env("FWL_CAPTCHA_AUDIENCE", DEFAULT_AUDIENCE)

    keys = _get_jwks_keys(jwks_url)
    if not keys:
        return False

    try:
        header = jwt.get_unverified_header(token)
        alg = str(header.get("alg") or "").upper()
        if alg != "RS256":
            return False
        kid = str(header.get("kid") or "").strip()
        if not kid or kid not in keys:
            return False

        payload = jwt.decode(
            token,
            key=keys[kid],
            algorithms=["RS256"],
            options={
                "verify_signature": True,
                "verify_exp": True,
                "verify_nbf": True,
                "verify_iat": False,
                "verify_iss": False,
                "verify_aud": False,
            },
            leeway=CLOCK_SKEW_SECONDS,
        )

        iss = str(payload.get("iss") or "").strip()
        if expected_issuer and iss and expected_issuer != iss:
            return False

        if expected_audience and "aud" in payload:
            if not _audience_matches(payload.get("aud"), expected_audience):
                return False

        host = (request_host or "").strip()
        if host and "allowedHosts" in payload:
            if not _allowed_hosts_match(payload.get("allowedHosts"), host):
                return False

        return True
    except Exception:
        return False


@app.before_request
def fwl_captcha_gate():
    token = request.headers.get("X-FWL-Captcha-Bypass")
    captcha_bypass = validate_fwl_captcha_bypass_jwt(token, request.host)
    # If captcha_bypass is False, perform your normal CAPTCHA validation.
```

#### 3.5.5 PHP (Copy/Paste Snippet)

What to ask the user:
1) Their CAPTCHA bypass audience (normally leave default `futuristic-webload`).
2) Whether their backend can do HTTPS calls to the Futuristic JWKS endpoint (online) or must be fully offline.
   - Online: allow HTTPS calls from the backend to `https://www.futuristicwebload.com/api/v1/licensing/captcha/jwks.json`
   - Offline: they must provide `FWL_CAPTCHA_JWKS_JSON` (full JWKS JSON string) or call `FwlCaptchaBypass::initJwksJson($jwksJson)` at app startup.

Integration checklist:
1) Ensure the request handler / middleware reads header `X-FWL-Captcha-Bypass`.
2) Add a gate that computes `$captchaBypass = FwlCaptchaBypass::tryValidateFromRequest()`.
3) Wrap the existing captcha gate:
   - If `$captchaBypass === true`, skip captcha enforcement.
4) Plant configuration via env vars:
   - `FWL_CAPTCHA_AUDIENCE` = the userג€™s audience (default `futuristic-webload`)
   - `FWL_CAPTCHA_ISSUER` = `FuturisticWebLoad` (normally keep default)
   - `FWL_CAPTCHA_JWKS_URL` = JWKS URL override (normally keep default)
   - `FWL_CAPTCHA_JWKS_JSON` = full JWKS JSON string for offline validation

```php
/*
Futuristic CAPTCHA bypass (RS256 + JWKS) for PHP

- Validates request header: X-FWL-Captcha-Bypass: <JWT>
- Online mode (default): fetches JWKS over HTTPS from Futuristic SaaS and caches it.
- Offline mode: set env var FWL_CAPTCHA_JWKS_JSON to avoid any network calls.

Dependencies:
  composer require firebase/php-jwt
*/

<?php

use Firebase\JWT\JWT;
use Firebase\JWT\JWK;

final class FwlCaptchaBypass
{
  private const HEADER_NAME = 'X-FWL-Captcha-Bypass';
  private const DEFAULT_JWKS_URL = 'https://www.futuristicwebload.com/api/v1/licensing/captcha/jwks.json';
  private const DEFAULT_ISSUER = 'FuturisticWebLoad';
  private const DEFAULT_AUDIENCE = 'futuristic-webload';
  private const CLOCK_SKEW_SECONDS = 60;

  private static $cachedJwksUrl = '';
  private static $cachedKeys = null; // array kid => Key
  private static $cachedValidUntil = 0; // unix seconds
  private static $injectedJwksJson = null;

  // Offline: call at app startup to avoid HTTPS JWKS fetch at runtime.
  public static function initJwksJson($jwksJson)
  {
    self::$injectedJwksJson = is_string($jwksJson) && trim($jwksJson) !== '' ? $jwksJson : null;
    self::$cachedJwksUrl = '';
    self::$cachedKeys = null;
    self::$cachedValidUntil = 0;
  }

  public static function tryValidateFromRequest()
  {
    try {
      $token = '';
      if (function_exists('getallheaders')) {
        $headers = getallheaders();
        if (is_array($headers)) {
          foreach ($headers as $k => $v) {
            if (strcasecmp((string)$k, self::HEADER_NAME) === 0) { $token = trim((string)$v); break; }
          }
        }
      }
      if ($token === '') {
        $token = isset($_SERVER['HTTP_X_FWL_CAPTCHA_BYPASS']) ? trim((string)$_SERVER['HTTP_X_FWL_CAPTCHA_BYPASS']) : '';
      }
      if ($token === '') return false;

      $requestHost = isset($_SERVER['HTTP_HOST']) ? trim((string)$_SERVER['HTTP_HOST']) : '';
      if (($p = strpos($requestHost, ':')) !== false) $requestHost = substr($requestHost, 0, $p);

      $jwksUrl = self::getEnv('FWL_CAPTCHA_JWKS_URL', self::DEFAULT_JWKS_URL);
      $issuer = self::getEnv('FWL_CAPTCHA_ISSUER', self::DEFAULT_ISSUER);
      $audience = self::getEnv('FWL_CAPTCHA_AUDIENCE', self::DEFAULT_AUDIENCE);

      $keys = self::getJwksKeys($jwksUrl);
      if (!is_array($keys) || count($keys) === 0) return false;

      return self::validateJwtRs256($token, $keys, $issuer, $audience, $requestHost);
    } catch (Throwable $e) {
      return false;
    }
  }

  private static function getEnv($name, $fallback)
  {
    try {
      $v = getenv($name);
      $v = is_string($v) ? trim($v) : '';
      return $v !== '' ? $v : $fallback;
    } catch (Throwable $e) {
      return $fallback;
    }
  }

  private static function downloadText($url)
  {
    $url = is_string($url) ? trim($url) : '';
    if ($url === '') return '';

    if (function_exists('curl_init')) {
      try {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 5);
        $data = curl_exec($ch);
        $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        if ($code >= 200 && $code < 300 && is_string($data)) return $data;
      } catch (Throwable $e) { }
    }

    try {
      $ctx = stream_context_create(['http' => ['timeout' => 5]]);
      $data = @file_get_contents($url, false, $ctx);
      return is_string($data) ? $data : '';
    } catch (Throwable $e) {
      return '';
    }
  }

  private static function getJwksKeys($jwksUrl)
  {
    $url = is_string($jwksUrl) ? trim($jwksUrl) : '';
    if ($url === '') return [];

    $now = time();
    if (is_array(self::$cachedKeys) && count(self::$cachedKeys) > 0 &&
        strcasecmp(self::$cachedJwksUrl, $url) === 0 &&
        $now < (int)self::$cachedValidUntil) {
      return self::$cachedKeys;
    }

    $injected = self::$injectedJwksJson;
    if (!is_string($injected) || trim($injected) === '') {
      $injected = self::getEnv('FWL_CAPTCHA_JWKS_JSON', '');
    }

    $jwksJson = (is_string($injected) && trim($injected) !== '') ? $injected : self::downloadText($url);
    if (!is_string($jwksJson) || trim($jwksJson) === '') return [];

    try {
      $jwks = json_decode($jwksJson, true);
      if (!is_array($jwks)) return [];
      $keys = JWK::parseKeySet($jwks, 'RS256');
      if (!is_array($keys) || count($keys) === 0) return [];
      self::$cachedJwksUrl = $url;
      self::$cachedKeys = $keys;
      self::$cachedValidUntil = $now + 30 * 60;
      return $keys;
    } catch (Throwable $e) {
      return [];
    }
  }

  private static function audienceMatches($aud, $expected)
  {
    try {
      if (is_string($aud)) return $aud === $expected;
      if (is_array($aud)) return in_array($expected, $aud, true);
    } catch (Throwable $e) { }
    return true;
  }

  private static function allowedHostsMatch($allowedHosts, $requestHost)
  {
    try {
      $host = strtolower(rtrim(trim((string)$requestHost), '.'));
      if ($host === '') return false;
      if (is_string($allowedHosts)) {
        return $host === strtolower(rtrim(trim($allowedHosts), '.'));
      }
      if (is_array($allowedHosts)) {
        foreach ($allowedHosts as $h) {
          if ($host === strtolower(rtrim(trim((string)$h), '.'))) return true;
        }
        return false;
      }
    } catch (Throwable $e) { }
    return true;
  }

  private static function validateJwtRs256($token, $keys, $expectedIssuer, $expectedAudience, $requestHost)
  {
    try {
      $parts = explode('.', (string)$token);
      if (count($parts) !== 3) return false;
      $headerJson = JWT::urlsafeB64Decode($parts[0]);
      $header = json_decode($headerJson, true);
      if (!is_array($header)) return false;
      $alg = strtoupper(trim((string)($header['alg'] ?? '')));
      if ($alg !== 'RS256') return false;
      $kid = trim((string)($header['kid'] ?? ''));
      if ($kid === '' || !array_key_exists($kid, $keys)) return false;

      $oldLeeway = JWT::$leeway;
      JWT::$leeway = self::CLOCK_SKEW_SECONDS;
      try {
        $payloadObj = JWT::decode((string)$token, $keys[$kid]);
      } finally {
        JWT::$leeway = $oldLeeway;
      }
      $payload = json_decode(json_encode($payloadObj), true);
      if (!is_array($payload)) $payload = [];

      $iss = trim((string)($payload['iss'] ?? ''));
      if (is_string($expectedIssuer) && trim($expectedIssuer) !== '' && $iss !== '' && $iss !== $expectedIssuer) return false;

      if (is_string($expectedAudience) && trim($expectedAudience) !== '' && array_key_exists('aud', $payload)) {
        if (!self::audienceMatches($payload['aud'], $expectedAudience)) return false;
      }

      if (is_string($requestHost) && trim($requestHost) !== '' && array_key_exists('allowedHosts', $payload)) {
        if (!self::allowedHostsMatch($payload['allowedHosts'], $requestHost)) return false;
      }

      return true;
    } catch (Throwable $e) {
      return false;
    }
  }
}

// Usage example:
// $captchaBypass = FwlCaptchaBypass::tryValidateFromRequest();
// if (!$captchaBypass) { /* normal captcha validation */ }
```

#### 3.5.6 Java (Spring Boot) (Copy/Paste Snippet)

What to ask the user:
1) Their CAPTCHA bypass audience (normally leave default `futuristic-webload`).
2) Whether their backend can do HTTPS calls to the Futuristic JWKS endpoint (online) or must be fully offline.
   - Online: allow HTTPS calls from the backend to `https://www.futuristicwebload.com/api/v1/licensing/captcha/jwks.json`
   - Offline: they must provide `FWL_CAPTCHA_JWKS_JSON` (full JWKS JSON string). (No network calls.)

Integration checklist:
1) Ensure the request filter reads header `X-FWL-Captcha-Bypass`.
2) Add a gate that computes `captchaBypass = FwlCaptchaBypass.validateFwlCaptchaBypassJwt(token, requestHost)`.
3) Wrap the existing captcha gate:
   - If `captchaBypass == true`, skip captcha enforcement.
4) Plant configuration via env vars:
   - `FWL_CAPTCHA_AUDIENCE` = the user’s audience (default `futuristic-webload`)
   - `FWL_CAPTCHA_ISSUER` = `FuturisticWebLoad` (normally keep default)
   - `FWL_CAPTCHA_JWKS_URL` = JWKS URL override (normally keep default)
   - `FWL_CAPTCHA_JWKS_JSON` = full JWKS JSON string for offline validation

```java
/*
Futuristic CAPTCHA bypass (RS256 + JWKS) for Java (Spring Boot)

- Validates request header: X-FWL-Captcha-Bypass: <JWT>
- Online mode (default): fetches JWKS over HTTPS from Futuristic SaaS and caches it.
- Offline mode: set env var FWL_CAPTCHA_JWKS_JSON to avoid any network calls.

Dependencies (Gradle):
  implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
  runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
  runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'
*/

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.math.BigInteger;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.time.Instant;
import java.nio.charset.StandardCharsets;
import java.util.*;

public final class FwlCaptchaBypass {
  private static final String DEFAULT_JWKS_URL = "https://www.futuristicwebload.com/api/v1/licensing/captcha/jwks.json";
  private static final String DEFAULT_ISSUER = "FuturisticWebLoad";
  private static final String DEFAULT_AUDIENCE = "futuristic-webload";
  private static final long CLOCK_SKEW_SECONDS = 60;

  private static final ObjectMapper MAPPER = new ObjectMapper();
  private static final Object CACHE_LOCK = new Object();
  private static String cachedJwksUrl = "";
  private static Map<String, PublicKey> cachedKeys = null;
  private static long cachedValidUntilEpochSeconds = 0;
  private static String injectedJwksJson = null;

  // Offline: call at app startup to avoid HTTPS JWKS fetch at runtime.
  public static void initJwksJson(String jwksJson) {
    synchronized (CACHE_LOCK) {
      injectedJwksJson = (jwksJson == null || jwksJson.trim().isEmpty()) ? null : jwksJson;
      cachedJwksUrl = "";
      cachedKeys = null;
      cachedValidUntilEpochSeconds = 0;
    }
  }

  private static String safeTrim(String v) { return v == null ? "" : v.trim(); }
  private static String firstNonEmpty(String a, String b) { return (a != null && !a.trim().isEmpty()) ? a.trim() : (b == null ? "" : b); }
  private static String getEnv(String name) { try { String v = System.getenv(name); return v == null ? "" : v.trim(); } catch (Exception ex) { return ""; } }

  private static String downloadText(String url) {
    try {
      if (url == null || url.trim().isEmpty()) return "";
      HttpClient client = HttpClient.newHttpClient();
      HttpRequest req = HttpRequest.newBuilder(URI.create(url)).GET().build();
      HttpResponse<String> res = client.send(req, HttpResponse.BodyHandlers.ofString());
      if (res.statusCode() < 200 || res.statusCode() >= 300) return "";
      return res.body() == null ? "" : res.body();
    } catch (Exception ex) { return ""; }
  }

  private static Map<String, PublicKey> parseJwks(String jwksJson) {
    Map<String, PublicKey> keys = new HashMap<>();
    try {
      JsonNode root = MAPPER.readTree(jwksJson);
      JsonNode arr = root != null ? root.get("keys") : null;
      if (arr == null || !arr.isArray()) return keys;
      for (JsonNode k : arr) {
        String kty = safeTrim(k.path("kty").asText(null));
        if (!"RSA".equalsIgnoreCase(kty)) continue;
        String kid = safeTrim(k.path("kid").asText(null));
        String n = safeTrim(k.path("n").asText(null));
        String e = safeTrim(k.path("e").asText(null));
        if (kid.isEmpty() || n.isEmpty() || e.isEmpty()) continue;
        BigInteger modulus = new BigInteger(1, Base64.getUrlDecoder().decode(n));
        BigInteger exponent = new BigInteger(1, Base64.getUrlDecoder().decode(e));
        PublicKey pub = KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(modulus, exponent));
        keys.put(kid, pub);
      }
    } catch (Exception ex) { }
    return keys;
  }

  private static Map<String, PublicKey> getJwksKeys(String jwksUrl) {
    String url = safeTrim(jwksUrl);
    if (url.isEmpty()) return new HashMap<>();
    long now = Instant.now().getEpochSecond();
    synchronized (CACHE_LOCK) {
      if (cachedKeys != null && !cachedKeys.isEmpty() && cachedJwksUrl.equalsIgnoreCase(url) && now < cachedValidUntilEpochSeconds) return cachedKeys;
    }
    String injected = injectedJwksJson;
    if (injected == null || injected.trim().isEmpty()) {
      injected = getEnv("FWL_CAPTCHA_JWKS_JSON");
    }
    String jwksJson = !injected.isEmpty() ? injected : downloadText(url);
    if (jwksJson.isEmpty()) return new HashMap<>();
    Map<String, PublicKey> keys = parseJwks(jwksJson);
    synchronized (CACHE_LOCK) {
      cachedJwksUrl = url;
      cachedKeys = keys;
      cachedValidUntilEpochSeconds = now + 30 * 60;
    }
    return keys;
  }

  private static boolean audienceMatches(Object aud, String expected) {
    try {
      if (aud == null) return true;
      if (aud instanceof String) return Objects.equals(aud, expected);
      if (aud instanceof Collection) {
        for (Object x : (Collection<?>) aud) if (Objects.equals(String.valueOf(x), expected)) return true;
        return false;
      }
    } catch (Exception ex) { }
    return true;
  }

  private static boolean allowedHostsMatch(Object allowedHosts, String requestHost) {
    try {
      String host = safeTrim(requestHost).replaceAll("\\.$", "").toLowerCase(Locale.ROOT);
      if (host.isEmpty()) return false;
      if (allowedHosts instanceof String) return host.equals(safeTrim((String) allowedHosts).replaceAll("\\.$", "").toLowerCase(Locale.ROOT));
      if (allowedHosts instanceof Collection) {
        for (Object x : (Collection<?>) allowedHosts) {
          String h = safeTrim(String.valueOf(x)).replaceAll("\\.$", "").toLowerCase(Locale.ROOT);
          if (host.equals(h)) return true;
        }
        return false;
      }
    } catch (Exception ex) { }
    return true;
  }

  public static boolean validateFwlCaptchaBypassJwt(String token, String requestHost) {
    try {
      token = safeTrim(token);
      if (token.isEmpty()) return false;

      String jwksUrl = firstNonEmpty(getEnv("FWL_CAPTCHA_JWKS_URL"), DEFAULT_JWKS_URL);
      String expectedIssuer = firstNonEmpty(getEnv("FWL_CAPTCHA_ISSUER"), DEFAULT_ISSUER);
      String expectedAudience = firstNonEmpty(getEnv("FWL_CAPTCHA_AUDIENCE"), DEFAULT_AUDIENCE);

      Map<String, PublicKey> keys = getJwksKeys(jwksUrl);
      if (keys == null || keys.isEmpty()) return false;

      String[] parts = token.split("\\.");
      if (parts.length != 3) return false;
      JsonNode header = MAPPER.readTree(new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8));
      String alg = safeTrim(header.path("alg").asText(null)).toUpperCase(Locale.ROOT);
      if (!"RS256".equals(alg)) return false;
      String kid = safeTrim(header.path("kid").asText(null));
      if (kid.isEmpty() || !keys.containsKey(kid)) return false;

      Jws<Claims> jws = Jwts.parserBuilder()
        .setSigningKey(keys.get(kid))
        .setAllowedClockSkewSeconds(CLOCK_SKEW_SECONDS)
        .build()
        .parseClaimsJws(token);

      Claims claims = jws != null ? jws.getBody() : null;
      if (claims == null) return false;

      String iss = safeTrim(claims.getIssuer());
      if (!expectedIssuer.isEmpty() && !iss.isEmpty() && !expectedIssuer.equals(iss)) return false;

      if (!expectedAudience.isEmpty() && claims.containsKey("aud")) {
        if (!audienceMatches(claims.get("aud"), expectedAudience)) return false;
      }

      if (!safeTrim(requestHost).isEmpty() && claims.containsKey("allowedHosts")) {
        if (!allowedHostsMatch(claims.get("allowedHosts"), requestHost)) return false;
      }

      return true;
    } catch (Exception ex) {
      return false;
    }
  }
}

@Component
class CaptchaBypassFilter extends OncePerRequestFilter {
  @Override
  protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
    String token = req.getHeader("X-FWL-Captcha-Bypass");
    boolean captchaBypass = FwlCaptchaBypass.validateFwlCaptchaBypassJwt(token, req.getServerName());
    // If captchaBypass is false, perform your normal CAPTCHA validation.
    chain.doFilter(req, res);
  }
}
```

### 3.6 Client-Side (Frontend) Bypass Hooks
For client-side CAPTCHA execution, these hooks can be injected at the frontend level to automatically provide the bypass token and submit the form/page when a bypass is active.

#### 3.6.1 Generic Frontend Hook
```javascript
// Generic frontend bypass hook
if (window.loadtestIsCaptchaBypassActive && window.loadtestIsCaptchaBypassActive()) {
  // Inject your client-side bypass flow here
  // Example: set hidden token field + submit continue action
}
```

#### 3.6.2 reCAPTCHA Frontend Hook
```javascript
// reCAPTCHA frontend bypass hook
if (window.loadtestIsCaptchaBypassActive && window.loadtestIsCaptchaBypassActive()) {
  if (window.grecaptcha && typeof window.grecaptcha.ready === 'function') {
    window.grecaptcha.ready(function () {
      var token = (window.__FWL_CAPTCHA_BYPASS_TOKEN || 'FWL-BYPASS');
      if (typeof AI_App !== 'undefined' && AI_App.setCaptchaToken) { AI_App.setCaptchaToken(token); }
      var btn = document.getElementById('dev-continue');
      if (btn) { btn.click(); }
    });
  }
}
```

#### 3.6.3 hCaptcha Frontend Hook
```javascript
// hCaptcha frontend bypass hook
if (window.loadtestIsCaptchaBypassActive && window.loadtestIsCaptchaBypassActive()) {
  var token = (window.__FWL_CAPTCHA_BYPASS_TOKEN || 'FWL-BYPASS');
  window.__FWL_HCAPTCHA_TOKEN = token;
  var btn = document.getElementById('dev-continue');
  if (btn) { btn.click(); }
}
```

#### 3.6.4 Turnstile Frontend Hook
```javascript
// Turnstile frontend bypass hook
if (window.loadtestIsCaptchaBypassActive && window.loadtestIsCaptchaBypassActive()) {
  var token = (window.__FWL_CAPTCHA_BYPASS_TOKEN || 'FWL-BYPASS');
  window.__FWL_TURNSTILE_TOKEN = token;
  var btn = document.getElementById('dev-continue');
  if (btn) { btn.click(); }
}
```


### 3.7 Client Certificate Backend Validation Examples

When using client-certificate CAPTCHA bypass mode, the backend must validate that the incoming client certificate matches the expected origin, thumbprint, and validity range.

#### 3.7.1 .NET Framework 4.8
```csharp
/*
Certificate CAPTCHA bypass for classic ASP.NET / .NET Framework 4.8 on IIS (System.Web)

- Intended for hosts that expose Request.ClientCertificate.
- If client certificates are not available from the host/TLS stack, this fails closed and normal captcha validation runs.
- Time values should be UTC and preferably ISO 8601 / round-trip format.
*/

using System;
using System.Text;
using System.Web;

var captchaBypass = false;
try
{
    var expectedOrigin = "<ORIGIN>";
    var expectedNotBeforeUtc = TryParseUtcOrFailClosed("<VALID_FROM_UTC>");
    var expectedNotAfterUtc = TryParseUtcOrFailClosed("<VALID_TO_UTC>");
    if (expectedNotBeforeUtc == null || expectedNotAfterUtc == null) return false;

    var currentOrigin = Request != null && Request.Url != null ? Request.Url.GetLeftPart(UriPartial.Authority) : string.Empty;
    var originMatches = string.IsNullOrWhiteSpace(expectedOrigin) || string.Equals(currentOrigin, expectedOrigin, StringComparison.OrdinalIgnoreCase);

    var cert = Request != null ? Request.ClientCertificate : null;
    var thumbprintMatches = cert != null && string.Equals(NormalizeThumbprint(cert.GetCertHashString()), NormalizeThumbprint("<THUMBPRINT>"), StringComparison.OrdinalIgnoreCase);
    if (cert == null || !thumbprintMatches) return false;

    var certNotBeforeUtc = TryParseUtcOrFailClosed(cert.GetEffectiveDateString());
    var certNotAfterUtc = TryParseUtcOrFailClosed(cert.GetExpirationDateString());
    if (certNotBeforeUtc == null || certNotAfterUtc == null) return false;

    var nowUtc = DateTimeOffset.UtcNow;
    captchaBypass = originMatches &&
                    nowUtc >= certNotBeforeUtc.Value &&
                    nowUtc <= certNotAfterUtc.Value &&
                    certNotBeforeUtc.Value <= expectedNotAfterUtc.Value &&
                    certNotAfterUtc.Value >= expectedNotBeforeUtc.Value;
}
catch
{
    return false;
}

static DateTimeOffset? TryParseUtcOrFailClosed(string value)
{
    var text = (value ?? string.Empty).Trim();
    if (string.IsNullOrWhiteSpace(text)) return null;

    DateTimeOffset parsedOffset;
    if (DateTimeOffset.TryParse(text, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AssumeUniversal | System.Globalization.DateTimeStyles.AdjustToUniversal, out parsedOffset)) return parsedOffset;

    DateTime parsedDateTime;
    var styles = System.Globalization.DateTimeStyles.AssumeUniversal | System.Globalization.DateTimeStyles.AdjustToUniversal;
    if (DateTime.TryParse(text, System.Globalization.CultureInfo.InvariantCulture, styles, out parsedDateTime)) return new DateTimeOffset(DateTime.SpecifyKind(parsedDateTime, DateTimeKind.Utc));

    var formats = new[] { "O", "o", "yyyy-MM-ddTHH:mm:ssZ", "yyyy-MM-ddTHH:mm:ss.fffZ", "yyyy-MM-ddTHH:mm:ss.fffffffZ", "yyyy-MM-dd HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss.fffZ", "yyyy-MM-dd HH:mm:ss.fffffffZ", "yyyy-MM-ddTHH:mm:sszzz", "yyyy-MM-ddTHH:mm:ss.fffzzz", "yyyy-MM-ddTHH:mm:ss.fffffffzzz" };
    for (var i = 0; i < formats.Length; i++)
    {
        if (DateTimeOffset.TryParseExact(text, formats[i], System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AssumeUniversal | System.Globalization.DateTimeStyles.AdjustToUniversal, out parsedOffset)) return parsedOffset;
    }
    return null;
}

static string NormalizeThumbprint(string value)
{
    var text = (value ?? string.Empty).Trim().ToUpperInvariant();
    var chars = new StringBuilder(text.Length);
    for (var i = 0; i < text.Length; i++)
    {
        var ch = text[i];
        if (ch == ' ' || ch == ':' || ch == '-' || ch == '\t' || ch == '\r' || ch == '\n') continue;
        chars.Append(ch);
    }
    return chars.ToString();
}

if (!captchaBypass)
{
    // normal captcha validation
}
```

#### 3.7.2 .NET 8+
```csharp
/*
Certificate CAPTCHA bypass for ASP.NET Core 8+ on Kestrel or IIS with client certificate forwarding enabled.

- Intended for hosts that expose the client certificate to ctx.Connection.GetClientCertificateAsync().
- If you are behind a reverse proxy, configure certificate forwarding so the client cert reaches ASP.NET Core.
- If the client certificate is unavailable, this fails closed and normal captcha validation runs.
- Time values should be UTC and preferably ISO 8601 / round-trip format.
*/

using System;
using System.Globalization;
using System.Text;
using Microsoft.AspNetCore.Http;

app.Use(async (ctx, next) =>
{
    var expectedOrigin = "<ORIGIN>";
    var expectedNotBeforeUtc = TryParseUtcOrFailClosed("<VALID_FROM_UTC>");
    var expectedNotAfterUtc = TryParseUtcOrFailClosed("<VALID_TO_UTC>");
    if (expectedNotBeforeUtc == null || expectedNotAfterUtc == null)
    {
        ctx.Items["BypassCaptcha"] = false;
        await next();
        return;
    }

    var currentOrigin = $"{ctx.Request.Scheme}://{ctx.Request.Host}";
    var originMatches = string.IsNullOrWhiteSpace(expectedOrigin) || string.Equals(currentOrigin, expectedOrigin, StringComparison.OrdinalIgnoreCase);
    var cert = await ctx.Connection.GetClientCertificateAsync();
    if (cert == null)
    {
        ctx.Items["BypassCaptcha"] = false;
        await next();
        return;
    }

    var thumbprintMatches = string.Equals(NormalizeThumbprint(cert.Thumbprint), NormalizeThumbprint("<THUMBPRINT>"), StringComparison.OrdinalIgnoreCase);
    if (!thumbprintMatches)
    {
        ctx.Items["BypassCaptcha"] = false;
        await next();
        return;
    }

    var certNotBeforeUtc = cert.NotBefore.ToUniversalTime();
    var certNotAfterUtc = cert.NotAfter.ToUniversalTime();
    var nowUtc = DateTime.UtcNow;
    var certValidityMatches = nowUtc >= certNotBeforeUtc && nowUtc <= certNotAfterUtc && certNotBeforeUtc <= expectedNotAfterUtc.Value.UtcDateTime && certNotAfterUtc >= expectedNotBeforeUtc.Value.UtcDateTime;
    var captchaBypass = originMatches && certValidityMatches;
    ctx.Items["BypassCaptcha"] = captchaBypass;
    if (!captchaBypass)
    {
        // normal captcha validation
    }

    await next();
});

static DateTimeOffset? TryParseUtcOrFailClosed(string value)
{
    var text = (value ?? string.Empty).Trim();
    if (string.IsNullOrWhiteSpace(text)) return null;

    DateTimeOffset parsedOffset;
    if (DateTimeOffset.TryParse(text, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out parsedOffset)) return parsedOffset;

    DateTime parsedDateTime;
    var styles = DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal;
    if (DateTime.TryParse(text, CultureInfo.InvariantCulture, styles, out parsedDateTime)) return new DateTimeOffset(DateTime.SpecifyKind(parsedDateTime, DateTimeKind.Utc));

    var formats = new[] { "O", "o", "yyyy-MM-ddTHH:mm:ssZ", "yyyy-MM-ddTHH:mm:ss.fffZ", "yyyy-MM-ddTHH:mm:ss.fffffffZ", "yyyy-MM-dd HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss.fffZ", "yyyy-MM-dd HH:mm:ss.fffffffZ", "yyyy-MM-ddTHH:mm:sszzz", "yyyy-MM-ddTHH:mm:ss.fffzzz", "yyyy-MM-ddTHH:mm:ss.fffffffzzz" };
    for (var i = 0; i < formats.Length; i++)
    {
        if (DateTimeOffset.TryParseExact(text, formats[i], CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out parsedOffset)) return parsedOffset;
    }
    return null;
}

static string NormalizeThumbprint(string value)
{
    var text = (value ?? string.Empty).Trim().ToUpperInvariant();
    var chars = new StringBuilder(text.Length);
    for (var i = 0; i < text.Length; i++)
    {
        var ch = text[i];
        if (ch == ' ' || ch == ':' || ch == '-' || ch == '\t' || ch == '\r' || ch == '\n') continue;
        chars.Append(ch);
    }
    return chars.ToString();
}
```

#### 3.7.3 Node.js (Express)
```javascript
// Expected cert thumbprint: <THUMBPRINT>
// Expected cert subject: <SUBJECT>
// Expected origin: <ORIGIN>
// Expected cert valid from UTC: <VALID_FROM_UTC>
// Expected cert valid to UTC: <VALID_TO_UTC>
// Node.js (Express)

function normalizeThumbprint(value) {
  return String(value || '').replace(/[\s:-]/g, '').toUpperCase();
}

function parseUtcOrFailClosed(value) {
  const text = String(value || '').trim();
  if (!text) return null;
  const ms = Date.parse(text);
  return Number.isFinite(ms) ? ms : null;
}

app.use((req, res, next) => {
  try {
    const expectedOrigin = '<ORIGIN>';
    const expectedNotBeforeUtc = parseUtcOrFailClosed('<VALID_FROM_UTC>');
    const expectedNotAfterUtc = parseUtcOrFailClosed('<VALID_TO_UTC>');
    if (expectedNotBeforeUtc === null || expectedNotAfterUtc === null) {
      req.captchaBypass = false;
      return next();
    }

    const currentOrigin = `${req.protocol}://${req.get('host')}`;
    const originMatches = !expectedOrigin || currentOrigin.toLowerCase() === expectedOrigin.toLowerCase();

    const cert = req.socket.getPeerCertificate?.();
    if (!cert || !cert.fingerprint256) {
      req.captchaBypass = false;
      return next();
    }

    const thumbprintMatches = normalizeThumbprint(cert.fingerprint256) === normalizeThumbprint('<THUMBPRINT>');
    if (!thumbprintMatches) {
      req.captchaBypass = false;
      return next();
    }

    const certNotBeforeMs = parseUtcOrFailClosed(cert.valid_from);
    const certNotAfterMs = parseUtcOrFailClosed(cert.valid_to);
    if (certNotBeforeMs === null || certNotAfterMs === null) {
      req.captchaBypass = false;
      return next();
    }

    const nowMs = Date.now();
    const certValidNow = nowMs >= certNotBeforeMs && nowMs <= certNotAfterMs;
    const overlapsExpected = certNotAfterMs >= expectedNotBeforeUtc && certNotBeforeMs <= expectedNotAfterUtc;

    req.captchaBypass = originMatches && certValidNow && overlapsExpected;
  } catch (e) {
    req.captchaBypass = false;
  }

  // If req.captchaBypass is false, perform your normal CAPTCHA validation.
  next();
});
```

#### 3.7.4 Python (Flask)
```python
# Expected cert thumbprint: <THUMBPRINT>
# Expected cert subject: <SUBJECT>
# Expected origin: <ORIGIN>
# Expected cert valid from UTC: <VALID_FROM_UTC>
# Expected cert valid to UTC: <VALID_TO_UTC>
# Python (Flask)

from datetime import datetime, timezone

def normalize_thumbprint(value):
    return "".join(c for c in str(value or "").upper() if c not in (" ", ":", "-", "\t", "\r", "\n"))

def parse_utc_or_fail_closed(value):
    text = str(value or "").strip()
    if not text:
        return None
    
    # Try common ISO 8601 parsing variations
    formats = [
        "%Y-%m-%dT%H:%M:%SZ",
        "%Y-%m-%dT%H:%M:%S.%fZ",
        "%Y-%m-%d %H:%M:%SZ",
        "%Y-%m-%d %H:%M:%S.%f",
        "%Y-%m-%dT%H:%M:%S%z",
        "%Y-%m-%dT%H:%M:%S.%f%z"
    ]
    
    # Try default fromisoformat (handles many standard offsets)
    try:
        # replace Z offset with +00:00 for fromisoformat compatibility in python < 3.11
        normalized = text.replace('Z', '+00:00')
        return datetime.fromisoformat(normalized).astimezone(timezone.utc)
    except Exception:
        pass

    for fmt in formats:
        try:
            return datetime.strptime(text, fmt).replace(tzinfo=timezone.utc)
        except Exception:
            continue
            
    return None

@app.before_request
def captcha_gate():
    try:
        expected_origin = '<ORIGIN>'
        expected_not_before = parse_utc_or_fail_closed('<VALID_FROM_UTC>')
        expected_not_after = parse_utc_or_fail_closed('<VALID_TO_UTC>')
        if not expected_not_before or not expected_not_after:
            g.captcha_bypass = False
            return

        current_origin = (request.host_url or '').rstrip('/')
        origin_matches = (not expected_origin) or (current_origin.lower() == expected_origin.lower())
        
        cert_fp = request.headers.get('X-Client-Cert-Thumbprint')
        if not cert_fp:
            g.captcha_bypass = False
            return
            
        thumbprint_matches = normalize_thumbprint(cert_fp) == normalize_thumbprint('<THUMBPRINT>')
        if not thumbprint_matches:
            g.captcha_bypass = False
            return
            
        cert_not_before = request.headers.get('X-Client-Cert-NotBeforeUtc')
        cert_not_after = request.headers.get('X-Client-Cert-NotAfterUtc')
        cert_from = parse_utc_or_fail_closed(cert_not_before)
        cert_to = parse_utc_or_fail_closed(cert_not_after)
        if not cert_from or not cert_to:
            g.captcha_bypass = False
            return

        now_utc = datetime.now(timezone.utc)
        cert_valid_now = cert_from <= now_utc <= cert_to
        overlaps_expected = cert_to >= expected_not_before and cert_from <= expected_not_after

        g.captcha_bypass = bool(origin_matches and cert_valid_now and overlaps_expected)
    except Exception:
        g.captcha_bypass = False

    # If g.captcha_bypass is False, perform your normal CAPTCHA validation.
```

#### 3.7.5 PHP
```php
// Expected cert thumbprint: <THUMBPRINT>
// Expected cert subject: <SUBJECT>
// Expected origin: <ORIGIN>
// Expected cert valid from UTC: <VALID_FROM_UTC>
// Expected cert valid to UTC: <VALID_TO_UTC>
// PHP
<?php

function normalizeThumbprint($value) {
    return strtoupper(str_replace([' ', ':', '-', "\t", "\r", "\n"], '', (string)$value));
}

function parseUtcOrFailClosed($value) {
    $text = trim((string)$value);
    if ($text === '') return null;
    try {
        $dt = new DateTimeImmutable($text, new DateTimeZone('UTC'));
        return $dt;
    } catch (Throwable $e) {
        return null;
    }
}

try {
    $expectedOrigin = '<ORIGIN>';
    $expectedNotBeforeUtc = parseUtcOrFailClosed('<VALID_FROM_UTC>');
    $expectedNotAfterUtc = parseUtcOrFailClosed('<VALID_TO_UTC>');
    if ($expectedNotBeforeUtc === null || $expectedNotAfterUtc === null) {
        $captchaBypass = false;
        return;
    }

    $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
    $currentOrigin = $scheme . '://' . ($_SERVER['HTTP_HOST'] ?? '');
    $originMatches = empty($expectedOrigin) || strcasecmp($currentOrigin, $expectedOrigin) === 0;

    $thumb = $_SERVER['SSL_CLIENT_FINGERPRINT'] ?? '';
    if ($thumb === '') {
        $captchaBypass = false;
        return;
    }

    $thumbprintMatches = normalizeThumbprint($thumb) === normalizeThumbprint('<THUMBPRINT>');
    if (!$thumbprintMatches) {
        $captchaBypass = false;
        return;
    }

    $certNotBeforeUtc = $_SERVER['SSL_CLIENT_V_START'] ?? '';
    $certNotAfterUtc = $_SERVER['SSL_CLIENT_V_END'] ?? '';
    $certFrom = parseUtcOrFailClosed($certNotBeforeUtc);
    $certTo = parseUtcOrFailClosed($certNotAfterUtc);
    if ($certFrom === null || $certTo === null) {
        $captchaBypass = false;
        return;
    }

    $nowUtc = new DateTimeImmutable('now', new DateTimeZone('UTC'));
    $certValidNow = $nowUtc >= $certFrom && $nowUtc <= $certTo;
    $overlapsExpected = $certTo >= $expectedNotBeforeUtc && $certFrom <= $expectedNotAfterUtc;

    $captchaBypass = $originMatches && $certValidNow && $overlapsExpected;
} catch (Throwable $e) {
    $captchaBypass = false;
}

if (!$captchaBypass) { /* normal captcha validation */ }
```

#### 3.7.6 Java (Spring Boot)
```java
// Expected cert thumbprint: <THUMBPRINT>
// Expected cert subject: <SUBJECT>
// Expected origin: <ORIGIN>
// Expected cert valid from UTC: <VALID_FROM_UTC>
// Expected cert valid to UTC: <VALID_TO_UTC>
// Java (Spring Boot) | Certificate CAPTCHA bypass

package com.example.captcha;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.security.MessageDigest;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

@Component
public class CaptchaBypassFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) 
            throws IOException, ServletException {
        
        boolean captchaBypass = false;
        try {
            String expectedOrigin = "<ORIGIN>";
            Instant expectedNotBeforeUtc = parseUtcOrFailClosed("<VALID_FROM_UTC>");
            Instant expectedNotAfterUtc = parseUtcOrFailClosed("<VALID_TO_UTC>");

            if (expectedNotBeforeUtc != null && expectedNotAfterUtc != null) {
                String proto = req.getScheme();
                String host = req.getHeader("Host");
                String currentOrigin = (proto != null && host != null) ? proto + "://" + host : "";
                boolean originMatches = expectedOrigin == null || expectedOrigin.isBlank() || expectedOrigin.equalsIgnoreCase(currentOrigin);

                X509Certificate[] certs = (X509Certificate[]) req.getAttribute("jakarta.servlet.request.X509Certificate");
                X509Certificate cert = (certs != null && certs.length > 0) ? certs[0] : null;

                if (cert != null) {
                    String expectedNormalized = normalizeThumbprint("<THUMBPRINT>");
                    String hashAlgorithm = expectedNormalized.length() == 40 ? "SHA-1" : "SHA-256";
                    String certThumbprint = getThumbprint(cert, hashAlgorithm);

                    boolean thumbprintMatches = expectedNormalized.equals(normalizeThumbprint(certThumbprint));
                    if (thumbprintMatches) {
                        Instant certNotBeforeUtc = cert.getNotBefore().toInstant();
                        Instant certNotAfterUtc = cert.getNotAfter().toInstant();
                        Instant nowUtc = Instant.now();

                        boolean certValidNow = !nowUtc.isBefore(certNotBeforeUtc) && !nowUtc.isAfter(certNotAfterUtc);
                        boolean overlapsExpected = !certNotAfterUtc.isBefore(expectedNotBeforeUtc) && !certNotBeforeUtc.isAfter(expectedNotAfterUtc);

                        captchaBypass = originMatches && certValidNow && overlapsExpected;
                    }
                }
            }
        } catch (Exception e) {
            captchaBypass = false;
        }

        if (!captchaBypass) {
            // normal captcha validation
        }

        chain.doFilter(req, res);
    }

    private static Instant parseUtcOrFailClosed(String value) {
        if (value == null) return null;
        String text = value.trim();
        if (text.isEmpty()) return null;

        try {
            return Instant.parse(text);
        } catch (Exception e) {}

        try {
            return OffsetDateTime.parse(text).toInstant();
        } catch (Exception e) {}

        try {
            return ZonedDateTime.parse(text).toInstant();
        } catch (Exception e) {}

        String[] patterns = {
            "yyyy-MM-dd HH:mm:ssX",
            "yyyy-MM-dd HH:mm:ss.SSSX",
            "yyyy-MM-dd'T'HH:mm:ssX",
            "yyyy-MM-dd'T'HH:mm:ss.SSSX",
            "yyyy-MM-dd HH:mm:ss",
            "yyyy-MM-dd'T'HH:mm:ss",
            "yyyy-MM-dd HH:mm:ss.SSS",
            "yyyy-MM-dd'T'HH:mm:ss.SSS"
        };
        for (String pattern : patterns) {
            try {
                DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern).withZone(ZoneOffset.UTC);
                return Instant.from(dtf.parse(text));
            } catch (Exception e) {}
        }
        return null;
    }

    private static String normalizeThumbprint(String value) {
        if (value == null) return "";
        StringBuilder sb = new StringBuilder();
        String upper = value.toUpperCase();
        for (int i = 0; i < upper.length(); i++) {
            char c = upper.charAt(i);
            if (c != ' ' && c != ':' && c != '-' && c != '\t' && c != '\r' && c != '\n') {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    private static String getThumbprint(X509Certificate cert, String algorithm) {
        try {
            MessageDigest md = MessageDigest.getInstance(algorithm);
            byte[] der = cert.getEncoded();
            byte[] digest = md.digest(der);
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) {
                sb.append(String.format("%02X", b));
            }
            return sb.toString();
        } catch (Exception e) {
            return "";
        }
    }
}
```

---

## 4. Database Schema (Audit Columns)
Every table ingested via `runs-manager` receives these columns automatically:
- `import_batch_id` (String): Unique ingestion timestamp (`yyyyMMddHHmmssfff`).
- `source_file_rel` (String): Path to the worker's original TSV.
- `source_row_no` (Int): Original line index in the log.

---

## 5. Environment Variables & Contracts
- `FWL_RUN_ID`: Correlates multi-node results (examples: `run-1`, `run-2`).
- `RUN_SLOT`: Slot identity. Depending on how the process was launched it may be:
  - a numeric slot (`2`), or
  - a run id string (`run-2`) (Shard-Run uses this form for compatibility).
- `FWL_CLOUD_TASK_KEY`: Cloud/orchestration grouping key (Shard-Run sets this).
- `FWL_RECYCLE_USERS`: `1|0` (Shard-Run sets this; load-test also has `-recycleUsers`).
- `FWL_CAPTCHA_BYPASS_LICENSE`: Captcha bypass license token fallback for `-captchaBypassLicense`.
- `FWL_CLIENT_CERT_PASSWORD`: Password fallback for `-clientCertPassword`.
- `FWL_NETWORK_POLICY_PATH`: Fallback for `-networkPolicy`.
- `FWL_NETWORK_POLICY_DEFAULT_ACTION`: Fallback for `-networkPolicyDefaultAction`.
- `FWL_IDENTITY_INJECTION_FOLDER`: Fallback for identity injection folder (when identity rules require it).
- `FWL_IDENTITY_INJECTION_SERIAL_NO`: Fallback for identity selection override (when identity rules require it).
- `FWL_SYNCPOINT_WORKERS_DIVISOR`: Tunes wait/go behavior for syncpoints in local orchestration (Runs-Manager reads it).

---

## 6. Execution Signals (Triage)
Do not assume a fixed "signals taxonomy" unless the run artifacts explicitly provide one.

Minimum triage workflow for customer AI:
1. Ask for the exact command line used.
2. Ask for the run output folder (the `-outDir` tree) or a zip of artifacts.
3. Identify whether failure is:
   - JSON validation (`--dry-run` fails)
   - Selector resolution (0 matches / multiple matches)
   - Navigation/baseUrl issues (`-baseUrlOverride`, `-startUrlOverride`)
   - Orchestration/syncpoint waits (Shard-Run / `-orchestrationServer`)

## 7. Output Artifacts (What to Ask For)
When diagnosing issues, ask for the `-outDir` folder. Typical structure:

```text
<outDir>/
  Runs/
    User1/
      Results/
        http_report.tsv
        http_grouped.tsv
        steps_report.tsv
        page_snapshot.html
        page_visible_detail.html
        dom_summary.json
        interactive_map.json
        visible_text.json
        console.out.txt
        console.err.txt
        console.debug.txt
      Summary/
        script_summary.txt
        http_summary.txt
      Snapshots/
        Snapshot-00001/ (optional)
          screenshot.png (optional)
          page_snapshot.html (optional)
          page_visible_detail.html (optional)
          dom_summary.json (optional)
          interactive_map.json (optional)
          visible_text.json (optional)
          http_report.tsv (optional)
    Summary/
      http_report.tsv
      http_summary.txt
      steps_report.tsv
      script_summary.txt
```

High-value files for triage:
- `Runs/User*/Results/steps_report.tsv` (step-by-step failures)
- `Runs/User*/Results/console.err.txt` (browser console errors)
- `Runs/User*/Results/page_snapshot.html` + `page_visible_detail.html` + JSON page sidecars (page state, selector, and UX debugging)
- `Runs/Summary/*` (aggregate view in multi-user runs)

## 7.1 Test Results Artifacts (What Each File Contains)
This section describes the common artifact files produced by `load-test.exe` and how Runs-Manager consumes them.

### 7.1.1 Tree Structure (Conventions)
Most runs follow this pattern:

```text
<runRoot>/
  recording.json               # the script used (often copied into the run folder)
  Runs/
    Summary/                   # aggregate summaries across all users
    User<N>/
      Results/                 # per-user results (step + network + page inspection + console)
      Summary/                 # per-user summary metrics
      Snapshots/               # optional snapshots (folders per snapshot id)
```

In Cloud Runs, a task folder may contain multiple `run-*` folders:

```text
task-<id>/
  run-1/
  run-2/
  ...
  cloudrun_signature*.json
```

### 7.1.2 Results Files (Per User: `Runs/User<N>/Results/`)
- `steps_report.tsv`: one row per step execution (step id, action, timing, selector/value, and error fields).
- `http_report.tsv`: one row per HTTP call observed during the run (method, domain/path, timing, status, bytes, error).
- `http_grouped.tsv`: grouped HTTP summary (aggregated by domain/protocol/path group).
- `page_snapshot.html`: close-to-origin, human-readable page snapshot captured at end of run. Scripts are stripped, so it is not an executable reproduction.
- `page_visible_detail.html`: transformed visible-elements tree with computed styles, bounding rects, and visibility signals for layout/UX/selector analysis. It is partial by design and should not be treated as raw application DOM.
- `dom_summary.json`: compact page identity/state summary (URL, title, readyState, viewport, meta tags, counts, top ids/classes, page-state hints).
- `interactive_map.json`: actionable element map (links, buttons, inputs, selectors, rects, visibility/state signals).
- `visible_text.json`: visible semantic text extraction (headings, labels, buttons, links, errors, loading text, modals/toasts, CTAs).
- `console.out.txt` / `console.err.txt` / `console.debug.txt`: browser console output split by severity stream.
- `sync_users_report.tsv`: syncpoint coordination facts (used when syncpoints/wait-go are enabled).

### 7.1.3 Summary Files (Per User: `Runs/User<N>/Summary/`)
These are optional/feature-dependent, but commonly present:
- `script_summary.txt`: high-level run summary (timings, selected databank users, error counts, snapshot counts).
- `http_summary.txt`: compact HTTP summary.
- `http_timing_report.tsv`: timing distribution details for calls.
- `step_execution_diagnostics.tsv`: step-level diagnostics for resolution failures (useful for selector debugging).
- `step_vitals.tsv`: per-step vitals samples (when enabled).
- `navigation_vitals.tsv`: navigation/vitals samples (when enabled).
- `browser_runtime_metrics.tsv`: browser-level runtime metrics sampling (when enabled).
- `host_runtime_metrics.tsv`: host machine runtime metrics sampling (when enabled).
- `js_errors.tsv`: captured JavaScript errors observed during replay.
- `file_uploads.tsv`: file upload audit rows (when uploads occur).
- `downloads.tsv`: downloads audit rows (when downloads occur).
- `url_alignment_report.tsv`: URL alignment / mismatch report rows (when produced).

### 7.1.4 Snapshot Files (Per Snapshot: `Runs/User<N>/Snapshots/<SnapshotId>/`)
Snapshot folders (for example `Snapshot-Final` or `Snapshot-00001`) can include:
- `screenshot.png`: screenshot at snapshot time (if enabled).
- `trace.zip`: Playwright trace bundle (if enabled).
- `page_snapshot.html` / `page_visible_detail.html` / `dom_summary.json` / `interactive_map.json` / `visible_text.json`: page-inspection artifacts at snapshot time (if enabled).
- `http_report.tsv` / `http_grouped.tsv` / `http_summary.txt`: network capture at snapshot time (if enabled).
- `CustomReport.txt`: optional human-readable custom report text.
- `snapshot_vitals.json`: snapshot metadata/vitals payload (if produced).
Plus the same optional TSVs listed in user summary (vitals, runtime metrics, js errors, uploads/downloads).

### 7.1.5 Aggregate Summary Files (`Runs/Summary/`)
- `script_summary.txt`: aggregated summary across all users.
- `http_summary.txt`: aggregated HTTP summary.
- `http_report.tsv`: aggregated grouped HTTP report (same schema as grouped report).
- `steps_report.tsv`: aggregated step summary (counts by action, errors/success).
- `sync_users_report.tsv`: aggregated syncpoint report (when present).

## 7.2 How to Collect Artifacts for Runs-Manager (Manual)
Runs-Manager uses the `webLoadDir` you provide to discover and parse runs:
- It treats any directory named `Runs` as a candidate results tree (it searches recursively under `webLoadDir`).
- `webLoadDir` can be:
  - a single run folder (for example `...\run-1\`), or
  - the `Runs` folder itself (`...\run-1\Runs\`), or
  - a higher-level folder containing many runs (for example a CloudRuns `task-...` folder).

Practical manual collection options:
1. Zip the entire `<runRoot>` folder (include `recording.json` and the `Runs/` tree).
2. For Cloud Runs, zip the specific `run-N/` folder you want to analyze.

Runs-Manager local persistence:
- Known folders list is stored in `ReportsAppState.json` next to `runs-manager.exe`.
- Saved analysis presets are stored under `<webLoadDir>\Reports Analysis\SavedReports.json`.

## 8. Exit Codes (Process-Level)
### 8.1 load-test.exe
- `0`: success.
- `1`: invalid usage, missing files, invalid JSON, or runtime failure.
- `130`: interrupted (Ctrl+C).
- `143`: terminated (SIGTERM).

### 8.2 shard-run.exe
- `0`: success.
- `1`: failed to launch or execute `load-test.exe`.
- `2`: invalid arguments / usage.

## 9. Performance Advisory
- **Massive load**: prefer `-noVitals` and `-noBrowserSnapshot` to reduce CPU and I/O.
- **Shard runs**: use Shard-Run for on-prem horizontal scaling; it standardizes run grouping and output layout.
- **AI budgets**: set and enforce a budget cap before starting AI tasks; do not run open-ended analysis without an explicit cap.

