What is Arcjet?
Arcjet is the runtime security platform that ships in your AI code. It detects prompt injection, authorizes agent tool calls, redacts sensitive data, and blocks bots and abuse: real-time security building blocks you call inside your application, before an action happens.
That last clause carries the design. Arcjet is a library you install and call from your own code. There is no proxy to route traffic through and no DNS change to make. You import an SDK, declare rules, and call a function at the point where a decision matters, which is also the point where your application knows the most. It knows who the user is, what the agent is about to do, which record is involved, and what it costs if that goes wrong.
That call comes in two shapes, and the difference runs through everything else on this page:
protect()takes an HTTP request. Use it in route handlers and API endpoints, in any supported framework.guard()takes inputs directly, with noRequestobject. Use it inside tool handlers, MCP servers, queue consumers, and agent pipelines, where the consequential action has no HTTP front door.
A third function, capture(), records that an allowed action happened. It never changes a decision.
Think of it as the evidence primitive: a record of what an agent did, sitting next to the decisions
that permitted it.
Where this is going
This is worth stating plainly, because it explains why the API is shaped the way it is. Arcjet is
moving from point checks to workflow-level runtime policy: connecting protect() and guard()
decisions, together with audit events you supply, into one security trace, then enforcing before the
next risky production action executes. The thesis is an application-native security enforcement
layer for AI workflows, connecting enterprise identity to centrally managed policy, evaluating
context before consequential actions, and producing evidence.
The two speeds of AI agent runtime security sets out the reasoning, and it's explicit about the split. It borrows Google's terms from Beyond Zero. The floor is consistent baseline policy and enforcement, applied the same way across teams. The ceiling is where a decision draws on identity, resource sensitivity, recent behavior, and the sequence of actions that led to a request: "Google is describing the ceiling. Arcjet is making the floor deployable while building up from the foundations to reach the ceiling."
Everything documented on this page is the floor, and it ships today. Read the trace-level policy as direction rather than as something you can configure now. It matters for a build-versus-buy decision because the enforcement points are the durable part of the investment, and because the argument for putting them in your application doesn't change as the policy above them gets richer: "the reasoning only matters if there is somewhere to enforce its decision."
Not every rule is available on both surfaces, and the split isn't arbitrary. Rules that need a
parsed request body work on both. Rules that need HTTP itself, such as bot detection and Shield,
are protect() only. Content moderation and custom rules are guard() only. The
agent get started guide publishes the full matrix.
The rest of this page walks every feature, then asks the question a technical evaluator asks sooner or later: which of these would you be better off building yourself?
Where does each check run?
This is the first thing to understand, because it determines your latency, your privacy posture, and which compliance questions you have to answer.
Arcjet has two components: an SDK in your application, which includes a WebAssembly module for
local analysis, and a Cloud API. The architecture documentation
is specific about when the network call happens.
A Cloud API call is required when you configure Shield, rate limiting, bot protection where local
header analysis is inconclusive, email validation of a syntactically valid address, or a filter
that reads ip.src.* fields backed by the IP reputation database.
Two properties matter for capacity planning. Arcjet makes a single API call per request regardless of how many rules you configure. And deny decisions are cached locally for a TTL, so a blocked client doesn't cost a round trip on every subsequent request.
The published latency figures:
| Path | Documented overhead |
|---|---|
| Local analysis or a cached decision | "less than 1ms" |
| Cloud API call | "typically no more than 20-30ms, often significantly less" |
| Prompt injection detection | "approximately 100 ms" |
| Local sensitive information inference | "~6.6 ms" median, "~3.9 ms" on WebGPU |
The Cloud API runs in "over 300 global locations" and routes to the closest region, per the regions documentation. The default request timeout is 500 ms in production and 1000 ms in development.
We've written up how that budget is actually spent in how we achieve our 25ms p95 response time SLA, including why decisions are cached per rule type and why the transport uses HTTP/2 multiplexing. If you want the full component inventory behind the service, Arcjet's tech stack names about thirty production systems, which is itself a useful input to the build-versus-buy question.
Arcjet fails open by default. The architecture page states the reasoning plainly: a service
issue or a misconfiguration shouldn't block all of your traffic. You can configure it to fail
closed instead, and for guard() the framework wrappers already default the other way. Decide
this per action rather than globally, because a product search page and a payment endpoint don't
warrant the same answer.
One more thing runs whether you configure it or not. Arcjet performs post-request analysis on the platform, and once a client crosses a dynamic threshold of suspicious activity it gets a deny decision for a period. That's automatic and needs no configuration.
For a candid account of what stays in your process and what doesn't, see keeping security inspection local and the privacy documentation, which lists exactly which request data is processed remotely.
Shield WAF
Shield is Arcjet's web application firewall. It watches request metadata over time and blocks clients whose behavior crosses a threshold, rather than trying to adjudicate each request in isolation. It carries rules from the OWASP Core Rule Set covering SQL injection, cross-site scripting, local file inclusion, remote file inclusion, and PHP and Java code injection.
The API is one option:
import arcjet, { shield } from "@arcjet/next";
const aj = arcjet({ key: process.env.ARCJET_KEY!, rules: [shield({ mode: "LIVE" })],});Check decision.reason.isShield() when a request is denied. Shield is available as a remote rule,
so a security team can enable it from the dashboard or the MCP server without a deploy.
One limit matters here. Shield analysis uses request headers and query parameters, not the request body, and it happens on the Arcjet platform after the request is reported. Our limitations page records it as one of two entries. If you want body inspection, that's sensitive information detection, which works differently and stays local.
Rate limiting
Arcjet ships three algorithms, documented with their trade-offs in the algorithms guide: fixed window, sliding window, and token bucket. For the mechanics of each and when to choose which, see our rate limiting guide.
import arcjet, { fixedWindow, slidingWindow, tokenBucket } from "@arcjet/next";
const aj = arcjet({ key: process.env.ARCJET_KEY!, characteristics: ["userId"], rules: [ fixedWindow({ mode: "LIVE", window: "1h", max: 100 }), slidingWindow({ mode: "LIVE", interval: 60, max: 20 }), tokenBucket({ mode: "LIVE", refillRate: 10, interval: 60, capacity: 100 }), ],});Token bucket consumes a variable amount per call, which is what makes it the right primitive for
cost-weighted work: await aj.protect(req, { userId, requested: 50 }).
Two details decide whether your limits behave the way you intended. First, characteristics
combine into one fingerprint. ["ip.src", "userId"] gives you one bucket per unique IP and user
pair, not separate IP and user counters. If you want independent ceilings, write independent
rules. Second, don't key on anything the caller controls, or the limit isn't a limit. The
fingerprints documentation also warns against using
personal information as a characteristic, even though values are hashed.
State lives in the Cloud API, so limits hold across every instance of your application without you running Redis. Fixed and sliding window are available as remote rules. Token bucket isn't, because the SDK has to declare how many tokens each request spends.
The @arcjet/decorate package will add draft-standard RateLimit headers to your responses from
a decision.
Bot protection
Bot protection classifies automated clients by name and
by category, and you write policy against either. We document detection of more
than 600 bots across categories such as CATEGORY:AI, CATEGORY:SEARCH_ENGINE, and
CATEGORY:MONITOR.
detectBot({ mode: "LIVE", allow: ["CATEGORY:SEARCH_ENGINE", "CATEGORY:MONITOR"],});The semantics are worth stating precisely, because getting them backwards is a common mistake.
With allow, anything detected and not on the list is denied. With deny, anything detected and
not on the list is allowed. A bot that's detected but unidentifiable becomes UNKNOWN_BOT, and
if you use an allow list without including it, those requests are blocked. That's the default,
and it's usually what you want.
For allow rules, Arcjet verifies authenticity using IP data and reverse DNS, so a client claiming
to be Googlebot has to actually be Googlebot. Deny rules skip verification, since there's nothing
to gain by verifying a client you're blocking anyway. The @arcjet/inspect package exposes
isSpoofedBot, isVerifiedBot, and isMissingUserAgent.
The detection data is open source. arcjet/well-known-bots is MIT licensed and carries 635 entries, each with an ID, categories, a match pattern, and a verification field. You can read exactly how a given bot is identified, which is what makes it checkable if you're evaluating. It's a hard fork of crawler-user-agents, and the upstream copyright is preserved in the license.
Worth knowing from that dataset: the verification field is present on all 635 entries but populated on only 65 of them. The rest cannot be confirmed by reverse DNS or published IP range, because their operators don't publish one. That's a limit of the problem, not of the dataset.
You don't have to take our word for its usefulness, which is the point of publishing it. Fastly's log analytics tool reads the same JSON file as its bot feed, and a 2026 UCLouvain paper on detecting robots from server logs cites it as one of the canonical crowdsourced bot-identification lists.
For how the matching itself is implemented, making Arcjet's Wasm bot detector smaller and
faster documents
replacing a single large RegexSet with an Aho-Corasick automaton plus a small residual regex set,
taking the component from about 944 KB gzipped to about 689 KB and detection from roughly
0.9 ms to 0.4 ms. It also explains why a fresh instance is created per request rather than
reused, which is a memory-isolation decision rather than a performance one.
Advanced bot signals adds a browser-side layer, documented at
advanced signals. A WebAssembly module
in the page collects environment signals, exchanges them for a continue token stored in the
aj_signals cookie, and your server-side detectBot rule reads that cookie on the next request.
A missing cookie is itself a signal, and you can enforce its presence with a filter. Billing keys
off the detectBot call, not the script load. The page lists the Content-Security-Policy entries
you'll need.
That limit is in our own documentation: "no bot detection system can be 100% accurate." For the AI-agent-specific version of this problem, see AI agent bot management.
Email validation
Email validation runs in two stages, and the split matters for privacy. Syntax validation happens locally in the SDK. Only a syntactically valid address goes to the Cloud API, which checks MX records, whether the domain is disposable or free, and whether a Gravatar exists.
validateEmail({ mode: "LIVE", deny: ["DISPOSABLE", "INVALID", "NO_MX_RECORDS"],});Then pass the address at call time: await aj.protect(req, { email }).
Two options change the strictness. requireTopLevelDomain defaults to true, so foo@bar is
rejected; set it to false and it isn't. allowDomainLiteral defaults to false, so
foo@[123.456.789.0] is rejected.
The local syntax check is implemented on the open source email_address crate, which Arcjet
forks publicly, and our reference page lists the ten
RFCs it accounts for. That's a real answer to a question most teams underestimate, which the
build section returns to.
Email validation isn't available as a remote rule, because the address comes from the request body.
Sensitive information detection
This is the feature where Arcjet's architecture is most visible. Sensitive information detection inspects request bodies for personally identifiable information (PII), and our docs commit to where that happens: "All of this logic runs locally and in-process. The raw request body is never sent to Arcjet." Arcjet receives the decision, not the content.
sensitiveInfo({ mode: "LIVE", deny: ["CREDIT_CARD_NUMBER", "EMAIL", "PHONE_NUMBER"],});The built-in engine covers four structured types: email addresses, phone numbers, IP addresses,
and credit card numbers. The matching rules are documented rather than left to inference. Card
numbers must pass the Luhn check, so 4242424242424242 matches and 4242424242424241 doesn't.
IP addresses must parse as an IpAddr. Short numbers like 911 aren't treated as phone numbers.
For names, addresses, and government or financial identifiers you add an optional on-device model:
import { rampart, rampartEntities } from "@arcjet/sensitive-info-rampart";
sensitiveInfo({ mode: "LIVE", deny: rampartEntities, backend: rampart() });Our reference page gives the model's specifications, which is the level of detail you need to make a deployment decision: roughly 14.7 MB and 18.5M parameters quantized to 4-bit weights, a 512-token context window, and about 6.6 ms median inference. It recalls around 98% of private terms across the seven Latin-script languages it supports.
It also publishes the limits. Non-Latin scripts have much lower recall. Identifiers without a checksum are recognized less reliably than the structured types. Inference is synchronous, so its latency lands on every request the rule scans. It needs a server runtime with filesystem and native-addon access, so it won't run on edge runtimes, and it must be excluded from server bundling. The model is CC BY 4.0 and the package is Apache-2.0.
You can also extend detection with a detect callback for custom patterns, or add recognizers when
using the model backend.
Running PII detection locally with the Rampart NER model covers the parts of named entity recognition (NER) that aren't the model: the tokenizer returns no original character offsets, so the implementation maintains a per-character map back to the source string, and long inputs are scanned as overlapping windows against the model's 512-token limit with longest-span-wins resolution. The model itself is a 6-layer BERT token classifier with 35 labels, and it ships in the public repository under CC BY 4.0 with attribution to National Design Studio.
Separately, @arcjet/redact redacts locally and gives
you a reversible handle:
const [redacted, unredact] = await redact(text, { entities: ["email"] });It replaces matches with <Redacted email #1> placeholders and unredact() restores them, which is
the pattern you want when you need a model to see structure but not values. For the wider treatment,
see how to detect and redact PII in LLM inputs and outputs.
Prompt injection and content moderation
Prompt injection detection evaluates a message with a specialist model before it reaches your provider. Unlike sensitive information detection, this one is a cloud call. Our docs say so directly: "The prompt text is sent to the Arcjet Cloud API for evaluation," and it adds approximately 100 ms. Be precise about that distinction when you answer a privacy review.
const aj = arcjet({ key: process.env.ARCJET_KEY!, rules: [detectPromptInjection({ mode: "LIVE" })],});
const decision = await aj.protect(req, { detectPromptInjectionMessage: message,});Two pieces of operational advice come with it. Keep the denial response generic, because a detailed
rejection teaches an attacker what to change. And run in DRY_RUN first to measure your
false-positive rate against real traffic before you enforce.
Injection doesn't only arrive in the user's message. Tool results re-enter the context too, which
is why the same rule is available on guard(). For that argument in full, see
the lethal trifecta and
prompt injection protection for LangChain, LlamaIndex, and Vercel AI SDK apps.
Content moderation detects harmful content in
untrusted text. It's a Guard rule with no protect() equivalent, exposed as moderateContent() in
JavaScript, ModerateContent() in Python, and GuardModerateContent in Go. The result is a binary
verdict rather than per-category scores.
Filters and signup protection
Filters give you a Wireshark-style expression language over request fields, evaluated by a Rust engine compiled to WebAssembly. The engine is a public fork of Cloudflare's wirefilter.
filter({ mode: "LIVE", deny: ['ip.src.country == "CN" and http.request.uri.path matches "^/admin"'],});Fields cover hosts, methods, paths, headers, cookies, query arguments, and a large set of
ip.src.* attributes including vpn, tor, proxy, hosting, asnum, and country. The
ip.src.* fields need the IP reputation database, so they cost a Cloud API call; everything else
evaluates locally. You get up to 10 expressions of up to 1024 bytes each.
Matching is literal and case-sensitive, which rules out a whole class of use. As our reference page puts it, "hand-written filters are not an effective way to block injection-style attacks like SQL injection (SQLi) or cross-site scripting (XSS)." That's what Shield is for.
Signup form protection bundles bot detection, email validation, and a sliding window into one rule, with a documented recommended configuration:
protectSignup({ email: { mode: "LIVE", deny: ["DISPOSABLE", "INVALID", "NO_MX_RECORDS"] }, bots: { mode: "LIVE", allow: [] }, rateLimit: { mode: "LIVE", interval: "10m", max: 5 },});The Python SDK doesn't ship the composite; you add the three rules independently.
Agent guards
Agent guards are the part of Arcjet that isn't about HTTP. They put a decision immediately before a side effect, in code, where you know the authenticated user, the tool, and the arguments.
import { launchArcjet, detectPromptInjection, tokenBucket,} from "@arcjet/guard";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const budget = tokenBucket({ bucket: "agent-tokens", refillRate: 2_000, intervalSeconds: 3_600, maxTokens: 5_000,});
const decision = await arcjet.guard({ label: "invoice.refund", actor: session.userId, correlationId: runId, rules: [ budget({ key: session.userId, requested: 1 }), detectPromptInjection()(message), ],});
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) { throw new Error("Refund blocked");}The Guard rate-limit options are named differently from the request SDK equivalents:
intervalSeconds and maxTokens rather than interval and capacity.
Four things about this API repay attention.
label is the policy selector. It names the action, and a security team writes remote policy
against that name. Labels are validated as slugs: lowercase letters, digits, dashes, and dots,
starting and ending with alphanumeric, up to 256 bytes.
actor must be server-side. If an agent can set its own actor, it can select its own policy.
Failure behaves differently by path. A direct guard() call fails open, returning ALLOW with
an error result rather than treating an incomplete check as a denial. Check hasFailedOpen(). The
framework wrappers default the other way and won't run the tool, unless you opt in with
onGuardError: "allow".
capture() is not a decision. It records that something happened, and its delivery is
best-effort by design: a bounded in-memory queue, batches sent on size or a short delay, no retry
on a failed batch, and the newest event dropped when the queue is full. Treat it as telemetry, not
as an audit log of record.
Remote policies let a security team change what a
labelled action permits without touching agent code. You pass typed inputs, and the exposure is
explicit in the type you choose: policyInput.server.string(value) sends the value, while
policyInput.local.string(value) keeps it local and sends a domain-separated SHA-256 digest plus a
rule attestation. We document that digest as correlation data rather than anonymization, since
low-entropy values can be guessed and hashed. Supported remote rules are allow
and deny string lists, string length, string-list membership, prompt injection, and local sensitive
information. Remote rate limits, URL rules, shell rules, and remote custom rules aren't supported.
defineCustomRule lets you write your own rule with a local evaluate function, which is Guard
only.
First-party framework integrations cover the Vercel AI SDK, LangChain in Python, Vercel Eve, Mastra,
and the Claude Agent SDK, each documented at
framework integrations. Import paths are
versioned against the upstream framework, such as @arcjet/guard/vercel-ai/v7 and
@arcjet/guard/mastra/v1. We've written up the per-framework enforcement points in
how to secure a Mastra agent,
Claude Agent SDK security, and
Eve agent security is three jobs.
Introducing Arcjet Guards sets out the placement argument: a tool handler receives untrusted input as a function argument rather than a request body, so there is no HTTP boundary for a proxy or WAF to hook.
A related pattern worth copying whether or not you use Arcjet is in how we defend MCP tool outputs from prompt injection. Tool output is model input, so any MCP tool that interpolates attacker-controlled text into a guidance field becomes a delivery mechanism. The fix is field-level trust separation, schema descriptions that label trust explicitly, and adversarial regression tests asserting hostile strings never reach the trusted fields.
For testing, @arcjet/guard/testing provides registerTestClient(). Understand what it does: it
records calls and returns a fail-open ALLOW, because no rule ran. It doesn't stub per-rule verdicts,
so helpers that fail closed will deny against it.
SDKs, languages, and the management plane
JavaScript and TypeScript are the most mature surface. The JS SDK is Apache-2.0 and reached 1.0 in January 2026, with framework packages for Next.js, Node.js, Express, Hono, Bun, Deno, Fastify, NestJS, Nuxt, Remix, React Router, SvelteKit, and Astro.
Python is pre-1.0 and covers FastAPI, Flask, and Django, with separate async and sync clients. Go is explicitly pre-release: version 0.1.0 requires Go 1.25 or later, and we document that the API may change. Weigh that if Go is your primary language.
Policy doesn't have to live only in code. Remote rules are stored on the platform and evaluated alongside your SDK rules, covering rate limits, bot protection, filters, and Shield, applied site-wide, taking effect without a deploy. There's no precedence between the two sets: if either denies in LIVE mode, the request is denied. Rules needing request body content stay in the SDK. For the trade-offs, see application-native versus remote security policies.
Three management surfaces exist, and they're deliberately agent-friendly. The
CLI treats its commands as a stable API contract, defaults to JSON
when stdout isn't a TTY, and requires --confirm for mutations. The
MCP server at https://api.arcjet.com/mcp exposes the same
surface over OAuth to MCP clients. The
Arcjet plugin bundles MCP access, coding rules, skills, and
a security analyst agent for Claude Code and Cursor, and the
skills repository is public.
Alongside the main SDK sit independent utilities: @arcjet/redact, @arcjet/inspect,
@arcjet/ip, @arcjet/decorate, and Nosecone for
security headers.
Not all of Arcjet is open, so here is the split. The SDKs and Nosecone are Apache-2.0, the bot dataset and the filter engine fork are MIT, and the docs are CC BY 4.0. The compiled analysis WebAssembly modules are committed to the public repository, so you can inspect the artifact, but their Rust sources aren't public. The Cloud API, the MCP server, and the CLI are closed, and we set out where that line sits and why in how Arcjet approaches open source. One piece of the toolchain is public and reusable on its own: gravity generates wazero host bindings for WebAssembly Components, though its README describes it as an early release.
On platform assurance, Arcjet completed a SOC 2 Type 2 examination for the Arcjet Platform as of February 2026, covering Security, Availability, and Confidentiality with an unqualified opinion. The report is available through the Trust Center, and the security page records a 10-working-day patch target for vulnerability reports.
Should you build this yourself?
Every control on this page is buildable. Rate limiting is a counter. Bot detection is pattern matching. PII detection is regular expressions. Each of those sentences is true, and each is the reason teams underestimate the work by an order of magnitude.
The honest framing isn't "can we build this". It's three narrower questions:
- Is this control a source of competitive advantage for us, or is it table stakes that every application needs?
- What does the maintained version cost, not the first working version?
- What happens the first time it's wrong, in either direction?
That third question is the one that separates security infrastructure from most internal tooling. A rate limiter that's too loose costs money. A rate limiter that's too tight blocks paying customers. Both failures are silent until someone notices.
The following sections walk what building each control actually involves, using published evidence rather than assertion. The pattern that emerges is consistent: the naive implementation is a weekend, the correct implementation is a team, and the vendors who publish their engineering are the ones telling you how hard it was.
We'll cite our own engineering writing where it's relevant. Read it with the same skepticism you'd apply to any vendor. It's one team's blog, mostly one author, and it isn't independent corroboration. It is, at least, specific enough to check.
What each control costs to build
Distributed rate limiting
Start with the most instructive fact available: Cloudflare doesn't count exactly. Their published
account of building rate limiting at scale
describes an approximation, and they measured its error at "0.003% of requests have been wrongly
allowed or rate limited" with "An average difference of 6% between real rate and the approximate
rate". They chose it because the storage primitives available to them were GET, SET, and INCR,
and a leaky bucket "requires multiple distinct operations that we cannot do atomically". That post
is from 2017, so read it as history rather than current behavior, but the constraint it describes is
the one you'll meet.
Then read Redis's own INCR documentation, which
walks through the obvious counter implementation and then says, in bold: "In the above code there is
a race condition." The fix is a Lua script evaluated with EVAL. Most in-house rate limiters skip
that step.
Next, the store. Redis Cluster's documentation states that it "does not guarantee strong consistency" and that "it is possible that Redis Cluster will lose writes that were acknowledged by the system to the client". Sentinel's documentation is equally direct: "there is always a window for losing acknowledged writes". Your quota counter inherits that.
Then the latency you added. Redis's own latency guide puts network round-trip on a 1 Gbit/s network at about 200 microseconds, and intrinsic latency at 115 microseconds on bare metal but 9.7 ms on a measured virtual machine, with up to 40 ms observed "in systems otherwise apparently running normally". The same page names mass key expiry as a latency source, which is exactly the access pattern a fixed-window limiter creates when every counter in a window expires at once.
GitHub built one properly and published the bugs. Two were pure distributed-systems faults: a reset timestamp computed by reading a TTL in Redis and adding it to the clock in Ruby, and a read that hit a replica while the write hit a primary, which rejected requests while the response headers advertised 5,000 remaining. They have since moved about 97% of API rate limiting to their gateway so that it "no longer contend[s] with request-serving workers inside the monolith".
If you'd rather buy a gateway than build, Kong documents the trilemma rather than hiding it. Local counters are "Less accurate" and "diverge when scaling the number of nodes". The cluster policy forces "a read and a write on the data store" per request. The Redis policy needs Redis. And when Redis is unreachable, Kong falls back to local counters, so "users will be able to perform more requests than the limit" – your limit silently multiplies by your node count. Envoy's reference rate limit service carries a similar caveat in memcache mode: "it's technically possible for a client to exceed quota briefly".
Finally, the standard itself declines to promise what most client implementations assume. The
IETF RateLimit headers draft
says "Clients MUST NOT consider the available quota parameter as a service level agreement", and
warns servers that "many throttled clients may come back at the very moment specified" – the spec
telling you that your 429 responses create the next thundering herd. Which is why you also need jittered
backoff, as AWS documented.
None of this is exotic. All of it is work you own forever.
Bot detection
Bot detection decays in a way the other controls don't, because an adversary updates faster than your rules do. Every cheap signal has a documented one-line defeat, and every expensive signal depends on something you don't control.
Start with the cheap signals. Setting the user agent is a supported API parameter in both
Playwright
and Puppeteer, not a hack. The
navigator.webdriver flag that the WebDriver specification defines falls to a single Chrome
command-line switch, and the
stealth plugin evasion that does it is a few dozen
lines. That plugin ships 17 evasions and still draws roughly 949,000 downloads a week despite its
last release being in 2023. curl_cffi, which impersonates browser TLS and HTTP/2 fingerprints,
runs above 41 million downloads a month.
Now the expensive signals, which decay on someone else's release schedule. TLS fingerprinting was the strongest cheap discriminator until Chrome began permuting its ClientHello extension order, announced in November 2022. Google's stated motive was anti-ossification rather than anti-detection, but the effect was immediate: Fastly watched the canonical Chrome JA3 fingerprint fall off their network within days, noting that with roughly 15 factorial possible orderings, each connection now has a practically unique JA3. Salesforce's original JA3 repository is archived, and its README says the project "is no longer being actively maintained by Salesforce".
The successor has a licensing catch that matters if you're building a product. JA4 itself is BSD-3-Clause, but the wider JA4+ suite is under the FoxIO License 1.1 and is patent pending, granting rights "only for non-commercial purposes" and stating that "Providing the software on a hosted or managed service basis to others is not a non-commercial purpose". Internal use is fine. Shipping it in something you sell needs a separate license.
Then the allowlist, which is the part teams most underestimate. Verifying AI crawlers against operator-published IP ranges means polling nine JSON files from four operators, together carrying around 1,900 CIDR prefixes. None of those operators commits to an update cadence, and when measured, their staleness ranged from one day to over eighteen months. Only Google publishes IPv6 ranges at all, so on an IPv6-reachable site you cannot verify the others by IP. Anthropic's published list carries no per-bot attribution, so it can't tell you whether a verified Anthropic address is ClaudeBot, Claude-User, or Claude-SearchBot – which is exactly the distinction a training opt-out depends on. And Google-Extended has no user agent and no IP range of its own, so it is only expressible in robots.txt. A self-built detector cannot implement that control at all.
There's a quieter failure mode here too. When Google moved its IP range files to new paths, the deprecated URL kept returning HTTP 200 with stale, incomplete data rather than a redirect or an error. Anyone who hardcoded the old path is still verifying successfully against a list that stopped being updated months ago, with nothing in their logs to say so. That's the kind of bug you find during an incident.
Meanwhile the name list churns. The community ai.robots.txt dataset that many implementations
depend on went from 36 entries to 165 in two years, across commits in 23 of 25 months, with entries
removed as well as added. Composition churns faster than the list: Bytespider was 37.3% of AI
crawler traffic in July 2024 and 5.8% a year later, per
Cloudflare's measurements. Any
per-agent ruleset hand-tuned in 2024 was mostly obsolete in 2025. Peer-reviewed work found the
predictable consequence: 4.5% of sites disallowed user agents that Anthropic never operated, while
missing the one it did.
The deeper problem is labeling your own traffic. A USENIX Security paper on machine learning in security puts it directly: "reliable labels are typically not available, resulting in a chicken-and-egg problem". Cloudflare's answer is instructive about the scale required. They employ analysts writing heuristics tuned to a false positive rate of "0.0001% (One out of 1 million)" for the express purpose of generating labels to train models on, and they have shipped nine model generations since 2019, with their own documentation conceding that older versions' accuracy "may degrade".
IP reputation doesn't rescue it either. Peer-reviewed measurement of residential proxy networks found that 90% of exit addresses relay for about 870 seconds, that only 2.20% ever appear on a blocklist, and that the average blocklisting delay is 22 days. By the time an address is listed, it has moved on.
CAPTCHA is not the fallback it once was. A reCAPTCHA v2 solve costs about $0.0008 at published solver prices, so the protected action has to be worth less than that for the economics to work. Research from ETH Zurich reported solving 100% of reCAPTCHA v2 image challenges with no statistically significant difference from a human in challenge count. And the W3C's note on CAPTCHA inaccessibility is unambiguous about the cost to real users: the interactive task "inherently excludes many people with disabilities, resulting in a denial of service to these users". We go into that trade-off in CAPTCHAs versus Arcjet.
One caveat on the numbers you'll see quoted elsewhere. No published "percentage of internet traffic that is bots" figure measures the internet. Each one measures a single vendor's customer base under that vendor's own classifier, and they disagree by nearly a factor of two, from 29% to 53%. Treat them as directional.
PII detection
The reference open source implementation is Presidio, and its own documentation is the strongest argument for not treating this as a solved problem. Its FAQ states that "there is no guarantee that Presidio will find all sensitive information. Consequently, additional systems and protections should be employed", and that it "is not an official product of any company and comes with no warranty or SLA". Its evaluation documentation says the vanilla configuration's "results aren't very accurate".
The project publishes the size of that gap in its own repository. On the 1,500-sample synthetic dataset in its evaluation notebooks, stock Presidio scores an F2 of 0.661 at 0.646 recall, and the tuned configuration reaches 0.910 F2 at 0.907 recall. Both are the maintainers' own numbers on their own synthetic data, and the notebooks say as much: "the synthetic dataset used here isn't representative of a real dataset". Read it as the shape of the gap, not as a benchmark.
What closes that gap is the interesting part, because the notebook is an itemised build bill. It swaps the NER model for a 434M-parameter transformer with 1.74 GB of weights, hand-writes a label map from that model's tags onto Presidio's entity names, adds three custom recognizers, deletes 14 that don't apply, widens the context window, and retunes the score threshold. All of that is specific to one dataset, and none of it transfers to yours.
Independent measurement backs that up. The Text Anonymization Benchmark, published in Computational Linguistics, measured stock Presidio's entity recall on direct identifiers – full person names and similar – at 0.460 on the test set. Closing that gap required fine-tuning a Longformer on in-domain data. A peer-reviewed clinical evaluation of Presidio with customization reported strict recall of 0.8064, and concluded that "additional checks are required to ensure person names are successfully anonymised".
Count what you actually get. Presidio's published entity list documents 80 types, of which 34 have a checksum or validation step and eight are model-driven. The rest are regular expressions plus optional context words, including US Social Security numbers, passports, driver's licences, and bank numbers. More to the point, only 19 of those types and 17 recognizers load by default for English. Everything else is opt-in, country-specific, or needs a separate model.
Presidio is candid about what those regexes are worth. Its own
Social Security recognizer
labels the bare nine-digit pattern "very weak" and scores it 0.05, and its card recognizer scores
the regex 0.3 and "weak", leaving the Luhn check and eleven context words to do the real work.
Checksums help less than people assume. Exactly 10% of random digit strings of any length pass the Luhn check, because the check digit is the unique digit that makes the weighted sum divisible by ten. Luhn tells you a number survived a transcription error. It doesn't tell you the number is a payment card. Order IDs and database keys in the correct shape will pass.
Names and addresses need a model, and models are domain-specific. On the same named-entity task, the highest published result on CoNLL-2003 newswire is 94.6 F1. On WNUT-2017 emerging entities in user-generated text, the shared task's winning system managed 41.86, and later work reaches only about 50. Your users write like the second corpus.
Downloading a purpose-built model doesn't reliably shortcut this either. Piiranha, one of the better
known PII taggers, is published under
cc-by-nc-nd-4.0 –
non-commercial, no derivatives – so it can't ship inside a commercial product at all. Check the
licence before the accuracy table. And in that table, surname recall is 0.78, the weakest row in a
model built specifically for this job.
Then deployment. Presidio's default spaCy model, en_core_web_lg, is 382.1 MB, against
AWS Lambda's 250 MB
unzipped package limit including layers, so you're on the container image path. spaCy's
published throughput is 10,014 words per second on CPU for
that pipeline and 684 for the accurate transformer one. And spaCy documents that with the spawn
start method "the model data is copied in memory for each new process", so per-worker memory
multiplies.
Compare that with the numbers in the sensitive information section: a 14.7 MB quantized model at about 6.6 ms median inference, running in-process. The interesting part isn't that one is smaller. It's that both sets of figures are published, so you can actually do the comparison.
Email validation
Email looks like a regular expression problem for about an hour. RFC 5322 defines the grammar in 133 ABNF rules, 53 of which are obsolete forms, and section 4 requires that those obsolete forms "MUST be accepted and parsed by a conformant receiver". RFC 6531 then extends the grammar to UTF-8, which makes any ASCII-only character class wrong by specification.
The famous 6,000-character validation regex has a real author and a real caveat. Paul Warren generated it from the grammar, and his own page records the limit: comments in addresses can nest arbitrarily, and "A single regular expression cannot cope with this". His module preprocesses addresses to strip comments before matching.
And syntax was the easy half. Deliverability needs MX lookups, disposable-domain intelligence that decays weekly, and a decision about role addresses.
Prompt injection detection
This is the one control where the honest answer is that neither building nor buying solves the problem, and we'd rather say so than sell you a number. Arcjet ships prompt injection detection. Run it, but don't treat it as a gate you can stand behind.
Start with what the standards bodies say, because they're unusually direct. OWASP's 2026 Top 10 for LLM applications states that LLMs "make no architectural distinction between instructions and data", that "no reliable prevention mechanism exists today", and draws the conclusion in one sentence: "Defense is therefore architectural rather than interceptive." The UK's National Cyber Security Centre is blunter still, warning that prompt injection "cannot be fully mitigated with a product or appliance" and advising readers to "beware any that claim they can 'stop' prompt injection". NIST's adversarial machine learning taxonomy tells designers to assume "prompt injection attacks are possible if a model is exposed to untrusted input sources".
Now the measurements, which are worse than the marketing. Vendor model cards routinely publish accuracy above 99%. Independent evaluation at a realistic false-positive budget puts the same detectors in single digits: one peer-reviewed benchmark measured a widely used open detector at 1.97% true positive rate at a 1% false positive rate, and 0.00% at 0.1%. Meta's own second-generation model card scores its own first generation at 21.2% recall at a 1% false positive rate, against the 99.9% headline on that model's own card. The gap isn't dishonesty. It's the operating point: an in-distribution test split flatters a classifier that a deployment budget does not.
Under adaptive attack, which is the only threat model that matters for a security control, they collapse. Researchers from Google DeepMind and ETH bypassed 12 recent defenses with attack success above 90% for most, noting that "the majority of defenses originally reported near-zero attack success rates". A separate study measured detection rates falling from 61% to 1% once the attacker adapted. And stacking doesn't rescue it: the same authors report that "simply adding more filters or stacking additional detectors does not resolve the underlying robustness problem". Trivial transformations still work – putting spaces between letters once took a shipped Meta classifier from 100% accuracy to 0.2%.
Then the part that decides the architecture. Detection that catches everything catches your product too. A USENIX Security evaluation found the only detector achieving a zero false negative rate did so with a false positive rate as high as 0.93. And in AgentDojo, adding a prompt injection classifier cut targeted attack success from 57.69% to 7.95% but destroyed 27.5 points of benign utility, while an architectural tool filter reached a better 6.84% attack success rate and raised utility. That result is the whole argument in one table: constraining what the agent may do beats trying to recognise what the attacker said.
Which is why we treat these as two controls rather than one. Prompt injection detection is a layer,
and we tell you to run it in DRY_RUN first and keep denials generic. The
control that actually holds is authorization at the point of action, which is what
agent guards are for. We make the same argument at more length in
the lethal trifecta, where a claimed 95% catch rate is a failing grade for
an exfiltration path, and in
human approval is not a security policy.
The fair counterweight, from the researchers who broke every detector they tested: "detectors are straightforward to deploy and can still provide practical value by blocking some unsophisticated or opportunistic attacks, making them a useful – but limited – component of a broader defense strategy." Build or buy, you own the architecture either way. Buying saves you the model, the evaluation set, and the false-positive budget on a threat that mutates weekly.
A WAF rule set
The OWASP Core Rule Set is free, excellent, and the clearest available illustration of maintenance cost. Its own documentation on paranoia levels is refreshingly blunt: at PL4 "the rules are so aggressive that they detect almost every possible attack, yet they also flag a lot of legitimate traffic as malicious", and "Running at the highest paranoia level, PL 4, may seem appealing from a security standpoint, but it could take many weeks to tune away the false positives encountered". A note on the same page warns that writing exclusions "can be a substantial amount of work".
The project ships continuously, and its changelog says what the work is. From v4.0.0 in February 2024 to v4.29.0 in August 2026 there were 33 stable v4 releases, close to one a month without a break. Across the point releases after v4.0.0, 180 changelog entries are fixes and more than 40 correct false positives. The false positives and tuning guide explains why that work never finishes: "A fresh CRS deployment has no awareness of the web services that may be running behind it, or the quirks of how those services work." It also warns that editing rule files directly forks the rule set, making every update a manual reapplication. The issue tracker carries 454 false-positive reports and 162 evasion reports.
Now the counter-intuitive result. ModSec-AdvLearn, published in IEEE Transactions on Information Forensics and Security, measured vanilla ModSecurity with CRS at a fixed 1% false positive rate and found the true positive rate went down as the paranoia level went up: 66.82% at PL2 against 61.00% at PL4 on one dataset. The authors' conclusion is worth quoting: "increasing the number of rules does not improve detection capabilities; instead, it worsens them by increasing false positives." More rules is not more security.
The maintainers have documented what this costs, in unusual detail. In spring 2022, Yahoo and Intigriti ran a three-week bug bounty against CRS. Project co-lead Christian Folini's retrospective is subtitled "it's not for the faint of heart" and earns it. The team had "estimated our capacity to be at around two security findings per week", then received 175 reports: "175 reports fixed and 511 individual payloads detected." Among them they "identified almost a dozen rule/partial ruleset bypasses", which Folini calls "really bad". Closing out three weeks of testing ran until February 2023, forced the replacement of the project's regex generator, and, in his words, "delayed the CRS v4 release by a year".
One line from that post is the whole build-versus-buy case for detection rules: "So an individual bypass is more or less daily business for us."
Some defects outlive entire release lines. In 2019, answering five ReDoS CVEs filed against its own regexes, the project was candid that the problem was known and unsolved: "We just have not solved it yet - or have not been able to solve it yet", noting that many rules were "10 or 15 years old" and predicting "This can take a while". Version 4.28.0, in July 2026, shipped five separate catastrophic-backtracking fixes. Same class of defect, seven years later.
Rule-set vulnerabilities aren't always fixable inside the rule set. A July 2026 advisory covers a bypass through XML attribute values that "affects approximately 159 rules across 9 rule files" at every paranoia level, and it needed a matching ModSecurity engine release to land. Changing a single regex safely is its own project: that release documents a differential test over roughly 2.4 million random inputs, a false-positive corpus run at all four paranoia levels, and an 889-test regression suite.
Deploying a rule set means inheriting its support window too. CRS patches "the two latest point releases on the current development line" plus one long-term support line. At close to monthly releases, a deployment that stops tracking updates leaves the supported set within a couple of months.
And the canonical incident. Cloudflare's post-mortem on the 2 July 2019 outage attributes 27 minutes of global downtime to "a single WAF rule that contained a poorly written regular expression that ended up creating excessive backtracking". The rule wasn't even blocking: it was deployed in simulate mode, "But even in the simulate mode the rules actually need to execute". Two numbers from that write-up describe the ongoing job better than any estimate: "In the last 60 days, 476 change requests have been handled for the WAF Managed Rules (averaging one every 3 hours)", and the remediation included "Manually inspecting all 3,868 rules".
Finally, CRS tells you what it can't do: "Application-specific vulnerabilities, such as logic bugs or missing authorization checks, cannot be detected by generic firewall rules." That's the same boundary we describe in SDK-based security versus WAF versus API gateway, and it's why a WAF is a layer rather than an answer.
When building in-house wins
A build-versus-buy guide that concedes nothing isn't a guide. Building is the right call more often than vendors admit, and these are the cases where it holds.
Your policy needs your domain model. This is the strongest case, and Shopify published the clearest example of it. They moved their API from request counting to calculated GraphQL query complexity because clients "use the same amount of credits regardless, even if they don't need all the data in an API response", and because writes "produce side effects that demand more load on servers than GET requests". No generic limiter can express "this query is 40 times more expensive than that one". If your policy depends on semantics only your application knows, the rule belongs in your code. Arcjet's own token bucket exists for a mild version of this, and the reasoning extends further in.
You already have the platform team. The marginal cost of one more control on a staffed internal platform differs completely from the cost of standing that platform up. Google's Site Reliability Engineering puts the minimum sustainable single-site on-call rotation at eight engineers, and caps aggregate operational work at 50% of SRE time. If you already run that, a rate limiter is a smaller step for you than for most. If you don't, the rotation is the cost, not the code.
Extreme scale changes the arithmetic. Usage-based pricing is good value until your volume makes it your largest infrastructure line item. The most fully documented public case is 37signals, whose founder reports taking a cloud bill "from the original $3.2 million/year run rate" to $1.3 million, with roughly $700,000 of hardware "entirely recouped during 2023". Read it as advocacy from an unusually favorable case – stable workload, strong in-house operations, no regulatory constraint – and then run your own numbers.
Dropbox's S-1 is the more rigorous datum, because it's an SEC filing rather than a blog post, and because it discloses both sides. Moving off a third-party provider removed $92.5 million of vendor spend in one year, and added back $53.0 million in "depreciation, facilities, and support expense" for infrastructure they now operated themselves. Owning it consumed well over half the saving. That ratio is the number to carry into your own model.
You can't send the data anywhere. For some organizations this is a legal fact rather than a preference. Under the GDPR, Chapter V governs any transfer of personal data to a third country, and Schrems II both invalidated the EU-US Privacy Shield and required supervisory authorities to suspend transfers where standard contractual clauses "are not or cannot be complied with in that third country". In EU financial services, the Digital Operational Resilience Act (DORA) has applied since January 2025 and subjects reliance on critical ICT third parties to mandated contractual terms and direct supervisory oversight.
Don't overstate this, though. Transfers to the US are not blocked. The Commission adopted a new adequacy decision for the EU-US Data Privacy Framework in July 2023, and the General Court upheld it in September 2025. The US remains on the Commission's adequacy list for organisations participating in the framework, with an appeal pending. What Chapter V leaves you with is a transfer assessment you have to make and document, not a prohibition.
Residency commitments also tend to carve out the category this page is about. Microsoft's EU Data Boundary documentation excludes Defender for Endpoint, Defender for Identity, and Defender for Cloud Apps, on the stated grounds that those services "require operations of global systems including artificial intelligence, automation, and humans on global data sets to hunt global customer threats". That reasoning is sound, because threat hunting is global work. It also means a data boundary you partly bought for security reasons need not cover your security tooling. Ask where each control inspects.
Be precise rather than absolute, though, because the answer differs per control. As the architecture section sets out, sensitive information detection never sends the body, while prompt injection detection does. Evaluate control by control, not vendor by vendor.
Vendor dependency is a real risk. Buying moves a failure mode rather than removing it, and the public record is specific. A CrowdStrike content update live for 78 minutes affected 8.5 million Windows devices. A single regular expression cost Cloudflare 82% of its traffic for about half an hour. One customer's valid configuration change made 85% of Fastly's network return errors. Cloudflare's longest outage since 2019 originated in a bot management configuration file, which is exactly the kind of vendor-managed security config a buyer cannot inspect.
The sharper version of this risk is that exiting is largely theoretical. In the quarter after its outage, CrowdStrike told the SEC that while it had seen "delays in creating sales opportunities and longer sales cycles", it had "not experienced high levels of customer churn following the incident", and reported a dollar-based net retention rate of 115%. A vendor can blue-screen 8.5 million machines and keep its customers. Plan for a vendor failure you have to absorb, because that is the usual outcome, and prefer controls you can reason about and turn off yourself.
Terms change too. When Redis relicensed in 2024 it put an end date on security patches for the license you were already on. Google shut down reCAPTCHA v1 outright and has since deprecated its successor's documentation into a different Google Cloud product. Buying includes the risk that your dependency's terms move on someone else's schedule.
Mitigate it deliberately rather than hoping: understand the fail-open behavior, decide it per action, and know what your application does when the vendor is unreachable.
The case where building usually loses is the common one. A control every application needs, ordinary requirements, no platform team, and failure modes subtle enough that you'll learn them in production.
What buying costs, concretely
Arcjet prices a base plan per application per month plus metered usage. At the time of writing that's $25 for Individual, $299 for Startup, and $799 for Growth, with Enterprise custom, and a 15-day trial. Usage is billed separately: $5 per million requests, with bot detection at $0.50 per million, advanced bot signals at $2 per million, PII detection at $1 per million, email validation at $1.50 per thousand, and prompt scanning at $2 per million tokens. Log retention differs by plan, from one hour on Individual to 30 days on Growth, which matters if you're relying on it for evidence. See compliance evidence for AI agent activity for why that's not an archive.
Compare that against the build side honestly, and build the comparison from figures you can check rather than a vendor's calculator.
US Bureau of Labor Statistics figures for May 2025 put the median annual wage for software developers at $135,980 and for information security analysts at $129,180. Wages aren't the employer's cost, though: BLS also reports that wages and salaries are 69.9% of total compensation for private industry workers, which makes the loading roughly 1.43 times salary before you count recruiting, equipment, or management.
Now add the operational floor. Google's Site Reliability Engineering states that "the minimum number of engineers needed for on-call duty from a single-site team is eight". Multiply that out at the median plus loading and a sustainable rotation is somewhere near $1.5 million a year in fully loaded payroll – that arithmetic is ours, not a published figure, and it buys you the rotation, not the security capability. The same book caps aggregate operations work at 50% of SRE time and budgets an incident at roughly six hours of follow-up, which is why the rotation has to be that size.
Then add the recurring work each control brings. Every figure here is sourced earlier on this page:
- A bot corpus that grew from 36 to 165 entries in two years, and whose largest member changed entirely.
- A rule set shipping roughly monthly, whose own maintainers warn that PL4 tuning "could take many weeks".
- A PII model that needs a domain evaluation set, because stock recall on direct identifiers measured 0.460.
- A Redis cluster whose own documentation warns you about acknowledged writes being lost.
For most teams the deciding factor isn't the invoice. Andy Jassy put the general version of it in Amazon's 2024 shareholder letter, filed with the SEC: "Why should builders spend 80% of their time on the undifferentiated heavy lifting vs. their unique customer experience?" None of the work on this page differentiates your product, and all of it competes for the engineers who could build what does.
One caution on numbers you'll meet while making this case. Several of the statistics that circulate in build-versus-buy arguments don't survive tracing. The claim that 100 ms of latency costs Amazon 1% of sales has no Amazon source; the nearest real measurement is Kohavi et al. at KDD 2014, reporting that a 250 ms server delay moved revenue about 1.5% at Bing, and explicitly cautioning against extrapolating linearly. The frequently quoted $4.45 million average breach cost is the 2023 IBM edition, three editions stale. If you're building a business case, check your own figures the way you'd want a vendor to check theirs.
How to decide
A short process that produces a defensible answer:
- List the controls you actually need, by the consequential actions in your application rather than by feature name. Authentication endpoints, anything that spends money, anything that sends messages outward, and any tool an agent can invoke.
- For each one, decide whether it's differentiating. If a competitor having the same control wouldn't hurt you, it's table stakes.
- Cost the maintained version, not the first version. Include the corpus, the tuning, the on-call, and the false-positive budget.
- Check the data-residency constraint per control, not for the vendor as a whole, since the answer differs by control.
- Decide the failure behavior per action before you deploy anything. Fail open on a search page and fail closed on a payment is a coherent policy; one global setting isn't.
- Run in dry run first. Every Arcjet rule takes
mode: "DRY_RUN", which evaluates and records without blocking, andarcjet analyze dry-run-impactquantifies what would have happened.
To see the enforcement points in your own stack, the agent get started guide installs a skill that makes your coding agent framework-aware, and the CLI and MCP server let it inspect real decisions rather than guess.
Related reading: What is runtime application security? · Enforce security rules at runtime in code · AI agent security platforms compared
Frequently asked questions
What is Arcjet?
Arcjet is the runtime security platform that ships in your AI code. It detects prompt injection, authorizes agent tool calls, redacts sensitive data, and blocks bots and abuse, through real-time building blocks you call inside your application before an action happens. There is no proxy and no DNS change: you import an SDK, declare rules, and call protect() on an HTTP request or guard() on an action that has no request object. The direction of the product is an application-native security enforcement layer for AI workflows, connecting enterprise identity to centrally managed policy, evaluating context before consequential actions, and producing evidence.
What features does Arcjet include?
Shield WAF, rate limiting in three algorithms, bot protection with optional browser signals, email validation, sensitive information detection, prompt injection detection, content moderation, filters, signup form protection, and agent guards for tool calls and other non-HTTP actions. Rules run through protect() on HTTP requests or guard() on actions with no request object.
Which Arcjet checks run locally and which call the cloud?
Sensitive information detection runs entirely in your process and never sends the request body. Shield, rate limiting, bot database lookups, email verification, and IP reputation filters need a Cloud API call, which we document at typically 20 to 30ms with one call per request regardless of rule count. Prompt injection detection sends the prompt text and adds roughly 100ms.
Is it cheaper to build application security in-house?
Rarely, once you cost the maintained version rather than the first working version. Google's SRE book puts a sustainable single-site on-call rotation at eight engineers, and each control brings recurring work: a bot corpus that churns monthly, a WAF rule set whose maintainers warn that tuning can take weeks, and a PII model that needs a domain evaluation set. Building wins when your policy needs your own domain model, when you already run the platform, at extreme scale, or when data residency rules it out.
Why is prompt injection detection not enough on its own?
Because detection degrades against an adversary who adapts. OWASP's 2026 Top 10 states that no reliable prevention mechanism exists and that defense is architectural rather than interceptive, and researchers bypassed 12 recent defenses with attack success above 90%. Run detection as a layer, then authorize the action itself at the point of the side effect.
What are the documented limits of Arcjet Shield?
Shield analyzes request headers and query parameters, not the request body, and the analysis happens on the Arcjet platform after the request is reported. Body inspection is a separate feature, sensitive information detection, which runs locally instead.
AI runtime security in your code
Protect your AI agent workflows with Arcjet
Arcjet runs inside your application, where it can use runtime context to enforce agent actions and budgets, detect prompt injection, and protect sensitive information before a workflow acts.