Runtime security

How to Enforce Security Rules at Runtime in Your Code

Security that lives in a separate system drifts from the application it protects. In-code enforcement closes that gap by construction: the rule is written, reviewed, and deployed with the feature it protects.

7 min read
In short: Security that lives in a separate system drifts from the application it protects. In-code enforcement closes that gap by construction: the rule is written, reviewed, and deployed with the feature it protects.

How do you enforce security rules at runtime in your code?

You write the rule in the same pull request as the feature it protects, and it runs in the request path, returning a decision that your code acts on before the action proceeds. The control ships with the thing it controls, the same people review it, and it deploys and rolls back on the same commit. This is runtime security that ships with your code.

The alternative, which is the default in most organizations, is that the control lives in a separate system and drifts.

The rule belongs next to the feature

Security that lives elsewhere ages apart from the application. A new route ships and the WAF rule doesn't. Someone adds a new tool and nobody tells the platform team. The control and the code diverge quietly, and teams usually find the divergence during an incident.

In-code enforcement closes that gap by construction. When the feature changes, its protection is right there in the diff. A reviewer looking at a new tool handler can see whether it is guarded without opening another console.

What running in the application buys you

The application gives you two things that the edge can't provide.

Context. In the application you know the authenticated user, their plan, the route, the tool, the arguments, and the body. At the network layer you have IP addresses and headers. Most interesting policy needs the application context. The edge can answer "Is this request rate-limited?" It can't answer "Is this user allowed to modify this object?", because the edge doesn't know who the user is or what the object is.

Reach. Rules in code run wherever your code runs, including tool calls, queue consumers, and workflow steps that never see an HTTP request. As applications take on more background and agentic work, the share of consequential actions that never cross the perimeter grows.

The cost, honestly

Enforcement in the request path has to be fast, so the following table gives real numbers for each path.

PathTypical overheadWhy
Local WebAssembly analysisUnder a millisecondRuns in the application with no network round trip
Cloud call for cross-request stateTypically 20 ms to 30 ms total

Distributed rate-limit counters and the live bot database need shared state, with one call per request regardless of how many rules are configured

Prompt injection detectionRoughly 100 msRuns a specialist detection model

When a security check can't finish (a timeout, a transport issue, or an incomplete policy), you choose what happens next. A direct guard() call returns allow with error codes rather than treating the incomplete check as a denial. Vercel AI SDK and LangChain wrappers fail closed unless you opt into continuing on error. HTTP request checks (protect()) can fail open when Arcjet's cloud can't be reached; that behavior is configurable.

Make the fail-open versus fail-closed choice per action rather than globally. A product search page and a payment endpoint don't need to degrade the same way.

A decision can be an allow because evaluation failed, which is a different thing from an allow because the request was fine, and a fail-closed policy needs to distinguish them.

Roll out without breaking production

  1. Dry run everything first. Rules log what they would have blocked against live traffic. arcjet analyze dry-run-impact quantifies it before you commit to anything.
  2. Enforce the confident ones. Move rules to live individually, not all at once, so a change in behavior has one obvious cause.
  3. Keep the tuning loop outside the release cycle where the product allows it. HTTP remote rules let security teams change site-wide fixed-window or sliding-window rate limits, bot, Shield, and filter rules without a redeploy. Token-bucket, email, PII, and prompt injection stay in code. Guard remote policies cover allow/deny lists, length, membership, prompt injection, and local sensitive-info, not rate limits. Application-specific rules, including budget refill and capacity, stay in code.

The failure mode that this sequence avoids is the one where a team turns on enforcement broadly, sees a support spike, can't attribute it to a specific rule, and switches the whole thing off. A control that gets disabled during its first incident wasn't a control.

The division that makes it work

Developers own where enforcement sits, because it is part of the feature. Security teams own what the policy is, because that is their job.

Most products in this category make you choose one. Developer libraries put rules in code and leave the security team unable to change anything without a deployment. Governance platforms put rules in a console and leave engineers out of a decision that affects their code path. Neither half is wrong, and organizations that have both functions need both.

Supporting both routes against the same enforcement point is what makes this operable past the first team. Both paths feed the same decision model, and every decision is inspectable in the Console, CLI, or MCP server.

What to put in code first

Start with the actions where a failure is most expensive rather than the routes with the most traffic. In practice that usually means authentication endpoints, anything that spends money, anything that sends messages outward, and any tool that an agent can invoke.

Rate limiting and bot detection are the conventional starting points, and they remain the highest-value controls for most applications. For more information about them, see the rate limiting guide and what is API abuse. For applications with AI features, add budgets at the tool call, injection detection on untrusted input, and local sensitive-information detection at the boundaries. For more information about those controls, see runtime security for LLM applications.

For the broader placement argument, including how this compares to a WAF or an API gateway, see what is runtime application security and SDK-based security vs WAF vs API gateway.

Where Arcjet fits

"In your code" is the position Arcjet occupies in this market. Most of the category is external: a proxy, an edge WAF, an API gateway, an AI gateway, or a hosted control plane. Those layers enforce on traffic that reaches them, and they do that well.

Arcjet runs as a library inside the application. JavaScript, Python, and Go are the documented languages for request handlers and Guard, with the Go SDK in pre-release. Most of the analysis runs in a local WebAssembly module, and Arcjet makes a cloud call only where cross-request state is required. It runs behind your existing CDN and WAF rather than replacing them.

The distinguishing property is reach and context together: rules run wherever your code runs, including tool calls and queue consumers with no HTTP request, with the authenticated user and target object available at decision time.

Learn more: Architecture · How to roll out application-layer security without breaking production

Frequently asked questions

How do I enforce security rules at runtime in my code?

Write the rule in the same pull request as the feature it protects, so it runs in the request path and returns a decision your code acts on before the action proceeds. It is reviewed, deployed, and rolled back on the same commit as the feature.

What does running in the application give me that the edge cannot?

Context and reach. In the application you know the authenticated user, their plan, the route, the tool, and the arguments, where the network layer has IP addresses and headers. And rules in code run wherever your code runs, including tool calls and queue consumers with no HTTP request.

How much latency does in-code enforcement add?

Local WebAssembly analysis is under a millisecond. Controls needing cross-request state use a cloud call for typically 20 to 30ms total, with one call per request regardless of how many rules are configured. Prompt injection detection is roughly 100ms because it runs a model.

How do I roll out enforcement without breaking production?

Dry run everything first so rules log what they would have blocked against live traffic, move rules to enforcing individually rather than all at once, and keep HTTP remote rules (site-wide fixed-window or sliding-window rate limits, bot, Shield, and filter) outside the release cycle. Token-bucket, email, PII, and prompt injection stay in code. Guard remote policies cover lists, length, membership, prompt injection, and local sensitive-info, not rate limits.

Application security in your code

Protect your application with Arcjet

Get rules that ship in the same pull request as the feature they protect.