### Install @proofofbot/client-akamai Source: https://proofofbots.github.io/web-re-toolkit/packages/akamai/node Install the Akamai client package. Requires Node 18 or later; the platform-specific `wred` binary is installed as an optional dependency. ```bash npm install @proofofbot/client-akamai ``` -------------------------------- ### Install the Akamai client package Source: https://proofofbots.github.io/web-re-toolkit/packages/go Run this command to install the generated Akamai client package. Requires Go 1.21 or later. ```bash go get github.com/proofofbots/web-re-toolkit/packages/go/clients/akamai ``` -------------------------------- ### Install wre-cli on PATH with cargo install Source: https://proofofbots.github.io/web-re-toolkit/start/install Installs the CLI binary so you can type 'wre' instead of the full path. Requires the build to have completed. ```bash cargo install --path crates/wre-cli ``` -------------------------------- ### Install Kasada client Source: https://proofofbots.github.io/web-re-toolkit/packages/kasada/node Install the required package for Kasada integration. ```bash npm install @proofofbot/client-kasada ``` -------------------------------- ### Install the Kasada client package Source: https://proofofbots.github.io/web-re-toolkit/packages/kasada/python Install the `wre-client-kasada` package via pip. Requires Python 3.9 or later; the `wred` binary is included in the wheel. ```bash pip install wre-client-kasada ``` -------------------------------- ### Install Kasada Go client Source: https://proofofbots.github.io/web-re-toolkit/packages/kasada/go Install the Kasada client package for Go. Requires Go 1.21 or later. ```bash go get github.com/proofofbots/web-re-toolkit/packages/go/clients/kasada ``` -------------------------------- ### Full run: discover, solve, report, and request Source: https://proofofbots.github.io/web-re-toolkit/packages/kasada/rust Complete example that opens a session, discovers the page, solves the interrogation if protected, reports flagged checks, and fetches the page again. A session that never answered gets the interrogation instead of the page. ```rust use std::collections::BTreeSet; use regex::Regex; use wre_sdk_kasada::{Client, DiscoverInput, KasadaConfig, OpenOptions, RequestInput, SolveInput}; fn listings(html: &str) -> Vec { let pattern = Regex::new(r#"href="(/property-[^"]+)""#).unwrap(); let mut found = BTreeSet::new(); for capture in pattern.captures_iter(html) { found.insert(capture[1].to_string()); } found.into_iter().collect() } fn main() -> Result<(), Box> { let page = std::env::var("PAGE") .unwrap_or_else(|_| "https://www.realestate.com.au/buy/in-sydney,+nsw/list-1".into()); let config = KasadaConfig { page_url: Some(page.clone()), ..Default::default() }; let client = Client::open(&config, OpenOptions::default())?; let surface = client.discover(&DiscoverInput::default())?; println!("{page} answered {}, protected {}", surface.status, surface.protected); if !surface.protected { println!("no interrogation is being served, nothing to solve"); } else { let solved = client.solve(&SolveInput::default())?; println!( "verdict {}, clearance {}", solved.verdict, solved.clearance.as_deref().unwrap_or("") ); println!("payload {} bytes in {} ms", solved.payload_bytes, solved.ms); let report = client.report()?; let flagged = report.flagged.as_array().map(Vec::len).unwrap_or(0); println!("the agent flagged {flagged} of its own checks"); } let answered = client.request(&RequestInput { url: page.clone(), ..Default::default() })?; println!("page {}, {} bytes", answered.status, answered.bytes); let found = listings(&answered.body); println!("{} listings", found.len()); for href in found.iter().take(10) { println!(" https://www.realestate.com.au{href}"); } client.close()?; Ok(()) } ``` -------------------------------- ### Install @proofofbot/runtime Source: https://proofofbots.github.io/web-re-toolkit/packages/node Install the runtime package via npm. ```bash npm install @proofofbot/runtime ``` -------------------------------- ### Install the runtime package Source: https://proofofbots.github.io/web-re-toolkit/packages/go Run this command to install the runtime package, used to drive a binary that has no generated package or to handle multiple targets from one process. ```bash go get github.com/proofofbots/web-re-toolkit/packages/go/wre ``` -------------------------------- ### Install wre-client-akamai Source: https://proofofbots.github.io/web-re-toolkit/packages/akamai/python Install the wre-client-akamai package. Requires Python 3.9 or later; the wred binary ships in the wheel for your platform. ```bash pip install wre-client-akamai ``` -------------------------------- ### Perform a full Akamai session run Source: https://proofofbots.github.io/web-re-toolkit/packages/akamai/rust This example demonstrates warming a session, discovering protection status, solving challenges, extracting antiforgery tokens, and submitting a login form. ```rust use std::collections::BTreeMap; use std::time::{SystemTime, UNIX_EPOCH}; use serde_json::json; use wre_sdk_akamai::{ AkamaiConfig, Client, DiscoverInput, OpenOptions, RequestInput, SolveInput, }; const PAGE: &str = "https://login.xero.com/identity/user/login"; const PRECHECK: &str = "https://login.xero.com/identity/user/login/pre-check"; fn field(html: &str, name: &str) -> Option { let at = html.find(&format!("name=\"{name}\""))?; let rest = &html[at..]; let start = rest.find("value=\"")?; let tail = &rest[start + 7..]; let end = tail.find('"')?; Some(tail[..end].to_string()) } fn headers(pairs: &[(&str, &str)]) -> BTreeMap { pairs .iter() .map(|(key, value)| (key.to_string(), value.to_string())) .collect() } fn main() -> Result<(), Box> { let config = AkamaiConfig { page_url: Some(PAGE.into()), wait_ms: Some(100), rounds: Some(1), ..Default::default() }; let client = Client::open(&config, OpenOptions::default())?; let found = client.discover(&DiscoverInput::default())?; println!("discover: status {} protected {}", found.status, found.protected); let solved = client.solve(&SolveInput::default())?; println!( "solve: payload {} bytes, posts {}", solved.payload.as_deref().map(str::len).unwrap_or(0), solved.posts ); let state = client.page()?; let html = if state.html.is_empty() { client .request(&RequestInput { url: PAGE.into(), ..Default::default() })? .body } else { state.html.clone() }; let token = state .fields .get("__RequestVerificationToken") .cloned() .or_else(|| field(&html, "__RequestVerificationToken")) .ok_or("no antiforgery token")?; let return_url = state .fields .get("ReturnUrl") .cloned() .or_else(|| field(&html, "ReturnUrl")) .unwrap_or_default(); let username = format!( "nx{:x}@example.com", SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() ); client.request(&RequestInput { url: PRECHECK.into(), method: Some("POST".into()), json: Some(json!({ "Username": username })), headers: Some(headers(&[ ("accept", "application/json, text/plain, */*"), ("origin", "https://login.xero.com"), ("requestverificationtoken", &token), ])), ..Default::default() })?; let mut form = BTreeMap::new(); form.insert("ReturnUrl".to_string(), return_url); form.insert("PreCheckCompleted".to_string(), "true".to_string()); form.insert("Username".to_string(), username); form.insert("Password".to_string(), "Nx7!aQ2zR9kL".to_string()); form.insert("__RequestVerificationToken".to_string(), token); let answer = client.request(&RequestInput { url: PAGE.into(), method: Some("POST".into()), form: Some(form), headers: Some(headers(&[ ("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"), ("origin", "https://login.xero.com"), ("sec-fetch-dest", "document"), ("sec-fetch-mode", "navigate"), ("sec-fetch-site", "same-origin"), ("upgrade-insecure-requests", "1"), ])), ..Default::default() })?; let body = answer.body.to_lowercase(); println!( "login: status {} refused {} credential_error {}", answer.status, answer.refused, body.contains("email address or password") || body.contains("incorrect") ); client.close()?; Ok(()) } ``` -------------------------------- ### Full run: discover, solve, report, and fetch Source: https://proofofbots.github.io/web-re-toolkit/packages/kasada/python A complete example that opens a session, discovers the page, solves the interrogation if protected, reports flagged checks, and fetches the page. Uses `open_client` and handles `WreError`. ```python import os import re import sys from wre_client_kasada import WreError, open_client PAGE = os.environ.get("PAGE", "https://www.realestate.com.au/buy/in-sydney,+nsw/list-1") LISTING = re.compile(r'href="(/property-[^"]+)"') def listings(html: str) -> list[str]: seen: dict[str, None] = {} for href in LISTING.findall(html): seen.setdefault(href, None) return list(seen) def main() -> int: with open_client({"page_url": PAGE}) as client: try: surface = client.discover({}) print(f"{PAGE} answered {surface['status']}, protected {surface['protected']}") if not surface["protected"]: print("no interrogation is being served, nothing to solve") else: solved = client.solve({}, deadline=120.0) print(f"verdict {solved['verdict']}, clearance {solved['clearance']}") print(f"payload {solved['payload_bytes']} bytes in {solved['ms']} ms") report = client.report() print(f"the agent flagged {len(report['flagged'])} of its own checks") page = client.request({"url": PAGE}, deadline=60.0) print(f"page {page['status']}, {page['bytes']} bytes") found = listings(page["body"]) print(f"{len(found)} listings") for href in found[:10]: print(f" https://www.realestate.com.au{href}") except WreError as error: print(f"{error.kind}: {error}", file=sys.stderr) return 1 return 0 if __name__ == "__main__": raise SystemExit(main()) ``` -------------------------------- ### Run Altcha lab test with cargo Source: https://proofofbots.github.io/web-re-toolkit/research/altcha Starts the lab server and runs the ignored Rust lab test, which solves a live challenge and gets verified: true back in about 20 ms. ```bash python3 reference/altcha/lab/server.py & cargo test -p wre-client-altcha --test lab -- --ignored ``` -------------------------------- ### Start sandbox capture server Source: https://proofofbots.github.io/web-re-toolkit/guides/sandbox Starts the sandbox capture server on http://127.0.0.1:8099 and waits for a browser to connect. After the device profile is sent, the process exits and the profile is stored in profiles/. ```bash wre sandbox capture ``` -------------------------------- ### Install wre-runtime with pip Source: https://proofofbots.github.io/web-re-toolkit/packages/python Install the wre-runtime package from PyPI. This is the first step to use the runtime to drive a binary that has no generated package or to manage multiple targets from one process. ```bash pip install wre-runtime ``` -------------------------------- ### Challenge JSON example Source: https://proofofbots.github.io/web-re-toolkit/research/altcha Example of a v3 protocol challenge JSON object with parameters and signature. The parameters include algorithm, cost, keyLength, keyPrefix, nonce, and salt. ```json { "parameters": { "algorithm": "SHA-256", "cost": 10, "keyLength": 32, "keyPrefix": "a9a9e81c084ad44c25351c5f5568ce22", "nonce": "9295b841727d48460ca4b4954584a14f", "salt": "b83df484bc74cdde5caad7992284ca01" }, "signature": "b3aeb49b8a27accf753a38d53e180ce8aaf6cacf72480719f4872d884574a168" } ``` -------------------------------- ### Full Kasada run example Source: https://proofofbots.github.io/web-re-toolkit/packages/kasada/node A complete workflow that opens a session, checks for protection, solves the interrogation, reports findings, and scrapes page content. ```javascript import { open, WreError } from "@proofofbot/client-kasada"; const PAGE = process.env.PAGE ?? "https://www.realestate.com.au/buy/in-sydney,+nsw/list-1"; const listings = (html) => { const found = new Set(); const pattern = /href="(\/property-[^"]+)"/g; let match; while ((match = pattern.exec(html)) !== null) found.add(match[1]); return [...found]; }; const client = await open({ page_url: PAGE }); try { const surface = await client.discover({}); console.log(`${PAGE} answered ${surface.status}, protected ${surface.protected}`); if (!surface.protected) { console.log("no interrogation is being served, nothing to solve"); } else { const solved = await client.solve({}, { deadlineMs: 120000 }); console.log(`verdict ${solved.verdict}, clearance ${solved.clearance}`); console.log(`payload ${solved.payload_bytes} bytes in ${solved.ms} ms`); const report = await client.report(); console.log(`the agent flagged ${report.flagged.length} of its own checks`); } const page = await client.request({ url: PAGE }, { deadlineMs: 60000 }); console.log(`page ${page.status}, ${page.bytes} bytes`); const found = listings(page.body); console.log(`${found.length} listings`); for (const listing of found.slice(0, 10)) console.log(` https://www.realestate.com.au${listing}`); } catch (error) { if (error instanceof WreError) { console.error(`${error.kind}: ${error.message}`); process.exitCode = 1; } else { throw error; } } finally { await client.close(); } ``` -------------------------------- ### Open browser page and run script with virtual time Source: https://proofofbots.github.io/web-re-toolkit/guides/sandbox Opens a document on top of an installed profile using `wre_sandbox::browser::open`, then charges a one-off cost, runs a script, loads, plays events, and advances virtual time. `Page::read` extracts page structure and drops `value` attributes. ```rust let page = Page::read(url, html).with_epoch(now_ms()); let mut browser = open(&profile, &page, hooks, RealmOptions::default())?; browser.charge_on("bmak", "startTs", 25.0)?; browser.run(&script, "target:sensor")?; browser.load()?; browser.play(stream.events())?; browser.advance(4_000.0)?; ``` -------------------------------- ### Install sandbox and iterate misses Source: https://proofofbots.github.io/web-re-toolkit/guides/sandbox Installs a sandbox on a realm and profile, then iterates over recorded misses after running the target. Misses are logged when a lookup has no recorded answer, and should be read together with `wre grade` output. ```rust let sandbox = install(&mut realm, &profile)?; // ... run the target ... for miss in sandbox.misses() { println!("{miss}"); } ``` -------------------------------- ### Connect, open session, and call solve Source: https://proofofbots.github.io/web-re-toolkit/packages/node Connect to a sidecar binary, open a session, call 'solve' with a deadline, and handle a blocked error. The sidecar serves many sessions; reuse a session to avoid expensive state setup. ```javascript import { connect, WreError, ErrorKind } from "@proofofbot/runtime"; const sidecar = await connect({ binary: "/path/to/wred" }); console.log(sidecar.hello.schema_hash); const session = await sidecar.open("example", {}); try { const result = await session.call("solve", { url: "https://acme.example/" }, { deadlineMs: 20000, }); console.log(result); } catch (err) { if (err instanceof WreError && err.kind === ErrorKind.Blocked) { console.error("target reported a block, retryable:", err.retryable); } throw err; } await session.close(); await sidecar.shutdown(); ``` -------------------------------- ### `open` request with `diag` configuration Source: https://proofofbots.github.io/web-re-toolkit/reference/protocol Example `open` request with an optional `diag` object that configures the session's diagnostics recorder. The `diag` object is optional and controls diagnostics behavior. ```json {"target":"example","config":{},"diag":{"mode":"on_error","dir":"/tmp/reports","include_params":false,"max_events":400,"keep_files":20}} ``` -------------------------------- ### Full Kasada session run Source: https://proofofbots.github.io/web-re-toolkit/packages/kasada/go Full run example: open a session, discover the page, solve if protected, report flagged checks, and fetch the page again through the same session. Uses the realestate.com.au URL by default and extracts property links. ```go package main import ( "context" "encoding/json" "log" "os" "regexp" "time" clientkasada "github.com/proofofbots/web-re-toolkit/packages/go/clients/kasada" "github.com/proofofbots/web-re-toolkit/packages/go/wre" ) var listing = regexp.MustCompile(`href="(/property-[^"]+)"`) func listings(html string) []string { seen := map[string]bool{} found := []string{} for _, match := range listing.FindAllStringSubmatch(html, -1) { if !seen[match[1]] { seen[match[1]] = true found = append(found, match[1]) } } return found } func main() { page := os.Getenv("PAGE") if page == "" { page = "https://www.realestate.com.au/buy/in-sydney,+nsw/list-1" } ctx := context.Background() client, err := clientkasada.Open(ctx, &clientkasada.KasadaConfig{PageURL: &page}, clientkasada.OpenOptions{}) if err != nil { log.Fatal(err) } defer client.Close(ctx) surface, err := client.Discover(ctx, clientkasada.DiscoverInput{}) if err != nil { log.Fatal(err) } log.Printf("%s answered %d, protected %v", page, surface.Status, surface.Protected) if !surface.Protected { log.Println("no interrogation is being served, nothing to solve") } else { solveCtx, cancel := context.WithTimeout(ctx, 120*time.Second) solved, err := client.Solve(solveCtx, clientkasada.SolveInput{}) cancel() if err != nil { if wre.IsKind(err, wre.KindBlocked) { log.Fatalf("blocked: %v", err) } log.Fatal(err) } clearance := "" if solved.Clearance != nil { clearance = *solved.Clearance } log.Printf("verdict %s, clearance %s", solved.Verdict, clearance) log.Printf("payload %d bytes in %d ms", solved.PayloadBytes, solved.Ms) report, err := client.Report(ctx) if err != nil { log.Fatal(err) } var flagged []json.RawMessage if err := json.Unmarshal(report.Flagged, &flagged); err != nil { log.Fatal(err) } log.Printf("the agent flagged %d of its own checks", len(flagged)) } fetchCtx, cancel := context.WithTimeout(ctx, 60*time.Second) defer cancel() answered, err := client.Request(fetchCtx, clientkasada.RequestInput{URL: page}) if err != nil { log.Fatal(err) } log.Printf("page %d, %d bytes", answered.Status, answered.Bytes) found := listings(answered.Body) log.Printf("%d listings", len(found)) for _, href := range found[:min(10, len(found))] { log.Printf(" https://www.realestate.com.au%s", href) } } ``` -------------------------------- ### Verify the binary with wre --help Source: https://proofofbots.github.io/web-re-toolkit/start/install Runs the built binary to display its help output. The binary is located at ./target/release/wre. ```bash ./target/release/wre --help ``` -------------------------------- ### Initialize a workspace with wre init Source: https://proofofbots.github.io/web-re-toolkit/start/workspace Creates a new workspace with the given target name and URL. The `--url` flag specifies the target's URL. Without `--root`, `wre` walks up for the nearest `wre.toml`, then for `.git`. ```bash wre init acme --url https://acme.example/ ``` -------------------------------- ### ALTCHA v1 challenge JSON example Source: https://proofofbots.github.io/web-re-toolkit/research/altcha Example of a v1 challenge payload sent by older server libraries. The v3 widget accepts this flat object and internally rewrites it to the v3 shape. ```json { "algorithm": "SHA-256", "challenge": "f4cb24d4a5bffabadd37c4ce3cfb5246adc8f2cb88e3e49497f0b3c3af90ffaf", "salt": "saltysalt?expires=1700000000", "signature": "c053bcd07083810a8a9412efb35168a56b6a2d8ca03bbe9e52ceceb69f591d9a" } ``` -------------------------------- ### Report response Source: https://proofofbots.github.io/web-re-toolkit/guides/kasada Example report response. An empty flagged array is the goal; a flagged check names itself and quotes the value it objected to. ```json { "posted": 1, "about": { "build": "…", "version": "j-1.2.661", "checks": 123 }, "flagged": [] } ``` -------------------------------- ### Scaffold a client with wre client new Source: https://proofofbots.github.io/web-re-toolkit/guides/clients Scaffolds a new client crate under clients/acme, adds a conformance suite stub, and wires it into wred behind a target-acme feature. The skeleton's solve returns unsupported until implemented. ```bash wre client new acme --summary "Seals payloads for acme" ``` -------------------------------- ### Connect to wred and run a session with connect() Source: https://proofofbots.github.io/web-re-toolkit/packages/python Connect to a wred binary using the synchronous API. The binary path is required here, but connect() can resolve it automatically when omitted, checking the WRE_BINARY environment variable first. Set WRE_BINARY to an absolute path to use a local build, which skips the SHA-256 check. ```python from wre_runtime import connect with connect(binary="/path/to/wred") as sidecar: print(sidecar.hello) with sidecar.open("example", {"headless": True}) as session: result = session.call( "solve", {"url": "https://acme.example/"}, deadline=20.0, ) print(result) print(sidecar.metrics()) ``` -------------------------------- ### Use the generated Rust SDK Source: https://proofofbots.github.io/web-re-toolkit/packages/rust Open a client with an AkamaiConfig, solve a challenge, and print the resulting cookies. The binary is located via WRE_BINARY, then WRE_WRED, then target/release/wred upward from the working directory, then PATH. ```rust use wre_sdk_akamai::{AkamaiConfig, Client, OpenOptions, SolveInput}; let config = AkamaiConfig { page_url: Some("https://acme.example/".into()), ..Default::default() }; let client = Client::open(&config, OpenOptions::default())?; let solved = client.solve(&SolveInput::default())?; println!("{:?}", solved.cookies); client.close()?; ``` -------------------------------- ### Solve and request operations Source: https://proofofbots.github.io/web-re-toolkit/guides/kasada Example JSON payloads for the solve and request operations. The solve operation fetches the URL and solves the interrogation, while request sends the request with the token. ```json { "op": "solve", "params": { "url": "https://www.example.com/buy" } } ``` ```json { "op": "request", "params": { "url": "https://www.example.com/buy" } } ``` -------------------------------- ### connect() binary resolution Source: https://proofofbots.github.io/web-re-toolkit/packages/node Describes how connect() selects the binary to use, including the binary option, resolveBinary() fallback, and the WRE_BINARY environment variable. ```APIDOC ## connect() binary resolution ### Description `connect()` uses the `binary` option if given, otherwise falls back to `resolveBinary()`, which checks `WRE_BINARY` first. Set `WRE_BINARY` to an absolute path to skip the package’s shipped or downloaded binary. That path is used as-is, with no hash check. ### Method N/A (library function) ### Endpoint N/A ### Parameters #### Options - **binary** (string) - Optional - Absolute path to a binary to use. #### Environment Variables - **WRE_BINARY** (string) - Optional - Absolute path to a binary; used as-is with no hash check. ### Request Example N/A ### Response N/A ### Error Handling N/A ``` -------------------------------- ### Generate and use the Akamai Rust SDK by path Source: https://proofofbots.github.io/web-re-toolkit/packages/akamai/rust Generates the Rust client package (command), declares a path dependency on it in Cargo.toml, then uses the generated Akamai SDK to open a client, solve a challenge, and print the resulting cookies. ```bash wre client package --bundle default ``` ```toml [dependencies] wre-sdk-akamai = { path = "dist/default/packages/rust/akamai" } ``` ```rust use wre_sdk_akamai::{AkamaiConfig, Client, OpenOptions, SolveInput}; let config = AkamaiConfig { page_url: Some("https://acme.example/".into()), ..Default::default() }; let client = Client::open(&config, OpenOptions::default())?; let solved = client.solve(&SolveInput::default())?; println!("{:?}", solved.cookies); client.close()?; ``` -------------------------------- ### Describe the Akamai client Source: https://proofofbots.github.io/web-re-toolkit/guides/akamai Displays the client's capabilities and usage information. Run this command to see the available operations and parameters. ```bash wre client describe akamai ``` -------------------------------- ### Open a Kasada client and solve an interrogation Source: https://proofofbots.github.io/web-re-toolkit/packages/kasada/rust Example of opening a Kasada client with a page URL and solving the interrogation. The token is bound to the KP_UIDz cookie, so solve against the actual URL you want. ```rust use wre_sdk_kasada::{Client, KasadaConfig, OpenOptions, SolveInput}; let config = KasadaConfig { page_url: Some("https://acme.example/buy".into()), ..Default::default() }; let client = Client::open(&config, OpenOptions::default())?; let solved = client.solve(&SolveInput::default())?; println!("{} {}", solved.verdict, solved.payload_bytes); client.close()?; ``` -------------------------------- ### Solve and request operations Source: https://proofofbots.github.io/web-re-toolkit/guides/akamai Example operations: solve warms up a session for the given URL, and request sends a request with the session's cookies. The request op can attach a fresh akamai-bm-telemetry header when telemetry is true. ```json { "op": "solve", "params": { "url": "https://login.example.com/" } } ``` ```json { "op": "request", "params": { "url": "https://api.example.com/orders", "telemetry": true } } ``` -------------------------------- ### Use the Akamai client to solve a challenge Source: https://proofofbots.github.io/web-re-toolkit/packages/go Opens a client, solves a challenge, and prints the cookies. Each operation is a method with typed input and result structs. ```go ctx := context.Background() page := "https://acme.example/" client, err := clientakamai.Open(ctx, &clientakamai.AkamaiConfig{PageURL: &page}, clientakamai.OpenOptions{}) if err != nil { log.Fatal(err) } defer client.Close(ctx) solved, err := client.Solve(ctx, clientakamai.SolveInput{}) if err != nil { log.Fatal(err) } fmt.Println(string(solved.Cookies)) ``` -------------------------------- ### Full run: warm session, discover, solve, and login Source: https://proofofbots.github.io/web-re-toolkit/packages/node Complete workflow: warm a session against a protected login page, read the antiforgery token, and post a form through the same cookie jar. Includes helper to extract form fields from HTML. `discover` reports protection status without running the sensor; `page` returns the loaded document and its inputs; `refused` is true on 403/429/access denied/challenge redirect. ```javascript import { open } from "@proofofbot/client-akamai"; const PAGE = "https://login.xero.com/identity/user/login"; const PRECHECK = "https://login.xero.com/identity/user/login/pre-check"; const field = (html, name) => { const at = html.indexOf(`name="${name}"`); if (at < 0) return null; const rest = html.slice(at); const start = rest.indexOf('value="'); if (start < 0) return null; const tail = rest.slice(start + 7); const end = tail.indexOf('"'); return end < 0 ? null : tail.slice(0, end); }; const client = await open({ page_url: PAGE, wait_ms: 100, rounds: 1 }); try { const found = await client.discover({}); console.log("discover:", { status: found.status, protected: found.protected }); const solved = await client.solve({}); console.log("solve:", { payload_bytes: solved.payload?.length ?? 0, posts: solved.posts, }); const page = await client.page(); const html = page.html || (await client.request({ url: PAGE })).body; const token = page.fields?.__RequestVerificationToken ?? field(html, "__RequestVerificationToken"); const returnUrl = page.fields?.ReturnUrl ?? field(html, "ReturnUrl") ?? ""; if (!token) throw new Error("no antiforgery token"); const username = `nx${Date.now().toString(16)}@example.com`; await client.request({ url: PRECHECK, method: "POST", json: { Username: username }, headers: { accept: "application/json, text/plain, */*", origin: "https://login.xero.com", requestverificationtoken: token, }, }); const answer = await client.request({ url: PAGE, method: "POST", form: { ReturnUrl: returnUrl, PreCheckCompleted: "true", Username: username, Password: "Nx7!aQ2zR9kL", __RequestVerificationToken: token, }, headers: { accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", origin: "https://login.xero.com", "sec-fetch-dest": "document", "sec-fetch-mode": "navigate", "sec-fetch-site": "same-origin", "upgrade-insecure-requests": "1", }, }); const body = (answer.body ?? "").toLowerCase(); console.log("login:", { status: answer.status, refused: answer.refused, credential_error: body.includes("email address or password") || body.includes("incorrect"), }); } finally { await client.close(); } ``` -------------------------------- ### Event envelope: {"t":"evt","event":"progress",...} Source: https://proofofbots.github.io/web-re-toolkit/reference/protocol Event envelope sent from host to consumer, zero or more before the response, carrying the request's id. Example shows a progress event with done, total, and note fields. ```json {"t":"evt","v":1,"id":7,"event":"progress","data":{"done":2,"total":3,"note":"sealing"}} ``` -------------------------------- ### Describe the Kasada client Source: https://proofofbots.github.io/web-re-toolkit/guides/kasada Command to display information about the Kasada client using the wre CLI. ```bash wre client describe kasada ``` -------------------------------- ### Cancel envelope: {"t":"cancel","v":1,"id":7} Source: https://proofofbots.github.io/web-re-toolkit/reference/protocol Cancel envelope sent from consumer to host. A cancelled request still gets a response, usually with kind "cancelled". Cancellation is cooperative; a cancel for an unknown id is ignored. ```json {"t":"cancel","v":1,"id":7} ``` -------------------------------- ### Lift virtual machine with wre vm Source: https://proofofbots.github.io/web-re-toolkit/start/first-pass Discovers the virtual machine, probes each handler against Proxy sentinels to record reads, writes, and program counter touches, then lifts the VM to a new file. Only the frame model is per-target. ```bash wre vm discover collect.js wre vm probe collect.js --table HANDLERS --frame frame.js wre vm lift program.json --out lifted.js ``` -------------------------------- ### HIS payload structure Source: https://proofofbots.github.io/web-re-toolkit/research/altcha Example payload structure for the human interaction signature (HIS) sent to the challenge URL. The collector listens for focusin, keydown, pointerdown, pointermove, scroll, and touchmove events, keeping at most 60 samples per buffer with a 50 ms floor. Coordinates are integers, timestamps are in milliseconds since page load, and arrays are capped. ```json { "focus": [[elapsedMs, tabIndex, tagCode, hadInteraction]], "maxTouchPoints": 0, "pointer": [[x, y, t]], "scroll": [[y, t]], "time": 1700000000000, "touch": [[x, y, t, force, radiusX, radiusY]] } ``` -------------------------------- ### Define a bundle in clients.toml Source: https://proofofbots.github.io/web-re-toolkit/guides/clients Map targets and platforms to a binary in the workspace root's clients.toml. A static V8 build is 30 to 50 MB; a bundle whose targets all declare needs_v8 = false can be built without the V8 feature. ```toml [bundle.default] targets = ["example"] platforms = ["aarch64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"] ``` -------------------------------- ### Package output layout for wre client package Source: https://proofofbots.github.io/web-re-toolkit/guides/clients Output layout written by `wre client package` for the default bundle. It reads dist//bin, hashes every binary, asks the host binary for its descriptor, and writes these package files. Every generated package pins the schema hash and refuses to run against a binary that reports a different one. ```text dist/default/packages/ node/example/ index.js, index.d.ts, package.json, platform//bin/wred python/example/ wre_client_example/, setup.py, build_wheels.sh, binaries//wred go/example/ client.go, types.go, meta.go, go.mod rust/example/ src/lib.rs, Cargo.toml ``` -------------------------------- ### Compare whole builds with wre builds Source: https://proofofbots.github.io/web-re-toolkit/guides/identification Pairs functions by exact normalised hash first, then greedily by structural similarity, using each function at most once. Leftover functions are reported as gone or new. ```bash wre builds collect-old.js collect-new.js ``` -------------------------------- ### Verify capture with wre verify Source: https://proofofbots.github.io/web-re-toolkit/start/first-pass Runs every check the manifest declares that needs no browser. ```bash wre verify --target acme --capture captures/acme-2026-08-15 ``` -------------------------------- ### Build the toolkit with cargo Source: https://proofofbots.github.io/web-re-toolkit/start/install Clones the repository and builds the release binary. Requires Rust stable with cargo on PATH and network access for the first build. ```bash git clone https://github.com/proofofbots/web-re-toolkit cd web-re-toolkit cargo build --release ``` -------------------------------- ### wred --describe output: client descriptor JSON Source: https://proofofbots.github.io/web-re-toolkit/reference/protocol Output of `wred --describe` for the default bundle. Shows the protocol version, toolkit and binary versions, and a client descriptor with capabilities, config, ops, events, and types. `capabilities` indicates host requirements before the client can run; `needs_chrome` is checked by `health` rather than the first call that wants a browser. ```json { "protocol": 1, "bundle": "default", "toolkit_version": "0.1.0", "binary_version": "0.1.0", "clients": [ { "id": "example", "version": "0.1.0", "summary": "...", "capabilities": { "needs_v8": true, "needs_chrome": false, "needs_network": true, "stateful": true, "concurrency": "per_session", "warmup_ms": 150 }, "config": { "kind": "object", "name": "ExampleConfig", "fields": [...] }, "ops": [ { "name": "solve", "summary": "...", "params": { "kind": "ref", "name": "Facts" }, "returns": { "kind": "object", "name": "Solved", "fields": [...] }, "streams": ["progress"], "deadline_ms": 20000 } ], "events": [ { "name": "progress", "data": {...} } ], "types": { "Facts": {...}, "Solved": {...} } } ] } ``` -------------------------------- ### Open, solve, and request with AkamaiClient Source: https://proofofbots.github.io/web-re-toolkit/packages/akamai/node Basic usage: open a client, solve the challenge, make a request, and close. One client owns one session, which owns the mounted realm and the cookie jar; open it once and reuse it. Opening one per call pays the warmup cost every time and throws away the `_abck` cookie the previous run earned. ```javascript import { AkamaiClient } from "@proofofbot/client-akamai"; const client = await AkamaiClient.open({ page_url: "https://acme.example/" }); const solved = await client.solve({}); console.log(solved.cookies); const answered = await client.request({ url: "https://acme.example/api/checkout", method: "POST", json: { sku: "A-1" }, telemetry: true, }); console.log(answered.status, answered.refused); await client.close(); ``` -------------------------------- ### Check manifest and list targets Source: https://proofofbots.github.io/web-re-toolkit/start/workspace Checks a manifest without running anything and lists targets. `wre check` validates the manifest for the given target, and `wre targets` lists all targets. ```bash wre check acme wre targets ``` -------------------------------- ### Connect to a sidecar and call solve Source: https://proofofbots.github.io/web-re-toolkit/packages/go Connects to a sidecar binary, opens a session, and calls the 'solve' method. Handles blocked errors with wre.IsKind. ```go ctx := context.Background() sc, err := wre.Connect(ctx, wre.Options{ Binary: "/path/to/wred", ExpectSchemaHash: "0f3a9c2b1d4e5f60", }) if err != nil { log.Fatal(err) } defer sc.Close() sess, err := sc.Open(ctx, "example", map[string]any{"headless": true}) if err != nil { log.Fatal(err) } defer sess.Close(ctx) var solved struct { Body string `json:"body"` } err = sess.Call(ctx, "solve", map[string]any{"url": "https://acme.example/"}, &solved) if err != nil { if wre.IsKind(err, wre.KindBlocked) { log.Println("challenged, retry later") } log.Fatal(err) } fmt.Println(solved.Body) ``` -------------------------------- ### Open a graph profile and run a script Source: https://proofofbots.github.io/web-re-toolkit/guides/sandbox Opens a graph profile and runs a script in a graph realm. The graph replays only what the capture reached; frames are real V8 contexts and scripts run under their own URL. ```rust let profile = GraphProfile::read("profiles/graph/macbook-chrome.json")?; let page = GraphPage { url: url.to_string(), frames: 4, ..GraphPage::default() }; let mut graph = wre_sandbox::graph::open(&profile, &page, hooks, RealmOptions::default())?; graph.run(&script, "https://example.com/agent.js", false)?; graph.step()?; ``` -------------------------------- ### Full run: warm session, read token, post form Source: https://proofofbots.github.io/web-re-toolkit/packages/python Warm a session against a protected login page, read the antiforgery token out of the page the session already loaded, and post a form through the same jar. discover reports the surface without running the sensor, so it is the cheapest way to tell whether a page is protected. page returns the document the session last loaded along with every input it declares, which saves a second fetch. refused is true on a 403, a 429, an access denied body or a challenge redirect, so a False there with a credential error in the body means the session passed and the login itself was rejected. ```python import time from typing import Optional from wre_client_akamai import open_client PAGE = "https://login.xero.com/identity/user/login" PRECHECK = "https://login.xero.com/identity/user/login/pre-check" def field(html: str, name: str) -> Optional[str]: at = html.find(f'name="{name}"') if at < 0: return None rest = html[at:] start = rest.find('value="') if start < 0: return None tail = rest[start + 7 :] end = tail.find('"') return None if end < 0 else tail[:end] with open_client({"page_url": PAGE, "wait_ms": 100, "rounds": 1}) as client: found = client.discover({}) print("discover:", {"status": found["status"], "protected": found["protected"]}) solved = client.solve({}) print("solve:", { "payload_bytes": len(solved.get("payload") or ""), "posts": solved["posts"], }) page = client.page() html = page["html"] or client.request({"url": PAGE})["body"] fields = page["fields"] token = fields.get("__RequestVerificationToken") or field(html, "__RequestVerificationToken") return_url = fields.get("ReturnUrl") or field(html, "ReturnUrl") or "" if not token: raise RuntimeError("no antiforgery token") username = f"nx{int(time.time()):x}@example.com" client.request({ "url": PRECHECK, "method": "POST", "json": {"Username": username}, "headers": { "accept": "application/json, text/plain, */*", "origin": "https://login.xero.com", "requestverificationtoken": token, }, }) answer = client.request({ "url": PAGE, "method": "POST", "form": { "ReturnUrl": return_url, "PreCheckCompleted": "true", "Username": username, "Password": "Nx7!aQ2zR9kL", "__RequestVerificationToken": token, }, "headers": { "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "origin": "https://login.xero.com", "sec-fetch-dest": "document", "sec-fetch-mode": "navigate", "sec-fetch-site": "same-origin", "upgrade-insecure-requests": "1", }, }) body = (answer["body"] or "").lower() print("login:", { "status": answer["status"], "refused": answer["refused"], "credential_error": "email address or password" in body or "incorrect" in body, }) ``` -------------------------------- ### Loader stamping and request Source: https://proofofbots.github.io/web-re-toolkit/guides/kasada Mount the site's own loader and let it stamp requests. The loader builds all x-kpsdk-* headers, including proof of work, and a rebuild cannot drift from it. ```json { "op": "loader", "params": {} } { "op": "request", "params": { "url": "https://www.example.com/api/search", "stamped": true } } ``` -------------------------------- ### Capture with automatic Chrome launch Source: https://proofofbots.github.io/web-re-toolkit/guides/sandbox Launches Chrome through wre-cdp, opens the capture page automatically, and stores the resulting profile. The --label flag sets a descriptive label for the device. ```bash wre sandbox capture --open --label "MacBook Pro M3, Chrome 151" ``` -------------------------------- ### Lock a build with wre --lock Source: https://proofofbots.github.io/web-re-toolkit/guides/identification Writes the resolved build to a lock file, recording each role's binding, structural hashes, a bottom-k signature of its 3-grams, and the evidence. The manifest holds the intent, the lock holds the answer for one build, and they are separate on purpose. ```bash wre locate collect.js --target acme --lock targets/acme.lock ```