On-prem
xhr.dev is on-prem only. You run the solver as a container on your own infrastructure, and it talks directly to the target sites and proxies you configure — never through infrastructure xhr.dev operates. Your traffic, cookies, and target-site data stay inside your network.
There was once a hosted proxy. There isn't now, and the only solver we run is the trial box we lend to prospects during an evaluation.
Network posture
The container is designed to run on a box with no general internet access. That isn't only a deployment recommendation — the application enforces it on itself.
The runtime cannot open arbitrary connections
The launcher starts Node under Node's permission model, with an explicit network allowlist:
node --permission \
--allow-net=0.0.0.0:3000,127.0.0.1:8080 \
--allow-fs-read=<payload dir> \
--allow-child-process \
--allow-worker \
--input-type=module ---allow-net is the whole outbound story. The process may:
- listen on
0.0.0.0:3000— the API you call, and - connect to
127.0.0.1:8080— the local proxy that carries solver requests to the target site.
Any other destination is refused by the runtime, not by convention. There is no code path that reaches xhr.dev, a licence server, a telemetry endpoint, or an analytics host, because the process is not permitted to open the socket in the first place — a compromised dependency couldn't either.
Verify it yourself
The flags are visible from inside the container, so you don't have to take our word for it:
docker exec xhrdev sh -c 'for pid in /proc/[0-9]*/; do
cmd=$(cat ${pid}cmdline 2>/dev/null | tr "\0" " ")
case "$cmd" in *node*) echo "$pid: $cmd";; esac
done'/proc/7/: node --permission --allow-net=0.0.0.0:3000,127.0.0.1:8080 …Worth capturing for a security review — it's a stronger artifact than a policy document, because it's the actual argv of the running process.
The sandbox has no network at all
One layer further in: the anti-bot vendor's obfuscated challenge script is untrusted code, and it runs in a sandbox whose network surfaces are patched rather than real. XMLHttpRequest, WebRTC, and Workers are reimplemented and answered in-process, so the challenge script cannot open a socket even in principle — its "requests" never leave the process, let alone the box.
What that means for your firewall
Lock the box down. The container needs:
| Direction | Destination | Why |
|---|---|---|
| inbound | port 3000, from your scrapers only | the API |
| outbound | your proxy | fetching the challenge and submitting to the target |
Nothing else — no DNS to xhr.dev, no registry access at runtime, no NTP dependency unless you've opted into time-source hardening. Egress restricted to your proxy pool is a supported, tested configuration.
The network is the only access control
There is no per-request authentication and no API key — the licence gates startup, not requests. Anything that can reach port 3000 can use the solver. That's the trade for having no auth to misconfigure, and it means reachability is your whole access-control story. See Security.
Licence, not phone-home
A signed licence file unlocks the container at startup: it verifies the signature over your licence and checks the issued_at / expires_at window, entirely locally. That's what makes the --allow-net allowlist above possible at all — there's no licence server to call.
If the licence is missing, expired, or tampered with, the container exits immediately with {"event":"launcher_failed"} and a non-zero code. It fails closed, not open.
See Deployment for delivery and renewal.
What it solves
- Akamai Bot Manager — sensor challenges (
_abck/bm-sz) and SBSD. Direct HTTP solve, or a streaming WebSocket session for browser-automation setups. API reference - DataDome — captcha and interstitial challenges, via a single-shot HTTP call. API reference
Don't see what you need? Message us.
The hosted trial box
The one exception to all of the above. During an evaluation we'll point you at a solver we run, so you can try the API before deploying anything. It sits behind a reverse proxy that requires an x-api-key header on /akamai/* and /dd/* (/hc stays open), and the key is issued with the trial and revoked when it ends.
That key exists because the box is on the public internet. It has no equivalent in a self-hosted deployment — your own container has no API key and ignores the header. It's also the only configuration in which any of your traffic touches a machine we operate; the moment you deploy your own container, that stops.
The OpenAPI spec carries both: the trial server with ApiKeyAuth, and your own container with no security requirement.
Next
- Deployment — pulling the image and running it
- Security — the full trust model
- How to integrate