Arcjet JS SDK: Runtime Security Checks Inside Your TypeScript Application
Runtime security for AI apps and agents: prompt injection detection, tool-call authorization, sensitive-data redaction, bot protection, and rate limiting. Drop it into your JS/TS code.
At a glance
- What is it?
- Arcjet ships as a set of TypeScript packages that run security decisions inline, before a request handler or an agent tool call executes. The split between request protection and guard protection is the design decision that matters most when you pick it up.
- Who is it for?
- Adopt Arcjet if you already run Node, Bun, Deno or a framework adapter and you want prompt injection detection, tool-call authorization, redaction, bot blocking and rate limiting decided inside the same process that handles the request or the tool call.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The two entry points Arcjet splits its own product along
Most security libraries assume there is an HTTP request to inspect. Arcjet's README does not. It splits its packages by whether a Request object exists at the call site. Request protection covers HTTP route handlers, API endpoints and middleware, and is served by the framework packages: @arcjet/next, @arcjet/node, @arcjet/bun, @arcjet/deno, @arcjet/fastify, @arcjet/nest, @arcjet/nuxt, @arcjet/remix, @arcjet/react-router, @arcjet/sveltekit and @arcjet/astro. Guard protection covers AI agent tool calls, MCP server handlers, queue workers and background jobs, and is served by a single package, @arcjet/guard. The README is explicit that the two can coexist in one project. That split is the honest part of the design. A tool call inside an agent loop has no client IP, no User-Agent and no URL path, so bot detection and IP-based rate limiting are meaningless there, while prompt injection detection and tool-call authorization are meaningless on a static asset route. The audience is therefore narrower than the tagline suggests: teams running JS or TS services that expose both ordinary endpoints and agent-driven actions, and who want one decision layer in front of both.
What protect() and guard() actually decide
The README describes Arcjet as security building blocks you call inside your app, before an action happens, and lists the protections as prompt injection detection, tool-call authorization, sensitive-data redaction, bot protection and rate limiting. The naming maps to the split: request protection is wired through protect(), guard protection through guard(). The README does not publish the rule schema, the decision object shape, or the latency characteristics of any check, so anyone evaluating this should read the per-package READMEs in the monorepo rather than assume the surface from the top-level file. What is visible is the direction of the data flow. The check runs in your process, the decision comes back, and your code branches on it. That is different from a reverse proxy or a WAF, which sits in front of the application and never sees the tool call at all. It also means the sensitive-data redaction and prompt injection checks are operating on values your application has already assembled in memory, which is the only place an agent's tool arguments exist in the first place.
Installing it: the CLI, the skills step, and the package table
The README's own fast path is agent-driven. Step one is authentication through the CLI: npx @arcjet/cli auth login. Keys can also be managed at app.arcjet.com, or the Arcjet MCP server can be connected to an AI assistant. Step two installs a skill so a coding agent can detect the framework and wire up rules: npx @tanstack/intent@latest install. The README notes that this discovers @arcjet/skills from node_modules without executing package code, and recommends an allowlist in the app package.json so only those packages can surface skills, using an intent key with a skills array containing @arcjet/skills and @arcjet/guard. It also states plainly that editor hooks from intent hooks install are convenience, not a security boundary. Step three is the ordinary install: npm i @arcjet/next, npm i @arcjet/node, bun add @arcjet/bun, deno add npm:@arcjet/deno, and so on down the table, or npm i @arcjet/guard for the no-request path. Step four is a natural-language instruction to the agent, such as asking it to add bot protection and rate limiting to an API route, or to add guard with prompt injection detection to an MCP tool handler. The skill route is optional; the packages install the normal way.
Client IP provenance is the sharp edge in this SDK
The README carries a warning block, and it is the most concrete limitation in the material. When a platform-provided client IP is unavailable, Arcjet may fall back to forwarding headers such as X-Forwarded-For. Clients can spoof those headers if they can reach the application directly, or if the proxy in front preserves client-supplied values. Arcjet keeps protecting the request in that case, but logs one warning for the lifetime of each Arcjet client instance and marks the source as unverified-header in the client_ip_provenance debug facet. The mitigation is deployment-shaped: make the application reachable only through a proxy that overwrites or safely appends forwarding headers, and list every trusted hop in the proxies option. Proxy-service helpers such as cloudflare() exist where available. Invalid proxy entries and non-empty malformed ipSrc values are rejected, though ipSrc: "" is equivalent to omitting the option, and trusting 0.0.0.0/0 or ::/0 emits a configuration warning. You can inspect the selection with client.clientIpDetails(request) in @arcjet/node, or with findIpDetails() and resolveClientIp() from @arcjet/ip in other adapters. If your application determines the address itself, the README says to pass that validated value as ipSrc to both the debugging API and protect(). The practical reading: if your app is directly reachable, IP-derived rules are only as trustworthy as your proxy configuration, and the SDK says so rather than hiding it.
Where the in-process model is the wrong tool
Running security inside the application has a cost that the README does not discuss. Every protect() or guard() call is code in your request path, so a bug in rule wiring, a thrown error, or a misconfigured client becomes an availability question for the feature it guards, not just a security question. The README also does not state whether checks are evaluated locally, remotely, or both, which matters for anyone with strict data-residency constraints or an air-gapped deployment: if a check requires a network round trip, the call site inherits that latency and that dependency, and the material here does not settle the question. The client IP warning is a second boundary. And the guard path is narrower than it sounds: it covers tool calls, MCP handlers, queue workers and background jobs, which means it does not cover a model's raw output before it reaches a tool, only the call itself. Teams expecting a content filter on everything the model says should confirm the scope in the @arcjet/guard README before assuming coverage.
Choosing between Arcjet and a gateway-level filter
The obvious alternative category is an API gateway or reverse proxy that inspects HTTP traffic before it reaches the application. The difference in approach is structural rather than feature-level. A gateway sees one request with one set of headers and can be updated without redeploying the app, and it cannot be bypassed by a code path that forgets to call it. Arcjet sees the inside of the request: the parsed body, the tool arguments, the values a handler is about to act on. Prompt injection detection and tool-call authorization only make sense at that layer, because a gateway has no view of what an agent decided to do. The trade-off runs the other way for bot protection and rate limiting, which a gateway or CDN can enforce before your process is even woken. A team already running an edge proxy for rate limiting may find the overlap uninteresting and the in-process bot check redundant. A team whose agent tool calls never leave the process has no gateway equivalent at all.
Licence, release cadence and the cost of keeping up
The repository is Apache-2.0, which permits commercial use and modification and includes an explicit patent grant. That is a permissive licence, not a copyleft one, so embedding the packages in a closed-source service does not by itself trigger source-disclosure obligations. This is a description of the licence text, not legal advice; anyone with redistribution or modification plans should read the Apache-2.0 terms or consult counsel. On cadence, the recent releases are v1.12.0 on 2026-09-08, v1.11.0 on 2026-08-26 and v1.10.0 on 2026-08-11, roughly every two to three weeks, with the last push to main on 2026-09-10. The monorepo also publishes a large set of framework adapters, and each adapter is a separate package that can lag the core. The maintenance cost is therefore not the SDK itself but the adapter matrix: a Next.js app pins @arcjet/next, and a project that later adds a Fastify service picks up @arcjet/fastify on its own schedule. The README does not publish a support window or a deprecation policy for older adapter versions, so version pinning and upgrade testing are on you. The CLI-based auth flow also means the service side is a hosted component, and the README does not describe what happens to protect() and guard() calls if that component is unreachable.
Editorial conclusion
Adopt Arcjet if you already run Node, Bun, Deno or a framework adapter and you want prompt injection detection, tool-call authorization, redaction, bot blocking and rate limiting decided inside the same process that handles the request or the tool call. Do not adopt it if you cannot route traffic through a proxy you control that overwrites forwarding headers, because the README states that client IP selection falls back to X-Forwarded-For and marks the source as unverified-header when no platform-provided address exists. Before writing rule code, run npx @arcjet/cli auth login, install the matching package from the table, and confirm which of the two entry points your call site actually is: a handler with a Request object takes a framework SDK, and anything without one takes @arcjet/guard.
Community notes