Attack Mode
Attack Mode is Temps's built-in proof-of-work challenge for bot traffic and application-layer (L7) request floods. When enabled, every visitor completes a browser-solved challenge before reaching your app — no third-party CAPTCHA services, no external dependencies, everything runs on your infrastructure. It does not absorb volumetric network DDoS — put a CDN or your provider's DDoS protection in front for that.
The other protections the Temps proxy applies, security headers, the IP access list and vulnerability scanning, are on Security Settings. For the full security overview, see Security. Guide: Replace reCAPTCHA with a proof-of-work CAPTCHA explains the technique behind the challenge.
Overview
Attack Mode is Temps's self-hosted alternative to Cloudflare's "Under Attack Mode." When enabled, every visitor must complete a proof-of-work (PoW) challenge before accessing your application. The challenge is solved automatically in the visitor's browser using a WebAssembly solver -- no user interaction required.
Key Features:
- Zero dependencies -- no third-party CAPTCHA services or API keys
- Automatic solving -- visitors see a brief checkpoint page while their browser solves the challenge (2-5 seconds)
- 24-hour sessions -- once verified, visitors can browse freely for 24 hours
- TLS fingerprinting -- sessions are tied to JA4 TLS fingerprints for stronger identity binding
- WASM-powered -- Rust compiled to WebAssembly for 30-50x faster solving than JavaScript
- Per-project and per-environment control -- enable or disable per project or per environment from the dashboard, CLI, or API
When to use Attack Mode:
- Your application is getting an application-layer request flood
- You're seeing suspicious bot traffic patterns
- You want to protect a public-facing app during a critical event
- You need immediate protection without external service setup
When not to use it:
- For API endpoints consumed by other services (they can't solve PoW challenges)
- Permanently on production -- it adds latency for first-time visitors
- For applications that need to be crawled by search engines
How it works
When a visitor requests a page on a project with Attack Mode enabled, the Temps proxy intercepts the request (see Request flow for where this check sits) and runs through the following flow:
Challenge Flow
- Request arrives at the Pingora-based reverse proxy
- Session check -- the proxy looks up the visitor's JA4 TLS fingerprint (or IP address as fallback) in the
challenge_sessionstable - If valid session exists -- the request passes through to your application normally
- If no valid session -- the proxy returns a 403 response with an HTML challenge page
- Browser solves challenge -- the embedded WASM solver finds a nonce such that
SHA-256(challenge + nonce)has at least 20 leading zero bits (~1 million hash attempts, takes 2-5 seconds) - Solution submitted -- the browser POSTs the solution to
/api/_temps/captcha/verify - Server verifies -- the proxy recomputes the hash and checks the leading zero bits
- Session created -- a 24-hour session is stored in the database, keyed to the visitor's TLS fingerprint
- Page loads -- the browser redirects to the original URL
Attack Mode requires HTTPS. HTTP requests return a 426 Upgrade Required response because JA4 TLS fingerprinting -- used to identify visitors across requests -- is only available on TLS connections.
Proof-of-Work Details
The challenge uses SHA-256 hashing with a difficulty of 20 leading zero bits, which requires approximately 1 million hash attempts on average. The WASM solver processes this in 2-5 seconds on modern hardware.
- Name
Algorithm- Type
- SHA-256
- Description
The solver concatenates the challenge string with a nonce and computes
SHA-256(challenge + nonce). It increments the nonce until the hash has the required number of leading zero bits.
- Name
Difficulty- Type
- 20 bits
- Description
20 leading zero bits means the hash must start with at least 5 hex zeros (e.g.,
00000a3f...). This takes ~1M attempts on average -- enough to deter bots while remaining fast for browsers.
- Name
WASM Solver- Type
- Rust → WebAssembly
- Description
The solver is written in Rust and compiled to WebAssembly via
wasm-bindgen. It uses the optimizedsha2crate and is 30-50x faster than the JavaScript fallback. Progress callbacks update the UI every 10,000 iterations.
- Name
Session Duration- Type
- 24 hours
- Description
Once a challenge is solved, the session is valid for 24 hours. Sessions are keyed to the visitor's JA4 TLS fingerprint (or IP address if fingerprinting is unavailable). Disabling attack mode does not delete existing sessions: they stay valid until their 24 hours run out, so a visitor who already passed is not challenged again if you re-enable it within that window.
- Name
JavaScript Fallback- Description
If the WASM module fails to load (older browsers, disabled WebAssembly), the challenge page falls back to a pure JavaScript solver. It's slower but functional.
Enabling attack mode
Enable Attack Mode (project level)
- 1
Go to Projects and select your project.
- 2
Open Settings then Security.
- 3
Toggle Enable Attack Mode in the Attack Mode card.
- 4
Click Save Attack Mode Settings.
Checkpoint: Confirm the orange "Attack Mode is enabled for this project" banner appears on the project detail page.
Enable Attack Mode (environment level)
- 1
Go to Projects and select your project.
- 2
Open Settings then Environments.
- 3
Select the environment (for example production).
- 4
Open the Security card.
- 5
Toggle Attack Mode and click Save.
Checkpoint: Confirm the environment detail page shows Attack Mode as enabled for that environment only.
Attack Mode can be enabled from the dashboard or API at both the project and environment level. Via CLI, only project-wide Attack Mode is available today (projects settings -p my-app --attack-mode) — there is no per-environment CLI subcommand yet.
Via Dashboard
- Go to Projects and select your project
- Open Settings → Security
- Toggle Enable Attack Mode in the Attack Mode card
- Click Save Attack Mode Settings
When enabled, an orange banner appears on the project detail page:
Attack Mode is enabled for this project -- with a quick-access Disable button to turn it off immediately.
Via CLI
CLI Commands
# Enable attack mode on a project (all environments inherit this)
bunx @temps-sdk/cli projects settings -p my-app --attack-mode
The environments CLI command group has no settings subcommand and no
--attack-mode flag today — per-environment Attack Mode can only be toggled
from the dashboard or the Via API section below. Only project-wide Attack
Mode is available via CLI, with projects settings -p my-app --attack-mode
/ --no-attack-mode.
Via API
API Examples
# Enable attack mode at the project level
curl -X POST "https://your-temps-instance.com/api/projects/{project_id}/settings" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"attack_mode": true}'
# Disable attack mode at the project level
curl -X POST "https://your-temps-instance.com/api/projects/{project_id}/settings" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"attack_mode": false}'
Per-Environment Override
A common use case is keeping attack mode on for production while off for staging: your public traffic gets the PoW challenge, but internal CI runners and QA engineers are not blocked by it. See Environments for how environments are set up.
- Go to Project Settings → Environments
- Select the environment (production, staging, preview)
- Open the Security card
- Toggle Attack Mode for that specific environment
Environment-level settings override project-level settings -- they are not additive. If attack mode is enabled at the project level but explicitly disabled on staging, staging traffic bypasses the PoW challenge entirely. Likewise, if it is disabled at the project level but enabled on production, only production visitors must solve the challenge.
Configuration inheritance
Attack mode is read at two levels, project and environment. An environment-level value always wins over the project-level value (not additive); an environment with no value of its own inherits the project's. Security headers and the IP access list resolve differently; see Security Settings.