Skip to content

Agent guard remote policies

Agent guard remote policies let authorized site members, including members of a security team, change the policy applied to a guarded action without changing the tool implementation. Developers own the enforcement point and map application values into the policy contract; authorized site members can publish the policy selected by its label.

This page covers Agent guard remote policies for labeled guard() actions and tool calls. They are separate from Request Remote rules, which are site-scoped rules merged with SDK request rules for HTTP requests handled by protect().

Remote policies complement, rather than replace, SDK rules configured in code. For the architecture, ownership trade-offs, and vendor examples, read Application-Native vs Remote Security Policies.

Choose application-native rules, remote policies, or both

Section titled “Choose application-native rules, remote policies, or both”

An application-native rule is usually code-native: its semantics and change and release lifecycle are owned by the application, and it is authored and tested with the application. An SDK integration alone does not provide that policy model. An SDK can put enforcement inside an application while all policy remains configured in a remote control plane. Choose the authoring model based on who must change the rule and which lifecycle governs it.

Application-native (code-native) SDK rulesAgent guard remote policies
Primary ownerApplication engineeringAuthorized site members, often security or platform teams
Rule authoringApplication source codeArcjet control plane
Change lifecycleCode review, tests, build, and deploymentPublish without an application deployment
Best fitApplication-specific logic and engineering invariantsIndependent policy changes and rapid response
Application responsibilityConfigure the rule and enforce the decisionDefine the label and typed inputs, then enforce the decision
Can be used togetherYes; submit SDK rules with the Guard callYes; the matching published policy is evaluated on that call

Keep stable application invariants in code and put policy that security must change independently in the remote control plane. Do not duplicate a rule in both places unless one is an intentional defense-in-depth backstop with clear precedence and separate tests.

For each guarded action, security and development teams agree on:

  1. A stable label, such as email.sent.
  2. Whether the policy requires an actor.
  3. Named inputs, with a type and SERVER or LOCAL exposure.
  4. Rules over those values, each in LIVE or DRY_RUN mode.

Every Guard call includes the label so Arcjet can apply the matching published policy.

label identifies the action boundary and selects its remote policy. Use a hardcoded, past-tense action name such as email.sent or refund.issued. Labels use lowercase letters, digits, dashes, and dots, must start and end with a letter or digit, and are limited to 256 bytes.

actor is an optional opaque string asserted by trusted application code. It can represent the authenticated user, service, or tenant responsible for the action. The SDK does not derive it from a request or tool arguments and Arcjet does not authenticate the value for your application.

Every policy input is named and explicitly constructed. Plain values are rejected; the SDK does not inspect the tool schema or automatically discover arguments.

ExposureTypeJavaScript / TypeScriptPythonExample value
SERVERStringpolicyInput.server.string(value)server_input.string(value)"customer@example.com"
SERVERBooleanpolicyInput.server.boolean(value)server_input.boolean(value)true
SERVERIntegerpolicyInput.server.integer(value)server_input.integer(value)3
SERVERNumberpolicyInput.server.number(value)server_input.number(value)49.95
SERVERString listpolicyInput.server.stringList(value)server_input.string_list(value)["a@example.com", "b@example.com"]
LOCALStringpolicyInput.local.string(value)local_input.string(value)"Email body to inspect"

For example, an email policy can receive a selected recipient, the trusted allow list to compare it with, and a message body that stays local:

inputs: {
recipient: policyInput.server.string(recipient),
allowed_recipients: policyInput.server.stringList(allowedRecipients),
body: policyInput.local.string(body),
}

SERVER and LOCAL describe where policy evaluates an input and whether its raw value is sent to Arcjet.

The typed value is sent to Arcjet for server-side policy evaluation. A call containing only server inputs does not need to fetch a local policy projection. Use server inputs only for values Arcjet is allowed to receive.

Today, LOCAL inputs are used by the sensitive-information rule, which operates on a LOCAL STRING. Its raw value remains in SDK memory. The SDK evaluates the downloaded policy projection locally and sends a domain-separated SHA-256 digest plus rule attestation. The attestation includes denied entity types and offsets, not the matched raw values.

Agent guard remote policies support the following rules:

RuleExecutionWhat it evaluates
Allowed string valuesServerA string against an allow list using exact or email-domain matching
Denied string valuesServerA string against a deny list using exact or email-domain matching
String lengthServerMinimum and maximum UTF-8 byte length
String-list membershipServerWhether one string is a member of a supplied string-list input
Prompt injectionServerOne named server string
Sensitive informationLocalOne named local string with allowed or denied entity types

Each rule can be LIVE, where a denial contributes to the final decision, or DRY_RUN, where it is evaluated and reported without blocking.

Generic URL allow/deny rules, command or shell rules, remote custom rules, and remote rate limits aren’t supported. SDK rules configured in code remain available and can be submitted alongside a remote policy.

Configure two SERVER inputs – a string named recipient and a string list named allowed_recipients – then add a string-list membership rule requiring the recipient to appear in the list.

inputs: ({ recipient }) => ({
recipient: policyInput.server.string(recipient),
allowed_recipients: policyInput.server.stringList(user.allowedRecipients),
})

Block sensitive data before it leaves the tool

Section titled “Block sensitive data before it leaves the tool”

Configure a LOCAL string input named body, then add a sensitive information rule with the entity types the tool must not send. The policy evaluates the raw body locally.

inputs: ({ body }) => ({
body: policyInput.local.string(body),
})

Check the instruction that triggered an action

Section titled “Check the instruction that triggered an action”

Configure a SERVER string input named incoming_message, then add a prompt injection rule over that input. Resolve the message from the application-owned agent run rather than from a model-generated tool argument.

inputs: (toolInput) => ({
incoming_message: policyInput.server.string(incomingMessage),
})

One email.sent policy can combine all three of the preceding rules. The integration maps the current agent run and generated tool arguments into the policy before the framework executes the tool. Framework wrappers such as guardTool take that slug as action. Direct guard() calls take it as label.

const sendEmail = guardTool(arcjet, sendEmailTool, {
action: "email.sent",
actor: currentUser.id,
inputs: ({ recipient, body }) => ({
recipient: policyInput.server.string(recipient),
allowed_recipients: policyInput.server.stringList(
currentUser.allowedRecipients,
),
body: policyInput.local.string(body),
incoming_message: policyInput.server.string(incomingMessage),
}),
});
const tools = { sendEmail };
const result = await generateText({
model: "openai/gpt-4o-mini",
prompt: incomingMessage,
tools,
toolsContext: aiToolsContext(createAgentContext(), tools),
});

The actor, allowed recipients, and incoming message come from application-owned context. Only recipient and body come from the model’s validated tool call. If any LIVE rule denies, the integration prevents sendEmailTool from executing.

Arcjet has two separate remote configuration systems:

Agent guard remote policiesRequest Remote rules
SDK callguard()protect()
ScopeOne action labelA site and its HTTP requests
ContextTrusted actor and explicit typed inputsRequest metadata and supported request signals
ConfigurationAgent guard policyRemote rules

Request Remote rules protect HTTP requests. Agent guard remote policies protect tool calls and other actions that use guard().

When a usable policy is available, temporary connectivity issues do not cause the SDK to discard it.

If policy evaluation is incomplete or unavailable, the direct client returns an observable failed-open decision. Framework wrappers fail closed by default. See Availability and fail behavior.