← all tools
◆ WAF, Bot & API Protection
Arcjet
How we built AI agent behavior drift analysis with Cloudflare Clef (2026-10-14)
commercial
The section your link pointed to isn't on Arcjet's page any more, so we've brought you to the top.
Arcjet is the AI agent runtime security platform that helps teams observe agent activity, enforce policies on tool calls, and audit decisions.
const answers = await env.AI.run("@cf/cloudflare/clef-flash", {
model: "clef-flash",
state,
questions,
});
policy := arcjet.GuardPolicyServerStringList([]string{"suspicious-host.example.com", "third-party-api.example.net"})
// Result written to signals.ip_threat.dest.risk_level — block if 'high' or 'critical'
package arcjet.guard
import rego.v1
deny contains "malicious-destination" if {
input.signals.ip_threat.dest.risk_level in {"high", "critical"}
}
package arcjet.guard
import rego.v1
allowed := {"docs.arcjet.com", "github.com", "raw.githubusercontent.com"}
deny contains "malicious-destination" if {
some a in input.signals.ip_threat.dest.assessments
a.risk_level in {"high", "critical"}
not a.host in allowed
}
package arcjet.guard
import rego.v1
deny contains "botnet-destination" if {
some a in input.signals.ip_threat.dest.assessments
a.risk_level in {"high", "critical"}
"botnet" in a.activities
}
coding-agent.destination-threat
allowed = ["safe.internal.example.com"]
deny {
a := input.signals.ip_threat.dest.assessments[_]
a.risk_level in {"high", "critical"}
not a.host in allowed
}
{
"hooks": {
"PreToolUse": [{
"hooks": [{
"type": "command",
"command": "sh .muse/hooks/arcjet-hook.sh muse-code pre-tool-use",
"commandWindows": "powershell -NoProfile -File .muse/hooks/arcjet-hook.ps1 -Vendor muse-code -Event pre-tool-use",
"timeout": 10
}]
}],
"UserPromptSubmit": [{
"hooks": [{
"type": "command",
"command": "sh .muse/hooks/arcjet-hook.sh muse-code user-prompt-submit",
"commandWindows": "powershell -NoProfile -File .muse/hooks/arcjet-hook.ps1 -Vendor muse-code -Event user-prompt-submit",
"timeout": 10
}]
}]
}
}
echo 'ajkey_xxxxxxxxxxxxxxxx' > ~/.config/muse/arcjet.key
chmod 600 ~/.config/muse/arcjet.key
echo '{"tool":"bash","input":{"command":"ls"}}' | sh .muse/hooks/arcjet-hook.sh muse-code pre-tool-use
from arcjet.guard.google_adk import guard_plugin, google_adk_context, launch_arcjet
from arcjet import DetectPromptInjection, LocalDetectSensitiveInfo, TokenBucket
aj = launch_arcjet(
rules=[
TokenBucket(refill_rate=10, interval_seconds=60, max_tokens=100),
DetectPromptInjection(),
LocalDetectSensitiveInfo(),
]
)
app_context = {"session_id": conversation_id}
plugin = guard_plugin(
aj,
session_id=lambda call: google_adk_context(app_context).get("correlation_id"),
on_guard_error="deny",
)
runner = Runner(app_name="my-agent", agent=agent, plugins=[plugin])
from arcjet.guard.google_adk import guard_tool, launch_arcjet
from arcjet import DetectPromptInjection, TokenBucket
aj = launch_arcjet(rules=[DetectPromptInjection(), TokenBucket(refill_rate=5, interval_seconds=60, max_tokens=50)])
agent = LlmAgent(
name="order-agent",
before_tool_callback=guard_tool(
aj,
action=lambda call: f"{call['tool_name']}.invoked",
session_id=lambda call: conversation_id,
on_guard_error="deny",
),
)
import { launchArcjet, tokenBucket, localDetectSensitiveInfo } from "@arcjet/guard";
import { guardHooks } from "@arcjet/guard/cloudflare-think/v0";
import { Think } from "@cloudflare/think";
import { tool } from "ai";
import { z } from "zod";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 10, intervalSeconds: 60, maxTokens: 20 });
const detectPii = localDetectSensitiveInfo();
const inputSchema = z.object({ orderNumber: z.string(), note: z.string() });
const hooks = guardHooks(arcjet, {
action: "order.looked-up",
onGuardError: "deny",
rules: ({ toolName, input }) => {
if (toolName !== "lookup_order") return [];
const { orderNumber, note } = inputSchema.parse(input);
return [lookupLimit({ key: orderNumber, requested: 1 }), detectPii(note)];
},
});
export class OrderAgent extends Think<Env> {
beforeToolCall = hooks.beforeToolCall;
}
import { launchArcjet, detectPromptInjection } from "@arcjet/guard";
import { cloudflareThinkContext } from "@arcjet/guard/cloudflare-think/v0";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const inbound = detectPromptInjection();
async function screenInbound(conversationId: string, userText: string) {
const appContext = { sessionId: conversationId };
const decision = await arcjet.guard({
label: "message.received",
rules: [inbound(userText)],
...cloudflareThinkContext({ context: appContext }),
});
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) {
throw new Error("message blocked");
}
}
import arcjet.guard.google_adk as aj_guard
agent = Agent(
...,
before_tool_callback=aj_guard.guard_plugin,
)
pip install "arcjet[google-adk]"
npm i @arcjet/guard @cloudflare/think
const decision = await aj.protect(req);
if (decision.hasFailedOpen()) {
console.warn('Remote policy label not matched — guard skipped, request allowed by default');
}
guarded, err := agentframework.GuardTool(guard, issueRefund, agentframework.ToolPolicy{
Action: "refund.issued",
Actor: func(ctx context.Context, args json.RawMessage) (string, error) {
return userID, nil
},
OnDeny: func(d arcjet.GuardDecision) any {
return refundResult{Status: "refused", Reason: string(d.Reason)}
},
})
screen, err := agentframework.GuardMiddleware(guard, agentframework.MiddlewareConfig{
Inbound: &agentframework.InboundPolicy{
Action: "message.received",
Rules: func(_ context.Context, text string) ([]arcjet.GuardRuleInput, error) {
return []arcjet.GuardRuleInput{promptScan.Text(text)}, nil
},
},
Tools: func(t tool.Tool) (agentframework.ToolPolicy, bool) {
switch t.Name() {
case "issue_refund":
return agentframework.ToolPolicy{Action: "refund.issued", Actor: actor}, true
case "lookup_order":
return agentframework.ToolPolicy{Action: "order.looked-up", Actor: actor}, true
}
return agentframework.ToolPolicy{}, false
},
})
ctx = arcjet.ContextWithCorrelationID(ctx, conversationID)
resp, err := a.RunText(ctx, prompt).Collect()
{
"hooks": {
"PreToolUse": [{"hooks": [{"type": "command", "command": "sh .codex/hooks/arcjet-hook.sh codex pre-tool-use", "commandWindows": "powershell -NoProfile -File .codex/hooks/arcjet-hook.ps1 -Vendor codex -Event pre-tool-use", "timeout": 10}]}],
"UserPromptSubmit": [{"hooks": [{"type": "command", "command": "sh .codex/hooks/arcjet-hook.sh codex user-prompt-submit", "commandWindows": "powershell -NoProfile -File .codex/hooks/arcjet-hook.ps1 -Vendor codex -Event user-prompt-submit", "timeout": 10}]}]
}
}
allow_managed_hooks_only = true
[features]
hooks = true
[hooks]
managed_dir = "/etc/codex/hooks"
windows_managed_dir = 'C:\ProgramData\OpenAI\Codex\hooks'
[[hooks.PreToolUse]]
[[hooks.PreToolUse.hooks]]
type = "command"
command = "sh /etc/codex/hooks/arcjet-hook.sh codex pre-tool-use"
command_windows = 'powershell -NoProfile -File C:\ProgramData\OpenAI\Codex\hooks\arcjet-hook.ps1 -Vendor codex -Event pre-tool-use'
timeout = 10
echo '{"tool":"exec_command","input":{"cmd":"ls"}}' | ARCJET_KEY=<your-key> sh .codex/hooks/arcjet-hook.sh codex pre-tool-use
{
"version": "1",
"hooks": {
"preToolUse": [
{
"command": "sh .cursor/hooks/arcjet-hook.sh cursor pre-tool-use",
"timeout": 5000,
"failClosed": true
}
],
"beforeSubmitPrompt": [
{
"command": "sh .cursor/hooks/arcjet-hook.sh cursor user-prompt-submit",
"timeout": 5000,
"failClosed": true
}
],
"postToolUse": [
{
"command": "sh .cursor/hooks/arcjet-hook.sh cursor post-tool-use",
"timeout": 5000
}
]
}
}
package arcjet.guard
import rego.v1
deny contains "injection-on-destructive-tool" if {
input.signals.prompt_injection.incoming_message.detected
input.values.destructive
}
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY });
const refund = guardTool(arcjet, refundTool, {
action: "order.refunded",
actor: user.id,
inputs: ({ amount }) => ({
amount: policyInput.server.number(amount),
role: policyInput.server.string(user.role),
}),
});
package arcjet.guard
deny contains "external-recipient" if {
parts := split(input.values.recipient, "@")
count(parts) == 2
not lower(parts[1]) in input.values.team_domains
}
deny contains "bulk-attachments" if {
input.values.attachments > 10
}
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const refund = guardTool(arcjet, refundTool, {
action: "order.refunded",
actor: user.id,
inputs: ({ amount }) => ({
amount: policyInput.server.number(amount),
role: policyInput.server.string(user.role),
}),
});
package arcjet.guard
import rego.v1
deny contains "card-number-to-external" if {
"CREDIT_CARD_NUMBER" in input.signals.sensitive_info.body_scan.entities
not lower(split(input.values.recipient, "@")[1]) in input.values.team_domains
}
package arcjet.guard
import rego.v1
deny contains "injection-on-destructive-tool" if {
input.signals.prompt_injection.incoming_message.detected
input.values.destructive
}
package arcjet.guard
import rego.v1
# Rule declared with mode: DRY_RUN in the policy config
deny contains "bulk-attachments" if {
input.values.attachments > 5
}
export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_ENDPOINT=https://decide.arcjet.com
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer ARCJET_KEY"
export OTEL_LOG_USER_PROMPTS=1
export OTEL_LOG_TOOL_DETAILS=1
{
"env": {
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
"OTEL_LOGS_EXPORTER": "otlp",
"OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
"OTEL_EXPORTER_OTLP_ENDPOINT": "https://decide.arcjet.com",
"OTEL_EXPORTER_OTLP_HEADERS": "Authorization=Bearer ARCJET_KEY",
"OTEL_LOG_USER_PROMPTS": "1",
"OTEL_LOG_TOOL_DETAILS": "1"
}
}
import { defineChannel, POST } from 'eve/channels';
import { detectPromptInjection } from '@arcjet/guard';
import { guardInbound } from '@arcjet/guard/vercel-eve/v0';
import { arcjet } from '../arcjet.js';
export default defineChannel({
routes: [
POST('/webhook', async (req, args) => {
const { message, conversationId } = await req.json();
const verdict = await guardInbound(arcjet, message, {
rules: [detectPromptInjection()(message)],
action: 'message.received',
correlationId: conversationId,
});
if (!verdict.allowed) {
return new Response(JSON.stringify({ error: verdict.message }), {
status: verdict.outcome === 'UNAVAILABLE' ? 503 : 403,
});
}
const session = await args.from(conversationId).send(message, { auth: null });
return new Response(JSON.stringify({ sessionId: session.id }));
}),
],
});
import { defineOpenAPIConnection } from 'eve/connections';
import { tokenBucket } from '@arcjet/guard';
import { guardApproval } from '@arcjet/guard/vercel-eve/v0';
import { arcjet } from '../arcjet.js';
const sessionLimit = tokenBucket({ bucket: 'weather-session', refillRate: 10, intervalSeconds: 60, maxTokens: 10 });
const approverLimit = tokenBucket({ bucket: 'weather-approver', refillRate: 5, intervalSeconds: 60, maxTokens: 5 });
export default defineOpenAPIConnection({
description: 'Weather API',
spec: 'https://api.example.com/openapi.json',
approval: guardApproval(arcjet, {
action: 'weather.fetched',
rules: (ctx) => [sessionLimit({ key: ctx.session.id, requested: 1 })],
onAllow: 'user-approval',
response: {
action: 'weather.approved',
rules: (ctx) => [approverLimit({ key: ctx.responder.principalId, requested: 1 })],
},
}),
operations: { allow: ['GetForecast'] },
});
mkdir -p ~/.claude/skills
cp -r node_modules/@arcjet/guard/skills/integrate-arcjet-guard-eve ~/.claude/skills/
# Then inside Claude Code:
# /integrate-arcjet-guard-eve
import { launchArcjet, tokenBucket, policyInput } from '@arcjet/guard';
import { guardTool, aiToolsContext, createAgentContext } from '@arcjet/guard/vercel-ai/v7';
import { generateText, stepCountIs, tool } from 'ai';
import { z } from 'zod';
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const emailLimit = tokenBucket({ bucket: 'email', refillRate: 5, intervalSeconds: 60, maxTokens: 10 });
const sendEmail = guardTool(
arcjet,
tool({
description: 'Send an email',
inputSchema: z.object({ recipient: z.string().email(), body: z.string() }),
execute: ({ recipient, body }) => emailProvider.send({ to: recipient, body }),
}),
{
action: 'email.sent',
actor: (_, ctx) => ctx.actor,
rules: (input) => [emailLimit({ key: input.recipient, requested: 1 })],
inputs: ({ recipient, body }) => ({
recipient: policyInput.server.string(recipient),
body: policyInput.local.string(body),
}),
onGuardError: 'deny',
},
);
const context = createAgentContext({ correlationId: userId });
return generateText({
model: 'openai/gpt-4o-mini',
prompt,
tools: { sendEmail },
toolsContext: aiToolsContext(context, { sendEmail }),
stopWhen: stepCountIs(10),
});
import { launchArcjet, detectPromptInjection } from '@arcjet/guard';
import { createAgentContext } from '@arcjet/guard/vercel-ai/v7';
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const context = createAgentContext({ correlationId: conversationId });
const inbound = detectPromptInjection();
const decision = await arcjet.guard({
label: 'message.received',
rules: [inbound(prompt)],
correlationId: context.correlationId,
});
if (decision.conclusion === 'DENY' || decision.hasFailedOpen()) {
throw new Error('message blocked');
}
npm install @arcjet/guard ai @ai-sdk/provider-utils
mkdir -p ~/.claude/skills
cp -r node_modules/@arcjet/guard/skills/integrate-arcjet-guard-agents ~/.claude/skills/
import { launchArcjet, detectPromptInjection, localDetectSensitiveInfo, tokenBucket } from "@arcjet/guard";
import { guardMiddleware, tanstackAiContext } from "@arcjet/guard/tanstack-ai/v0";
import { chat, toolDefinition } from "@tanstack/ai";
import { z } from "zod";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 10, intervalSeconds: 60, maxTokens: 10 });
const detectPii = localDetectSensitiveInfo({ deny: ["EMAIL", "PHONE_NUMBER", "IP_ADDRESS", "CREDIT_CARD_NUMBER"] });
const inbound = detectPromptInjection();
const appContext = { sessionId: conversationId };
// Screen inbound text before chat()
const decision = await arcjet.guard({
label: "message.received",
rules: [inbound(userText)],
...tanstackAiContext({ context: appContext }),
});
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) {
throw new Error("message blocked");
}
// Gate tool calls with guardMiddleware
return chat({
adapter,
messages: [{ role: "user", content: userText }],
tools: [lookupOrder],
context: appContext,
middleware: [
guardMiddleware(arcjet, {
action: "order.looked-up",
sessionId: conversationId,
onGuardError: "deny",
rules: ({ toolName, input }) => {
if (toolName !== "lookup_order") return [];
const { orderNumber, note } = lookupOrderInput.parse(input);
return [lookupLimit({ key: orderNumber, requested: 1 }), detectPii(note)];
},
}),
],
});
import { guardMiddleware } from "@arcjet/guard/tanstack-ai/v0";
import { chat } from "@tanstack/ai";
import { arcjet } from "./arcjet.js";
chat({
adapter,
messages: [{ role: "user", content: userText }],
tools: [lookupOrder],
context: { sessionId: conversationId },
middleware: [
guardMiddleware(arcjet, {
sessionId: conversationId,
onDeny: "abort",
}),
],
});
from arcjet.guard.strands_agents import guard_tool
from arcjet.guard import launch_arcjet
from strands import tool
arcjet = launch_arcjet(key=ARCJET_KEY)
@tool
def send_email(to: str, body: str) -> str:
# side-effecting work here
...
guarded_send_email = guard_tool(
guard=arcjet,
tool=send_email,
action="email.sent",
on_guard_error="deny",
)
import { launchArcjet, guardHooks } from '@arcjet/guard/strands-agents/v1';
import { Agent } from '@strands-agents/sdk';
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const agent = new Agent({
tools: [myMcpTool, myOtherTool],
plugins: [
guardHooks(arcjet, {
action: ({ toolName }) => `${toolName}.invoked`,
onGuardError: 'deny',
}),
],
});
await agent.invoke('Send a summary email to the team');
import { launchArcjet } from "@arcjet/guard";
import { guardTool } from "@arcjet/guard/openai-agents/v0";
import { tool } from "@openai/agents";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const sendEmail = tool({
name: "send_email",
execute: async ({ to, body }) => { /* sends email */ },
});
export const guardedSendEmail = guardTool(arcjet, sendEmail, {
action: "email.sent",
onGuardError: "deny",
});
from agents import Runner
from arcjet.guard import DetectPromptInjection, launch_arcjet
from arcjet.guard.openai_agents import openai_agents_context
arcjet = launch_arcjet(key=ARCJET_KEY)
inbound = DetectPromptInjection()
app_context = {"session_id": conversation_id}
derived = openai_agents_context(app_context)
decision = await arcjet.guard(
label="message.received",
rules=[inbound(user_text)],
correlation_id=derived.correlation_id,
)
if decision.conclusion == "DENY" or decision.has_failed_open():
raise RuntimeError("message blocked")
await Runner.run(agent, user_text, context=app_context)
import { Agent } from '@mastra/core/agent';
import { guardProcessor } from '@arcjet/guard/mastra/v1';
import { detectPromptInjection } from '@arcjet/guard';
import { arcjet } from './arcjet.js';
const inbound = guardProcessor(arcjet, {
action: 'message.received',
rules: ({ text }) => [detectPromptInjection()(text)],
});
const outbound = guardProcessor(arcjet, {
action: 'message.completed',
rules: ({ text }) => [detectPromptInjection()(text)],
});
export const agent = new Agent({
id: 'support-agent',
name: 'support-agent',
instructions: 'Help the user.',
model: 'openai/gpt-4o',
inputProcessors: [inbound],
outputProcessors: [outbound],
});
import { createTool } from '@mastra/core/tools';
import { z } from 'zod';
import { guardTool } from '@arcjet/guard/mastra/v1';
import { tokenBucket, localDetectSensitiveInfo } from '@arcjet/guard';
import { arcjet } from './arcjet.js';
const lookupLimit = tokenBucket({ bucket: 'lookups', refillRate: 10, intervalSeconds: 60, maxTokens: 20 });
const detectPii = localDetectSensitiveInfo({ deny: ['EMAIL', 'PHONE_NUMBER', 'IP_ADDRESS', 'CREDIT_CARD_NUMBER'] });
export const lookupOrder = guardTool(
arcjet,
createTool({
id: 'lookup-order',
description: 'Look up an order by ID',
inputSchema: z.object({ orderId: z.string(), note: z.string() }),
async execute({ orderId, note }) { return { orderId, note, status: 'shipped' }; },
}),
{
action: 'order.looked-up',
rules: (input) => [lookupLimit({ key: input.orderId, requested: 1 }), detectPii(input.note)],
},
);
import { guardHooks } from '@arcjet/guard/mastra/v1';
import { tokenBucket } from '@arcjet/guard';
import { arcjet } from './arcjet.js';
const mcpLimit = tokenBucket({ bucket: 'mcp-access', refillRate: 5, intervalSeconds: 60, maxTokens: 10 });
export const hooks = guardHooks(arcjet, {
action: ({ toolName }) => `${toolName}.invoked`,
rules: ({ toolName }) => [mcpLimit({ key: toolName, requested: 1 })],
});
// Pass hooks to the Agent constructor:
// new Agent({ ..., ...hooks })
import { tool } from "@langchain/core/tools";
import { z } from "zod";
import { guardTool, langgraphAgentContext } from "@arcjet/guard/langgraph/v1";
import { tokenBucket, localDetectSensitiveInfo } from "@arcjet/guard";
import { arcjet } from "./arcjet.js";
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 5, intervalSeconds: 60, maxTokens: 10 });
const detectPii = localDetectSensitiveInfo({ deny: ["EMAIL", "PHONE_NUMBER", "CREDIT_CARD_NUMBER"] });
export const lookupOrder = guardTool(
arcjet,
tool(
async ({ orderNumber, note }) => ({ orderNumber, note, status: "shipped" }),
{
name: "lookup_order",
description: "Look up an order by number",
schema: z.object({ orderNumber: z.string(), note: z.string() }),
},
),
{
action: "order.looked-up",
rules: (input) => [lookupLimit({ key: input.orderNumber, requested: 1 }), detectPii(input.note)],
},
);
import { ToolNode } from "@langchain/langgraph/prebuilt";
import { guardToolNode } from "@arcjet/guard/langgraph/v1";
import { tokenBucket } from "@arcjet/guard";
import { arcjet } from "./arcjet.js";
const mcpLimit = tokenBucket({ bucket: "mcp-access", refillRate: 10, intervalSeconds: 60, maxTokens: 20 });
export const tools = guardToolNode(
arcjet,
new ToolNode([lookupOrder, ...mcpTools]),
{
action: ({ toolName }) => `${toolName}.invoked`,
rules: ({ toolName }) => [mcpLimit({ key: toolName, requested: 1 })],
},
);
// Then: graph.addNode("tools", tools);
import { detectPromptInjection } from "@arcjet/guard";
import { langgraphAgentContext } from "@arcjet/guard/langgraph/v1";
import { arcjet } from "./arcjet.js";
const config = { configurable: { thread_id: conversationId } };
const inbound = detectPromptInjection();
const decision = await arcjet.guard({
label: "message.received",
rules: [inbound(userText)],
...langgraphAgentContext(config),
});
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) {
throw new Error("message blocked");
}
await graph.invoke({ messages: [{ role: "user", content: userText }] }, config);
import { guardTool } from '@arcjet/guard/langchain/v1';
import { tool } from '@langchain/core/tools';
import { z } from 'zod';
import { arcjet } from './arcjet.js';
const sendEmail = tool(
async ({ to, body }) => { /* send email */ },
{ name: 'send_email', schema: z.object({ to: z.string(), body: z.string() }) }
);
const guardedSendEmail = guardTool(arcjet, sendEmail, {
action: 'email.sent',
});
from arcjet.guard import launch_arcjet
from arcjet.guard.langchain import ArcjetMiddleware, ToolPolicy, guard_tool
from langchain_openai import ChatOpenAI
arcjet = launch_arcjet(key=ARCJET_KEY)
middleware = ArcjetMiddleware(
guard=arcjet,
policies={
'send_email': ToolPolicy(action='email.sent'),
'query_db': ToolPolicy(action='database.queried'),
},
tools=[send_email_tool, query_db_tool],
)
agent = create_agent(model=ChatOpenAI(model='gpt-4o'), tools=[send_email_tool, query_db_tool], middleware=middleware)
import { launchArcjet, detectPromptInjection, localDetectSensitiveInfo, tokenBucket } from "@arcjet/guard";
import { guardPlugin, googleAdkContext } from "@arcjet/guard/google-adk/v2";
import { FunctionTool, InMemoryRunner, LlmAgent } from "@google/adk";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 10, intervalSeconds: 60, maxTokens: 100 });
const detectPii = localDetectSensitiveInfo({ deny: ["EMAIL", "PHONE_NUMBER", "CREDIT_CARD_NUMBER"] });
const inbound = detectPromptInjection();
// Screen user text before runAsync
const appContext = { sessionId: conversationId };
const decision = await arcjet.guard({
label: "message.received",
rules: [inbound(userText)],
...googleAdkContext(appContext),
});
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) throw new Error("message blocked");
// Run with guardPlugin gating all tool calls
const runner = new InMemoryRunner({
agent,
appName: "orders",
plugins: [
guardPlugin(arcjet, {
action: "order.looked-up",
sessionId: conversationId,
onGuardError: "deny",
rules: ({ toolName, input }) => [
lookupLimit({ key: input.orderNumber, requested: 1 }),
detectPii(input.note),
],
}),
],
});
await runner.runAsync({ userId: conversationId, sessionId: conversationId, newMessage: { parts: [{ text: userText }] } });
import { launchArcjet, tokenBucket, localDetectSensitiveInfo } from '@arcjet/guard';
import { guardTool } from '@arcjet/guard/genkit/v1';
import { genkit, z } from 'genkit';
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const ai = genkit({ /* model plugin */ });
const lookupLimit = tokenBucket({ bucket: 'lookups', refillRate: 10, intervalSeconds: 60, maxTokens: 10 });
const detectPii = localDetectSensitiveInfo({ deny: ['EMAIL', 'PHONE_NUMBER', 'IP_ADDRESS', 'CREDIT_CARD_NUMBER'] });
const lookupOrder = guardTool(
arcjet,
ai.defineTool(
{ name: 'lookup_order', description: 'Look up an order by number', inputSchema: z.object({ orderNumber: z.string(), note: z.string() }) },
async ({ orderNumber, note }) => ({ orderNumber, note, status: 'shipped' })
),
{
action: 'order.looked-up',
onGuardError: 'deny',
rules: (input) => [lookupLimit({ key: input.orderNumber, requested: 1 }), detectPii(input.note)],
}
);
import { detectPromptInjection } from '@arcjet/guard';
import { genkitContext } from '@arcjet/guard/genkit/v1';
import { arcjet } from './arcjet.js';
const inbound = detectPromptInjection();
const appContext = { sessionId: conversationId };
const decision = await arcjet.guard({
label: 'message.received',
rules: [inbound(userText)],
...genkitContext({ context: appContext }),
});
if (decision.conclusion === 'DENY' || decision.hasFailedOpen()) {
throw new Error('message blocked');
}
const response = await ai.generate({
prompt: userText,
tools: [lookupOrder],
use: [guardMiddleware(arcjet, { sessionId: conversationId })],
context: appContext,
});
from arcjet.guard import LocalDetectSensitiveInfo, TokenBucket, launch_arcjet_sync
from arcjet.guard.crewai import free_text_arguments, register_arcjet_hooks
arcjet = launch_arcjet_sync(key=ARCJET_KEY)
lookup_limit = TokenBucket(refill_rate=1, interval_seconds=60, max_tokens=10, bucket="lookups")
detect_pii = LocalDetectSensitiveInfo(deny=["EMAIL", "PHONE_NUMBER", "CREDIT_CARD_NUMBER"])
handle = register_arcjet_hooks(
guard=arcjet,
tools=["lookup_order"],
action="order.looked-up",
rules=lambda arguments, _ctx: [
lookup_limit(key="orders", requested=1),
detect_pii(free_text_arguments(arguments)["note"]),
],
on_guard_error="deny",
)
from arcjet.guard import DetectPromptInjection, launch_arcjet_sync
arcjet = launch_arcjet_sync(key=ARCJET_KEY)
inbound = DetectPromptInjection()
decision = arcjet.guard(
label="message.received",
rules=[inbound(user_text)],
)
if decision.conclusion == "DENY" or decision.has_failed_open():
raise RuntimeError("message blocked")
crew.kickoff(inputs={"request": user_text})
from arcjet.guard import TokenBucket, launch_arcjet_sync
from arcjet.guard.crewai import guard_tool
arcjet = launch_arcjet_sync(key=ARCJET_KEY)
lookup_limit = TokenBucket(refill_rate=1, interval_seconds=60, max_tokens=10, bucket="lookups")
guarded_lookup = guard_tool(
guard=arcjet,
tool=lookup_order,
action="order.looked-up",
rules=[lookup_limit(key="orders", requested=1)],
on_guard_error="deny",
)
guarded_lookup.run({"order_id": "ORD-42", "note": "priority customer"})
import Anthropic from "@anthropic-ai/sdk";
import { detectPromptInjection } from "@arcjet/guard";
import { claudeManagedAgentsContext, guardEvents } from "@arcjet/guard/claude-managed-agents/v0";
import { arcjet } from "./arcjet.js";
const client = new Anthropic();
export async function sendTurn(sessionId: string, conversationId: string, userText: string) {
const inbound = await guardEvents(
arcjet,
{
events: [{ type: "user.message", content: [{ type: "text", text: userText }] }],
inbound: {
action: "message.received",
rules: ({ text }) => [detectPromptInjection()(text)],
},
context: claudeManagedAgentsContext({ correlationId: conversationId }),
},
(body) => client.beta.sessions.events.send(sessionId, body)
);
if (!inbound.allowed) {
return inbound.message;
}
}
import { guardCustomTool, claudeManagedAgentsContext } from "@arcjet/guard/claude-managed-agents/v0";
import { arcjet } from "./arcjet.js";
const guardedSendEmail = guardCustomTool(arcjet, {
action: "email.sent",
context: claudeManagedAgentsContext({ correlationId: conversationId }),
onGuardError: "deny",
run: async (event) => {
// your custom tool logic here
},
});
import { query } from "@anthropic-ai/claude-agent-sdk";
import { randomUUID } from "node:crypto";
import { guardHooks } from "@arcjet/guard/claude-agent-sdk/v0";
import { detectPromptInjection } from "@arcjet/guard";
import { arcjet } from "./arcjet.js";
const sessionId = conversationId ?? randomUUID();
for await (const message of query({
prompt: userText,
options: {
sessionId,
hooks: guardHooks(arcjet, {
sessionId,
inbound: {
action: "message.received",
rules: ({ prompt }) => [detectPromptInjection()(prompt)],
},
}),
},
})) {
void message;
}
from claude_agent_sdk import ClaudeAgentOptions, query
from arcjet.guard import DetectPromptInjection, launch_arcjet
from arcjet.guard.claude_agent_sdk import guard_hooks
arcjet = launch_arcjet(key=ARCJET_KEY)
inbound = DetectPromptInjection()
options = ClaudeAgentOptions(
session_id=conversation_id,
hooks=guard_hooks(
guard=arcjet,
session_id=conversation_id,
on_guard_error="allow",
inbound={
"action": "message.received",
"rules": lambda arguments: [inbound(arguments["prompt"])],
},
),
)
async for message in query(prompt=user_text, options=options):
pass
mkdir -p ~/.claude/skills
cp -r node_modules/@arcjet/guard/skills/integrate-arcjet-guard-claude-agent-sdk ~/.claude/skills/
# Then in Claude Code:
/integrate-arcjet-guard-claude-agent-sdk
package arcjet.guard
import rego.v1
deny contains "piped-installer" if {
input.values.tool_kind == "shell"
contains(input.values.command, "|")
some fetcher in input.values.command_tokens
lower(fetcher) in {"curl", "wget"}
some shell in input.values.command_tokens
lower(shell) in {"sh", "bash", "zsh", "dash"}
}
package arcjet.guard
import rego.v1
deny contains "unlisted-domain" if {
input.values.tool_kind == "web"
some host in input.values.domains
not host in {"docs.arcjet.com", "github.com", "raw.githubusercontent.com"}
}
package arcjet.guard
import rego.v1
prompt_events := {
"user-prompt-submit",
"user-prompt-expansion",
"user-prompt-transformed"
}
deny contains "prompt-has-sensitive-info" if {
input.values.event in prompt_events
input.signals.sensitive_info.pii.detected
}
{
"version": "hooks",
"hooks": {
"preToolUse": [{
"type": "http",
"url": "https://decide.arcjet.com/v1/agent-hooks/copilot?event=pre-tool-use&surface=cli",
"headers": {
"Authorization": "Bearer $ARCJET_KEY",
"X-Arcjet-Principal": "$USER"
},
"allowedEnvVars": ["ARCJET_KEY", "USER"],
"timeoutSec": 5
}]
}
}
{
"hooks": {
"PreToolUse": [{
"hooks": [{
"type": "http",
"url": "https://decide.arcjet.com/v1/agent-hooks/claude-code?event=pre-tool-use&surface=cli",
"headers": {
"Authorization": "Bearer $ARCJET_KEY",
"X-Arcjet-Principal": "$USER"
},
"allowedEnvVars": ["ARCJET_KEY", "USER"],
"timeout": 5
}]
}]
},
"env": { "ARCJET_KEY": "ajkey_REPLACE_ME" },
"allowedHttpHookUrls": ["https://decide.arcjet.com/*"],
"allowManagedHooksOnly": true,
"permissions": { "disableBypassPermissionsMode": "disable" }
}
claude doctor
# Then inside a Claude Code session:
/status
/hooks
claude --debug-file /tmp/arcjet-hooks-debug.log
go get github.com/arcjet/arcjet-go@v1.0.0-rc.2
import { defineChannel, POST } from 'eve/channels';
import { detectPromptInjection } from '@arcjet/guard';
import { guardInbound } from '@arcjet/guard/vercel-eve/v0';
import { arcjet } from '../arcjet.js';
export default defineChannel({
routes: [
POST('/webhook', async (req, args) => {
const { message, conversationId } = await req.json();
const verdict = await guardInbound(arcjet, message, {
rules: [detectPromptInjection()(message)],
action: 'message.received',
correlationId: conversationId,
});
if (!verdict.allowed) {
return new Response(JSON.stringify({ error: verdict.message }), {
status: verdict.outcome === 'UNAVAILABLE' ? 503 : 403,
});
}
const session = await args.from(conversationId).send(message, { auth: null });
return new Response(JSON.stringify({ sessionId: session.id }));
}),
],
});
import { defineTool } from 'eve/tools';
import { z } from 'zod';
import { tokenBucket } from '@arcjet/guard';
import { guardTool } from '@arcjet/guard/vercel-eve/v0';
import { arcjet } from '../arcjet.js';
const lookupLimit = tokenBucket({ bucket: 'lookups', refillRate: 10, intervalSeconds: 60, maxTokens: 10 });
export default guardTool(
arcjet,
defineTool({
description: 'Look up an order by ID',
inputSchema: z.object({ orderId: z.string() }),
async execute(input) {
return { orderId: input.orderId, status: 'shipped' };
},
}),
{
action: 'order.looked-up',
rules: (input) => [lookupLimit({ key: input.orderId, requested: 1 })],
},
);
import { defineOpenAPIConnection } from 'eve/connections';
import { tokenBucket } from '@arcjet/guard';
import { guardApproval } from '@arcjet/guard/vercel-eve/v0';
import { arcjet } from '../arcjet.js';
const sessionLimit = tokenBucket({ bucket: 'weather-session', refillRate: 5, intervalSeconds: 60, maxTokens: 5 });
const approverLimit = tokenBucket({ bucket: 'weather-approver', refillRate: 5, intervalSeconds: 60, maxTokens: 5 });
export default defineOpenAPIConnection({
description: 'Weather API',
spec: 'https://api.example.com/openapi.json',
approval: guardApproval(arcjet, {
action: 'weather.fetched',
rules: (ctx) => [sessionLimit({ key: ctx.session.id, requested: 1 })],
onAllow: 'user-approval',
response: {
action: 'weather.approved',
rules: (ctx) => [approverLimit({ key: ctx.responder.principalId, requested: 1 })],
},
}),
operations: { allow: ['GetForecast'] },
});
import { launchArcjet, tokenBucket, policyInput } from '@arcjet/guard';
import { guardTool, aiToolsContext, createAgentContext } from '@arcjet/guard/vercel-ai/v7';
import { generateText, stepCountIs, tool } from 'ai';
import { z } from 'zod';
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const emailLimit = tokenBucket({ bucket: 'email', refillRate: 5, intervalSeconds: 3600, maxTokens: 10 });
const sendEmail = guardTool(
arcjet,
tool({
description: 'Send an email',
inputSchema: z.object({ recipient: z.string().email(), body: z.string() }),
execute: ({ recipient, body }) => emailProvider.send({ to: recipient, body }),
}),
{
action: 'email.sent',
actor: user.id,
rules: () => [emailLimit({ key: user.id, requested: 1 })],
inputs: ({ recipient, body }) => ({
recipient: policyInput.server.string(recipient),
body: policyInput.local.string(body),
}),
},
);
const context = createAgentContext({ correlationId: user.id });
return generateText({
model: 'openai/gpt-4o-mini',
prompt,
tools: { sendEmail },
toolsContext: aiToolsContext(context, { sendEmail }),
stopWhen: stepCountIs(5),
});
import { guardAction, ArcjetDeniedError, createAgentContext, securityMetadata } from '@arcjet/guard/vercel-ai/v7';
import { tokenBucket } from '@arcjet/guard';
import { arcjet } from './arcjet.js';
const submitLimit = tokenBucket({ bucket: 'reviews', refillRate: 2, intervalSeconds: 3600, maxTokens: 5 });
const context = createAgentContext({ correlationId: conversationId });
try {
await guardAction(
arcjet,
context,
{
action: 'review.submitted',
rules: [submitLimit({ key: review.repoId, requested: 1 })],
metadata: securityMetadata({ destination: 'github', reversibility: 'compensable' }),
},
() => github.pulls.createReview({ owner, repo, pull_number: prNumber, body: reviewText }),
);
} catch (error) {
if (error instanceof ArcjetDeniedError) {
console.warn('denied:', error.decision.reason);
}
}
npm install @arcjet/guard ai @ai-sdk/provider-utils
mkdir -p ~/.claude/skills
cp -r node_modules/@arcjet/guard/skills/integrate-arcjet-guard-agents ~/.claude/skills/
# Then in Claude Code:
# /integrate-arcjet-guard-agents
import { launchArcjet, tokenBucket, localDetectSensitiveInfo } from "@arcjet/guard";
import { guardMiddleware } from "@arcjet/guard/tanstack-ai/v0";
import { chat } from "@tanstack/ai";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 10, intervalSeconds: 60, maxTokens: 10 });
const detectPii = localDetectSensitiveInfo({ deny: ["EMAIL", "PHONE_NUMBER", "CREDIT_CARD_NUMBER"] });
const stream = chat({
adapter,
messages: [{ role: "user", content: userText }],
tools: [lookupOrder],
context: { sessionId: conversationId },
middleware: [
guardMiddleware(arcjet, {
action: ({ toolName }) => `${toolName}.invoked`,
sessionId: conversationId,
onGuardError: "deny",
onDeny: "abort",
rules: ({ toolName, input }) =>
toolName === "lookup_order"
? [lookupLimit({ key: input.orderNumber, requested: 1 }), detectPii(input.note)]
: [],
})
],
});
import { detectPromptInjection } from "@arcjet/guard";
import { tanstackAiContext } from "@arcjet/guard/tanstack-ai/v0";
import { arcjet } from "./arcjet.js";
const inbound = detectPromptInjection();
const appContext = { sessionId: conversationId };
const decision = await arcjet.guard({
label: "message.received",
rules: [inbound(userText)],
...tanstackAiContext({ context: appContext }),
});
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) {
throw new Error("message blocked");
}
import { launchArcjet, detectPromptInjection, tokenBucket } from "@arcjet/guard";
import { guardTool } from "@arcjet/guard/strands-agents/v1";
import { Agent, tool } from "@strands-agents/sdk";
import { z } from "zod";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const sendEmail = guardTool(arcjet, tool({
description: "Send an email",
parameters: z.object({ to: z.string(), body: z.string() }),
callback: async ({ to, body }) => { /* send */ }
}), {
action: "email.sent",
rules: ({ to }) => [detectPromptInjection(), tokenBucket({ capacity: 10, refillRate: 1, interval: "1m" })]
});
const agent = new Agent({ tools: [sendEmail] });
from arcjet.guard import launch_arcjet
from arcjet.guard.strands_agents import guard_hooks
from strands import Agent
arcjet = launch_arcjet(key=ARCJET_KEY)
agent = Agent(
hooks=[guard_hooks(
guard=arcjet,
action="tool.invoked",
on_guard_error="deny"
)]
)
import { detectPromptInjection } from '@arcjet/guard';
import { openaiAgentsContext } from '@arcjet/guard/openai-agents/v0';
import { run } from '@openai/agents';
import { arcjet } from './arcjet.js';
const inbound = detectPromptInjection();
const appContext = { sessionId: conversationId };
const decision = await arcjet.guard({
label: 'message.received',
rules: [inbound(userText)],
...openaiAgentsContext({ context: appContext, conversationId })
});
if (decision.conclusion === 'DENY' || decision.hasFailedOpen()) {
throw new Error('message blocked');
}
await run(agent, userText, { context: appContext });
npm install @arcjet/guard @openai/agents
mkdir -p ~/.claude/skills
cp -r node_modules/@arcjet/guard/skills/integrate-arcjet-guard-openai-agents ~/.claude/skills/
# Then in Claude Code:
# /integrate-arcjet-guard-openai-agents
import { guardHooks, mastraAgentContext } from '@arcjet/guard/mastra/v1';
import { tokenBucket } from '@arcjet/guard';
import { RequestContext, MASTRA_THREAD_ID_KEY, MASTRA_RESOURCE_ID_KEY } from '@mastra/core/request-context';
import { arcjet } from './arcjet.js';
const mcpLimit = tokenBucket({ bucket: 'mcp-access', refillRate: 5, intervalSeconds: 10, maxTokens: 10 });
export const hooks = guardHooks(arcjet, {
action: ({ toolName }) => `${toolName}.invoked`,
rules: ({ toolName }) => [mcpLimit({ key: toolName, requested: 1 })],
});
// Correlate the turn to a conversation and user before calling generate()
const requestContext = new RequestContext();
requestContext.set(MASTRA_THREAD_ID_KEY, conversationId);
requestContext.set(MASTRA_RESOURCE_ID_KEY, userId);
await agent.generate(message, { requestContext });
import { detectPromptInjection } from "@arcjet/guard";
import { langgraphAgentContext } from "@arcjet/guard/langgraph/v1";
import { arcjet } from "./arcjet.js";
const config = { configurable: { thread_id: conversationId } };
const inbound = detectPromptInjection();
const decision = await arcjet.guard({
label: "message.received",
rules: [inbound(userText)],
...langgraphAgentContext(config)
});
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) {
throw new Error("message blocked");
}
await graph.invoke({ messages: [{ role: "user", content: userText }] }, config);
import { tool } from "@langchain/core/tools";
import { z } from "zod";
import { guardTool } from "@arcjet/guard/langgraph/v1";
import { tokenBucket, localDetectSensitiveInfo } from "@arcjet/guard";
import { arcjet } from "./arcjet.js";
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 10, intervalSeconds: 60, maxTokens: 10 });
const detectPii = localDetectSensitiveInfo({ deny: ["EMAIL", "PHONE_NUMBER", "IP_ADDRESS", "CREDIT_CARD_NUMBER"] });
export const lookupOrder = guardTool(
arcjet,
tool(
async ({ orderNumber, note }) => ({ orderNumber, note, status: "shipped" }),
{
name: "lookup_order",
description: "Look up an order by number",
schema: z.object({ orderNumber: z.string(), note: z.string() })
}
),
{
action: "order.looked-up",
rules: (input) => [lookupLimit({ key: input.orderNumber, requested: 1 }), detectPii(input.note)]
}
);
import { ToolNode } from "@langchain/langgraph/prebuilt";
import { guardToolNode } from "@arcjet/guard/langgraph/v1";
import { tokenBucket } from "@arcjet/guard";
import { arcjet } from "./arcjet.js";
const mcpLimit = tokenBucket({ bucket: "mcp-access", refillRate: 5, intervalSeconds: 60, maxTokens: 20 });
export const tools = guardToolNode(
arcjet,
new ToolNode([lookupOrder, ...mcpTools]),
{
action: ({ toolName }) => `${toolName}.invoked`,
rules: ({ toolName }) => [mcpLimit({ key: toolName, requested: 1 })]
}
);
// Then: graph.addNode("tools", tools)
import { detectPromptInjection } from '@arcjet/guard';
import { langchainContext } from '@arcjet/guard/langchain/v1';
import { arcjet } from './arcjet.js';
const inbound = detectPromptInjection();
const config = { configurable: { thread_id: conversationId } };
const decision = await arcjet.guard({
label: 'message.received',
rules: [inbound(userText)],
...langchainContext(config)
});
if (decision.conclusion === 'DENY' || decision.hasFailedOpen()) {
throw new Error('message blocked');
}
await agent.invoke({ input: userText }, config);
import { launchArcjet } from '@arcjet/guard';
import { guardTool } from '@arcjet/guard/langchain/v1';
import { tool } from '@langchain/core/tools';
import { z } from 'zod';
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const sendEmail = tool(
async ({ to, body }) => { /* send email */ },
{ name: 'send_email', schema: z.object({ to: z.string(), body: z.string() }) }
);
const guardedSendEmail = guardTool(sendEmail, {
arcjet,
action: 'email.sent',
onGuardError: 'deny'
});
pip install 'arcjet[langchain-agents]'
import { launchArcjet, tokenBucket, localDetectSensitiveInfo } from "@arcjet/guard";
import { guardPlugin } from "@arcjet/guard/google-adk/v2";
import { InMemoryRunner } from "@google/adk";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const runner = new InMemoryRunner({
agent,
appName: "orders",
plugins: [
guardPlugin(arcjet, {
action: "order.looked-up",
sessionId: conversationId,
onGuardError: "deny",
rules: ({ toolName, input }) => [
tokenBucket({ bucket: "lookups", refillRate: 10, intervalSeconds: 60, maxTokens: 100 })({ key: input.orderNumber, requested: 1 }),
localDetectSensitiveInfo({ deny: ["EMAIL", "PHONE_NUMBER", "CREDIT_CARD_NUMBER"] })(input.note),
],
})
],
});
await runner.runAsync({ userId: conversationId, sessionId: conversationId, newMessage: { parts: [{ text: userText }] } });
import { launchArcjet, detectPromptInjection } from "@arcjet/guard";
import { googleAdkContext } from "@arcjet/guard/google-adk/v2";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const inbound = detectPromptInjection();
const appContext = { sessionId: conversationId };
const decision = await arcjet.guard({
label: "message.received",
rules: [inbound(userText)],
...googleAdkContext(appContext),
});
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) {
throw new Error("message blocked");
}
await runner.runAsync({ userId: conversationId, sessionId: conversationId, newMessage: { parts: [{ text: userText }] } });
import { guardTool } from '@arcjet/guard/genkit/v1';
import { tokenBucket, localDetectSensitiveInfo } from '@arcjet/guard';
import { arcjet } from './arcjet.js';
const lookupOrder = guardTool(
arcjet,
ai.defineTool(
{ name: 'lookup_order', description: 'Look up an order by number',
inputSchema: z.object({ orderNumber: z.string(), note: z.string() }) },
async ({ orderNumber, note }) => ({ orderNumber, note, status: 'shipped' })
),
{
action: 'order.looked-up',
onGuardError: 'deny',
rules: (input) => [
tokenBucket({ bucket: 'lookups', refillRate: 10, intervalSeconds: 60, maxTokens: 100 })({ key: input.orderNumber, requested: 1 }),
localDetectSensitiveInfo({ deny: ['EMAIL', 'PHONE_NUMBER', 'CREDIT_CARD_NUMBER'] })(input.note)
]
}
);
import { detectPromptInjection } from '@arcjet/guard';
import { genkitContext } from '@arcjet/guard/genkit/v1';
import { arcjet } from './arcjet.js';
const inbound = detectPromptInjection();
const appContext = { sessionId: conversationId };
const decision = await arcjet.guard({
label: 'message.received',
rules: [inbound(userText)],
...genkitContext({ context: appContext })
});
if (decision.conclusion === 'DENY' || decision.hasFailedOpen()) {
throw new Error('message blocked');
}
return ai.generate({
prompt: userText,
tools: [lookupOrder],
use: [guardMiddleware(arcjet, { sessionId: conversationId })],
context: appContext
});
from arcjet.guard import LocalDetectSensitiveInfo, TokenBucket, launch_arcjet_sync
from arcjet.guard.crewai import free_text_arguments, register_arcjet_hooks
arcjet = launch_arcjet_sync(key=ARCJET_KEY)
lookup_limit = TokenBucket(refill_rate=10, interval_seconds=60, max_tokens=10, bucket="lookups")
detect_pii = LocalDetectSensitiveInfo(deny=["EMAIL", "PHONE_NUMBER", "IP_ADDRESS", "CREDIT_CARD_NUMBER"])
handle = register_arcjet_hooks(
guard=arcjet,
tools=["lookup_order"],
action="order.looked-up",
on_guard_error="deny",
rules=lambda arguments, _ctx: [
lookup_limit(key="orders", requested=1),
detect_pii(free_text_arguments(arguments)["note"]),
],
)
from arcjet.guard import DetectPromptInjection, launch_arcjet_sync
arcjet = launch_arcjet_sync(key=ARCJET_KEY)
inbound = DetectPromptInjection()
decision = arcjet.guard(
label="message.received",
rules=[inbound(user_text)],
)
if decision.conclusion == "DENY" or decision.has_failed_open():
raise RuntimeError("message blocked")
crew.kickoff(inputs={"request": user_text})
from arcjet.guard import TokenBucket, launch_arcjet_sync
from arcjet.guard.crewai import guard_tool
arcjet = launch_arcjet_sync(key=ARCJET_KEY)
lookup_limit = TokenBucket(refill_rate=10, interval_seconds=60, max_tokens=10, bucket="lookups")
guarded_lookup = guard_tool(
guard=arcjet,
tool=lookup_order,
action="order.looked-up",
rules=[lookup_limit(key="orders", requested=1)],
on_guard_error="deny",
)
guarded_lookup.run({"order_id": "ord_123", "note": "urgent"})
import Anthropic from "@anthropic-ai/sdk";
import { detectPromptInjection } from "@arcjet/guard";
import { claudeManagedAgentsContext, guardEvents } from "@arcjet/guard/claude-managed-agents/v0";
import { arcjet } from "./arcjet.js";
const client = new Anthropic();
async function sendTurn(sessionId: string, conversationId: string, userText: string) {
const inbound = await guardEvents(
arcjet,
{
events: [{ type: "user.message", content: [{ type: "text", text: userText }] }],
inbound: {
action: "message.received",
rules: ({ text }) => [detectPromptInjection()(text)],
},
context: claudeManagedAgentsContext({ correlationId: conversationId }),
},
(body) => client.beta.sessions.events.send(sessionId, body)
);
if (!inbound.allowed) return inbound.message;
}
from arcjet.guard import launch_arcjet, DetectPromptInjection
from arcjet.guard.claude_managed_agents import guard_custom_tool
arcjet = launch_arcjet(key=ARCJET_KEY)
async def run_my_tool(event, send, anthropic_session_id):
# your tool logic here
return {"result": "ok"}
protected_tool = guard_custom_tool(
guard=arcjet,
action="my_tool.executed",
run=run_my_tool,
session_id=CONVERSATION_ID,
on_guard_error="deny",
)
import { query } from '@anthropic-ai/claude-agent-sdk';
import { randomUUID } from 'node:crypto';
import { guardHooks } from '@arcjet/guard/claude-agent-sdk/v0';
import { detectPromptInjection } from '@arcjet/guard';
import { arcjet } from './arcjet.js';
const sessionId = conversationId ?? randomUUID();
for await (const message of query({
prompt: userText,
options: {
sessionId,
hooks: guardHooks(arcjet, {
sessionId,
inbound: {
action: 'message.received',
rules: ({ prompt }) => [detectPromptInjection()(prompt)],
},
}),
},
})) {
void message;
}
mkdir -p ~/.claude/skills && cp -r node_modules/@arcjet/guard/skills/integrate-arcjet-guard-claude-agent-sdk ~/.claude/skills/
# Then inside Claude Code:
# /integrate-arcjet-guard-claude-agent-sdk
import { guardPlugin } from '@arcjet/guard/google-adk/v2';
const runner = new Runner({
agent: new LlmAgent({ ... }),
plugins: [guardPlugin(), ...otherPlugins],
});
await runner.runAsync(session);
pip install 'arcjet[claude-managed-agents]'
# anthropic>=0.92.0,<2 is the required peer
pip install "arcjet[sensitive-info-rampart]"
from arcjet import sensitive_info, Mode
from arcjet_sensitive_info_rampart import rampart, RampartOptions, rampart_entities
rule = sensitive_info(
mode=Mode.LIVE,
backend=rampart(
RampartOptions(
min_confidence=0.7,
providers=("CUDAExecutionProvider", "CPUExecutionProvider"),
max_input_chars=50_000,
)
),
deny=rampart_entities(),
)
import { launchArcjet, moderateContent } from "@arcjet/guard";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const moderate = moderateContent();
const decision = await arcjet.guard({
label: "tools.chat",
rules: [moderate(userMessage)],
});
if (decision.conclusion === "DENY" && decision.reason === "MODERATE_CONTENT") {
throw new Error("Harmful content detected – rephrase your message");
}
const result = moderate.result(decision);
console.log(result?.detected, result?.billing?.unit, result?.billing?.count);
moderation, err := arcjet.GuardModerateContent(arcjet.GuardModerateContentOptions{
Mode: arcjet.ModeLive,
})
if err != nil {
return err
}
decision, err := guard.Guard(ctx, arcjet.GuardRequest{
Label: "tools.generate",
Rules: []arcjet.GuardRuleInput{moderation.Text(userMessage)},
})
if err != nil {
return err
}
if decision.IsDenied() && decision.Reason == arcjet.ReasonModerateContent {
return errors.New("content flagged by moderation")
}
if result := moderation.Result(decision); result != nil && result.Billing != nil {
fmt.Printf("charged %d %s\n", result.Billing.Count, result.Billing.Unit)
}
moderation, err := arcjet.GuardModerateContent(arcjet.GuardModerateContentOptions{
Mode: arcjet.ModeDryRun,
})
if err != nil {
return err
}
decision, err := guard.Guard(ctx, arcjet.GuardRequest{
Label: "tools.generate",
Rules: []arcjet.GuardRuleInput{moderation.Text(userMessage)},
})
import { guard } from '@arcjet/guard';
const decision = await guard({
label: 'refund',
actor: { userId },
inputs: [{ GuardPolicyServerString: '...' }],
metadata: {
invoice: { id: 'inv_123', amount: 4200 },
plan: 'pro',
durationMs: 160,
success: true
},
correlationId: runID
});
import { capture } from '@arcjet/guard';
async function refund(limit, input) {
// No rule was evaluated — just record the event.
await capture({ action: 'refund.issued', metadata: { invoice: input } });
}
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { registerTestClient } from '@arcjet/guard/testing';
import { refund } from './refund.js';
test('refund captures an event', async () => {
using client = registerTestClient();
await refund('inv_1');
assert.equal(client.captures[0]?.action, 'refund.issued');
});
arcjet.capture({
action: "refund.issued",
correlationId: runId,
decisionId: decision.id,
metadata: {
invoice: { id: "inv_123", amount: 4200 },
refunded: true,
},
});
export default {
async fetch(request, env, ctx) {
const response = await handle(request);
ctx.waitUntil(arcjet.flush());
return response;
},
};
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
guard.Flush(ctx)
_ = guard.Close(ctx)
import { nonce } from "@nosecone/next";
export default async function Page() {
const cspNonce = await nonce();
return <script nonce={cspNonce}>{`/* your inline script */`}</script>;
}
import { nonce } from "@nosecone/next";
export default async function Page() {
const cspNonce = await nonce();
return <script nonce={cspNonce}>{`/* ... */`}</script>;
}
import arcjet, { sensitiveInfo } from "@arcjet/node";
import { rampart } from "@arcjet/sensitive-info-rampart";
const aj = arcjet({
key: process.env.ARCJET_KEY!,
rules: [
sensitiveInfo({
mode: "LIVE",
deny: ["EMAIL", "GIVEN_NAME", "SURNAME", "STREET_NAME", "SSN"],
backend: rampart(), // Uses the local Rampart NER model
}),
],
});
npx skills add arcjet/skills
Summary
Arcjet is a commercial web-app-security library that developers add directly to their application code to handle rate limiting, bot detection, and abuse prevention, with newer work extending this to guarding AI agent tool calls against unsafe or leaky actions. It runs as an SDK embedded in the app itself, currently available for JavaScript, Go, and Python, rather than as a separate service to deploy, and it reports back to a hosted Arcjet Console for visibility into both HTTP requests and non-HTTP events like agentic file access. It is aimed at application developers rather than dedicated security teams, though its remote policy model lets security teams adjust enforcement without touching code. Arcjet has been live since 2023 and remains under active development, with 16 tracked releases and the most recent one 23 days ago.
Arcjet is the AI agent runtime security platform that helps teams observe agent activity, enforce policies on tool calls, and audit decisions.
Which requests to my application are actually bots instead of real users?
evaluates each request against bot-detection rules embedded in the app and reports the verdict, without a separate service to route traffic through first
Which of my users are abusing an endpoint faster than my rate limits should allow?
enforces rate limiting inline in the request path and records each decision to the console, tied to the specific rule that fired
Is this incoming request carrying sensitive information it shouldn't, before I store or forward it?
screens the text against configured sensitive-information types and blocks or allows based on a named list, denying anything not on it
Which tool call is my agent about to make, and should it be allowed to run?
sits at the boundary between the model's proposed arguments and the tool's execution, checking the call against a published policy before the side effect happens
Did someone just try to smuggle instructions into my agent through a message or tool result?
screens inbound text for prompt injection at the same decision point as other guards, before the model acts on it, so hostile instructions can be blocked instead of executed
Which agents are running against my systems right now, and what have they done?
ingests agent activity through coding-agent hooks and guard calls without a code change, and lists the actions in the console's activity view alongside ordinary web requests
all 18 features, with the evidence for each →
Features
18 capabilities
Built from Arcjet's releases, docs, README and our own runs.
“Seen” marks the month we first spotted a release the vendor didn't date.
Group names and tags are our labels.
Compare with peers →
What Arcjet offers you
ML-based detection2
SIEM forwarding2
Administrative audit log1
Agent-based deployment1
Client SDKs1
Python scripting support1
Scripting language support1
No capabilities match. Clear filters
Back to list
Previous
Next
Lines in monospace are the tool's own words: help text
parsed from its source, or an endpoint from its API document. Under
release , a line is our summary of a dated release. Under
docs , it is quoted from the tool's own documentation or
website. Each links back to the source it came from.
Command line
11 commands for Arcjet, each
with where it came from. This tool has no source analysis yet, so there
is no option reference and no captured run beside them.
Source
All 11
From the docs 11
No command matches.
From the docs
/integrate-arcjet-guard-claude-agent-sdk
From the docs
await arcjet.guard(label=, rules=)
From the docs
echo '{"tool":"bash","input":{"command":"ls"}}' | sh .muse/hooks/arcjet-hook.sh muse-code pre-tool-use
From the docs
pip install "arcjet[google-adk]"
From the docs
npm i @arcjet/guard @cloudflare/think
From the docs
echo '{"tool":"exec_command","input":{"cmd":"ls"}}' | ARCJET_KEY=<your-key> sh .codex/hooks/arcjet-hook.sh codex pre-tool-use
From the docs
claude --debug-file /tmp/arcjet-hooks-debug.log
From the docs
go get github.com/arcjet/arcjet-go@v1.0.0-rc.2
From the docs
pip install 'arcjet[langchain-agents]'
From the docs
pip install "arcjet[sensitive-info-rampart]"
From the docs
npx skills add arcjet/skills
Back to list
Previous
Next
Screenshots
From the release notes, newest first. Click one to see it full size.
Score card
Shipping velocity
releases a month, last 12 months
Documentation quality
release-notes rubric, 0–100
Breaking-change discipline
how rarely a release breaks users, and notice given
Maintenance risk
quiet time against its own cadence, maintainers
Innovation & surface
new capabilities, API and MCP surface
Five scores for Arcjet, each built from the releases, notes and API history this site tracks — and, on Researcher and Business, where it stands among every measured tool in its category and how that has moved.
Score cards are part of every paid plan.
See plans →
Already subscribed? Sign in →
Trajectory
Blank means never measured; nothing here is a filled-in zero.
Peer standings are included with Researcher and Business. See how this tool ranks against its competitors, using data we don't publish anywhere else.
See plans →
Each score is 0–100 on a published rubric, derived from data this site already shows.
A dimension we cannot measure says so — it is never scored 0 and never enters a rank.
How this is scored
Shipping velocity — releases a month over the last 12 months, on a log scale where 16 a month scores 100. Needs 3 releases in the window.
Documentation quality — the release-notes rubric published on the analytics page, unchanged.
Breaking-change discipline — the share of releases carrying a breaking change (none scores 100, half or more scores 0). Only projects that label breaking changes are measured: a clean record cannot be told from an unlabelled one.
Maintenance risk — quiet time measured against the tool's own release cadence (quiet longer than 90 days and three typical gaps scores 0), plus the number of active maintainers and whether contributors are growing, where known. Higher is lower risk.
Innovation & surface — new capabilities in the feature ledger this year, API and MCP operations added, and the share of commits that are features: the mean of whichever we hold.
Community health (open-source tools only) — each is a direction, not a size: contributors this year against last, the bus factor now against a year ago, and issues closed per issue opened, where steady scores 50, doubled 100 and halved 0; plus the median time to a maintainer's first reply (an hour scores about 100, a month or more 0). A commercial card has no repository and carries none of the four.
Documented delivery (commercial tools) — when a vendor's documented new capabilities have slowed against its own baseline, that counts against the overall standing as heavily as two low dimensions. Steady delivery, or too little history to judge, leaves the standing alone; it is never a sixth score.
Open-source tools are scored on repository signals; commercial tools on their published changelog. Ranks compare tools within one category and one profile and need at least five measured peers; the overall standing needs three ranked dimensions. Best-in-breed or consolidator is a shape, not a quality, and is never scored.
8 readings of Arcjet's pricing page since Sep 2024, with 1 relaunch of its plan lineup and 1 change to what its plans include — each one dated, compared with the reading before it, and explained for someone buying the tool.
Pricing and packaging history is included with Researcher and Business. See how every vendor we track sells, and how that has changed over time, using data we don't publish anywhere else.
See plans →
Already subscribed? Sign in →
Plans by offering
Each offering on the pricing page links to its own page of plans, read at its latest capture.
Plan relaunches
Plans, prices and descriptions are quoted from the vendor's own pricing page as we read it on each date.
A plan missing from a later reading is shown as no longer listed on the page — a statement about the page, not the product.
Readings that failed to show any plans are left out rather than read as a change.
Website history
arcjet.com, every change
Arcjet's home and pricing pages and 12 more pages as they looked each month from November 2021 to October 2026: 20 renders, 5 changes of tagline or palette. Step through them, play them, or put any two months side by side.
Website history is included with Researcher and Business.
See plans →
Already subscribed? Sign in →
Homepage Pricing Product
More pages from the site's menu
Compare
Fullscreen
Since the previous capture
Eras
← → or h l a month · Shift a year · [ ] previous / next change · Space play · 1 2 3 page · T whole page · C compare · F full size
Side by side
Wipe
Swap
Whole page
← → or h l the right month · Shift the left · S swap · W wipe · T whole page · Esc back
Rebranding timeline
Dates bound when a change appeared between captures. Possible rebrands are AI suggestions; a redesign alone does not establish a rebrand.
Coverage
Missing something? Tell us
What Arcjet connects to, runs on and detects, read from its release notes and docs. So far: 3 new detections.
Show
Latest changes 3
Detections 3
3 changes, tracked since Oct 10, 2026.
Oct 15 2026
new On-device sensitive information detection via Rampart backend
Adds email addresses, phone numbers, IP addresses, card numbers, names, street addresses, government or financial identifiers . 7 in all.
Detection runs on-device and covers email addresses, phone numbers, IP addresses, card numbers, names, street addresses, and government or financial identifiers.
Source ↗
Oct 10 2026
new Arcjet threat intel (IP threat) for outbound hosts in Remote Policies
With signals.ip_threat.dest.risk_level, AJP1009
Arcjet threat intel scores the hosts a call would contact with the same assessment Protect uses for the caller of an HTTP request. The input must be exposure – there is no variant. An empty value is risk none. A lookup that cannot be completed fails a live rule closed AJP1009
Source ↗
Oct 10 2026
new Server-side sensitive information detection in Remote Policies
With malicious-destination
The server-side sensitive information detector screens values up to 2 KB. A larger value is Both sensitive information kinds write their result to "malicious-destination".
Source ↗
Show all
Arcjet threat intel (IP threat) for outbound hosts in Remote Policies
First seen Oct 10, 2026
On-device sensitive information detection via Rampart backend
First seen Oct 15, 2026
Server-side sensitive information detection in Remote Policies
First seen Oct 10, 2026
Show all
Nothing first seen in this window. That doesn't mean Arcjet shipped nothing, only that we found no record of it.
Changes per month New Updated Retired
How we count
Read from 5 of 81 release notes and docs updates we hold for Arcjet. "First seen" is the earliest note that names something; "last" is the latest. Our history may start partway through Arcjet's life, so an old integration can show a recent first-seen date.
Each change counts once: "added 40 checks" is one line, so the numbers measure how often coverage moves, not how big the catalogue is or how well it detects.
Releases
Show
All 81 Releases 11 Docs 70 Breaking 1
Coverage in all entries
Any
Detections 2
Search
Full history
81 entries
No entries match these filters. Clear filters
October 202611
Oct 15
docs update
2 updates
Adds `@arcjet/sensitive-info-rampart` package for on-device sensitive information detection via the `rampart` backend.
Oct 14
release
Adds AI agent behavior drift analysis with cross-session state tracking, Cloudflare Clef scoring, and Rego-based Guard policy enforcement.
Oct 10
docs update
3 updates
Adds server-side threat intel for outbound hosts and sensitive-info detection via Remote Policies.
Oct 4
docs update
5 updates
Adds outbound threat detection for AI agents, scoring every external destination against real-time threat intelligence before requests are made.
September 202653
Sep 25
docs update
6 updates
Adds Google ADK adapters for Python and JavaScript to gate agent tool calls before side effects run
Sep 18
docs update
6 updates
Remote policies gain label validation helpers and a new `AJ1023` warning code for invalid labels.
Sep 17
announced
Launches agent runtime security with observe, enforce, and audit capabilities for AI agents.
Sep 17
release
Introduces Agent Runtime Security with OpenTelemetry ingestion, Rego policy enforcement via API/CLI/MCP, and native SDKs for multiple agent frameworks.
Sep 16
docs update
23 updates
Adds a Guards policy authoring system with visual builder, plain-English generation, Rego editor, and a full MCP server API
Sep 15
docs update
13 updates
Guard adds Vercel Eve agent integration with guardTool(), guardApproval(), guardInbound(), and arcjetHooks() helpers
Sep 8
docs update
3 updates
Adds agent guards for Google ADK, TanStack AI, and Claude Managed Agents across JavaScript and Python
August 20269
Aug 23
docs update
breaking
8 updates
Bot protection now supports remote rules from the dashboard or MCP server for incident response without code changes.
Aug 15
docs update
Guard adds Vercel Eve and Mastra agent framework integrations with inbound screening, connection approvals, and lifecycle hooks
July 20263
Jul 3
release
2 updates
Adds proxy support, Nosecone Nonce API, enhanced security briefings, and a new Console activity view.
Jul 1
release
Adds pluggable `backend` to `sensitiveInfo` rule, enabling local Rampart NER model for names and addresses.
June 20263
Jun 12
release
JS SDK adds trusted proxy configuration for Cloudflare and updates Wasm bot detection performance.
Jun 3
release
Introduces Advanced Bot Signals to detect browser automation without CAPTCHAs using client-side telemetry.
Jun 2
release
Announces design principles for its agent-safe CLI, introducing structured error codes, confirmation envelopes, and token-based auth.
May 20262
May 22
release
Unifies coding agent skills into a single `arcjet/skills` package with expanded Python docs and email notification management.
May 20
release
Introduces prompt injection detection using hosted LLMs via Open Inference Protocol v2
Show
All 81 Releases 11 Docs 70 Breaking 1
Coverage in all entries
Any
Detections 2
Search
81 entries
No entries match these filters. Clear filters
Arcjet adds @arcjet/sensitive-info-rampart package for on-device sensitive information detection via the rampart backend.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
Arcjet adds @arcjet/sensitive-info-rampart, a new on-device PII PII Personally Identifiable Information — data that can identify a specific individual, such as names, email addresses, or SSNs. Cyber tools flag or redact it to limit exposure and meet privacy regulations. detection backend with remote policy support.
How we built AI agent behavior drift analysis with Cloudflare Clef (2026-10-14)
Oct 14, 2026
·
issue 109
Arcjet adds AI agent behavior drift analysis with cross-session state tracking, Cloudflare Clef scoring, and Rego Rego A declarative policy language created by the Open Policy Agent project, used to express authorization and compliance rules as code so cyber tools can evaluate structured data against those rules consistently. -based Guard policy enforcement.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Run the Clef Flash model to score a batch of session activity for containment risk inside a Cloudflare Worker.
javascript
copy
const answers = await env.AI.run("@cf/cloudflare/clef-flash", {
model: "clef-flash",
state,
questions,
});
Adds cross-session risk analysis for coding agents (Claude Code, Codex, Cursor, Muse Code, GitHub Copilot, and custom agents) that flags a session as tainted when it detects prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. in tool results, PII PII Personally Identifiable Information — data that can identify a specific individual, such as names, email addresses, or SSNs. Cyber tools flag or redact it to limit exposure and meet privacy regulations. in files the agent read, or action sequences resembling credential theft.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Uses @cf/cloudflare/clef-flash via env.AI.run() to score structured, typed questions (yes/no probability, multiple-choice distribution, scale position) over session activity sequences — replacing broad single-question hazard checks. Stores per-session risk state in a Cloudflare Durable Object (created on demand per session) that serializes writes, exposes active flags and explanations, and is readable by the next hook request with minimal latency. Separates sequence review questions into four categories: factual hazard, evidence sufficiency, triage (drives highlight notifications), and containment (drives session locking) — with different score thresholds for highlights versus locks. Enforces session taint via a Rego Guard policy that reads session_flags from the session Durable Object on every tool call, with risky tool categories (shell, web, mcp, file_write) and lock-trigger flag names (prompt_injection, data_exfiltration, code_execution_risk, sensitive_data_exposure, elevated_risk, credential_acce…) defined as named sets.
+1 more
show less
Applies a scorer isolation rule preventing Arcjet's own output (narratives, model verdicts, taint rows, lock-caused denials) from feeding back into the Clef scoring state, so analysis is grounded only in raw agent actions.
6 details in Everything
Arcjet Remote Policies gain typed Go constructors, policy labels (up to 256 bytes), and outbound host threat scoring writing to signals.ip_threat.dest.risk_level.
New detection Adds arcjet.GuardPolicyServerString, arcjet.GuardPolicyServerBoolean, arcjet.GuardPolicyServerInteger, arcjet.GuardPolicyServerNumber, arcjet.GuardPolicyServerStringList, and arcjet.GuardPolicyLocalString constructor functions in Go for defining typed Remote Policy inputs (requires arcjet-go v1.2.0 or later).Supports policy labels — ASCII letters, digits, dashes, underscores, and dots, up to 256 bytes, case-sensitive — to uniquely own a policy; requires arcjet-go v1.2.0+ for Go or Arcjet Python 1.4.0+.
2 details in Everything
Arcjet launches Agent Guards: per-action runtime policies for AI agents covering email, refunds, DB calls, MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. servers, and more.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
Adds @arcjet/guard (JavaScript/TypeScript) and arcjet.guard (Python) packages plus github.com/arcjet/arcjet-go (Go) to instrument agent tool calls with the guard() function, which evaluates a named Guard policy before an action runs.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Introduces action labels (e.g., email.sent, refund.issued) that route each guard() call to its published Guard policy, with typed SERVER or LOCAL inputs — LOCAL inputs are evaluated entirely in-process and only a digest leaves the SDK. Adds prompt-injection prompt-injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. detection over a SERVER string input and sensitive-information detection over either LOCAL or SERVER strings as declarable Guard policy detectors. Supports IP threat intelligence scoring on outbound hosts a tool would contact, enabling Guard policies to deny high-risk destinations before the call is made. Enables per-user, per-tool, and per-tenant rate limiting for actions that never arrive over HTTP (queue consumers, workflow steps, MCP tool calls).
+5 more
show less
Adds DRY_RUN mode per rule: a dry-run rule is evaluated and recorded but never denies, letting teams observe impact in the Console Activity page before going live; new rules default to dry run. Supports Guard policy authoring in Rego, a visual Console builder, or plain-English description with Arcjet-generated rules; publication runs stored tests and cross-checks the edge evaluator against Open Policy Agent before rollout. Ships framework integrations for Vercel AI SDK, LangChain, LangGraph, Genkit, Google ADK ADK Google's Agent Development Kit, an open-source framework for building and orchestrating AI agents; cyber tools use it to compose multi-agent workflows with built-in tool-calling and state management. , Cloudflare Think, OpenAI Agents, Strands Agents, TanStack AI, Vercel Eve, Mastra, Claude Agent SDK, and Claude Managed Agents (JavaScript/TypeScript) and LangChain, CrewAI, Google ADK, OpenAI Agents, Strands Agents, Claude Agent SDK, and Claude Managed Agents (Python), plus Microsoft Agent Framework (Go). Adds no-SDK Guard policies for coding agents (Claude Code, Cursor, GitHub Copilot, OpenAI Codex, Muse Code) applied directly from the Console. Supports recording agent sessions via OpenTelemetry OpenTelemetry An open standard and set of tools for collecting traces, metrics and logs from applications. It lets services export observability data without tying instrumentation to one monitoring vendor. or the Claude Compliance API without enforcing a Guard policy.
10 details in Everything
Arcjet now secures AI agent tool calls — including Claude Code, Copilot, Codex, Cursor, and Muse Code — via their existing hooks.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
Arcjet adds outbound threat detection for AI agents, scoring every external destination against real-time threat intelligence before requests are made.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Block agent tool calls to any destination scored high or critical risk — the minimal starting policy for outbound threat detection.
rego
copy
package arcjet.guard
import rego.v1
deny contains "malicious-destination" if {
input.signals.ip_threat.dest.risk_level in {"high", "critical"}
}
Exempt known-good hosts (docs, GitHub) while still blocking high/critical destinations across each individually assessed host.
rego
copy
package arcjet.guard
import rego.v1
allowed := {"docs.arcjet.com", "github.com", "raw.githubusercontent.com"}
deny contains "malicious-destination" if {
some a in input.signals.ip_threat.dest.assessments
a.risk_level in {"high", "critical"}
not a.host in allowed
}
Deny agent actions only when a specific activity (botnet) is confirmed at high or critical risk on any individually assessed destination.
rego
copy
package arcjet.guard
import rego.v1
deny contains "botnet-destination" if {
some a in input.signals.ip_threat.dest.assessments
a.risk_level in {"high", "critical"}
"botnet" in a.activities
}
Adds GUARD_POLICY_DETECTOR_KIND_IP_THREAT detector kind for agent threat detection, exposing per-host and rolled-up risk signals under input.signals.ip_threat.<detector_id> in Rego policies.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Exposes rolled-up fields detected, risk_level, reputation, activities, host, ip, and assessments under input.signals.ip_threat.<detector_id> for worst-wins assessment across all scored public destinations. Provides a coding-agent.destination-threat starter policy in the Arcjet Console to automatically block destinations with high or critical risk levels on Tool call executions. Supports per-destination assessments array entries (each carrying host, ip, risk_level, reputation, activities, detected) enabling per-host policy decisions rather than only rolled-up verdicts. Supports fine-grained deny rules on specific activities values (malware, botnet, brute_force, exploit, spam, scanning, denial_of_service, crawling, fraud, post_exploitation, audit, abuse) within Rego policies.
+2 more
show less
Supports inline allowlists in Rego destination-threat policies to exempt trusted hosts from block decisions. New agent threat detection capability analyzes outbound requests from both coding agents and custom agents against Arcjet's real-time threat intelligence database before the request is made.
7 details in Everything
Arcjet adds threat-intel scoring for outbound destinations, screening hosts a call would contact against its IP-risk engine.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
Adds Arcjet threat intel (IP threat) guard to score outbound destination hosts using the same IP-risk engine applied to inbound HTTP callers, writing results to signals.ip_threat.dest.risk_level with values "high" or "critical".︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Returns error code AJP1009 when a threat-intel lookup for a destination host cannot be completed in a live rule.
2 details in Everything
Arcjet adds coding-agent policies for AWS access blocking, threat detection, and autonomous-mode enforcement across Claude Code, Codex, Cursor, Copilot, and Muse Code.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Deny agent calls to malicious external destinations (APIs, MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. servers, web hosts) rated high or critical risk, with an allowlist for known-safe hosts.
rego
copy
coding-agent.destination-threat
allowed = ["safe.internal.example.com"]
deny {
a := input.signals.ip_threat.dest.assessments[_]
a.risk_level in {"high", "critical"}
not a.host in allowed
}
Adds coding-agent.aws-access policy with three layered checks covering CLI names (aws, paths ending in /aws), AWS API destinations (.amazonaws.com, .aws.amazon.com), and credential markers (AWS_ACCESS_KEY, AWS_SECRET_ACCESS, .aws/credentials, .aws/config) in a single round trip.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds coding-agent.no-auto-mode policy to deny tool calls when Claude Code or Codex reports bypassPermissions or dontAsk permission mode, while allowing plan and acceptEdits modes. Adds coding-agent.tainted-session policy to block shell, web, MCP, and file-write events while any session-taint flag is active. Exposes destinations field on hook payloads, populated with URL-argument hosts and hosts of absolute URLs found in shell commands, enabling destination-layer policies for tools like curl. Supports threat-detection allowlist bypass by matching .signals.ip_threat.dest.assessments entries so known-safe hosts skip the high/critical risk-level deny within the same policy.
5 details in Everything
Arcjet now supports Muse Code: enforce policies and record sessions via .muse/hooks.json hook scripts.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Bootstrap Arcjet enforcement on every tool call and prompt in a Muse Code repository — commit the hook config so the whole team inherits it.
.muse/hooks.json
copy
{
"hooks": {
"PreToolUse": [{
"hooks": [{
"type": "command",
"command": "sh .muse/hooks/arcjet-hook.sh muse-code pre-tool-use",
"commandWindows": "powershell -NoProfile -File .muse/hooks/arcjet-hook.ps1 -Vendor muse-code -Event pre-tool-use",
"timeout": 10
}]
}],
"UserPromptSubmit": [{
"hooks": [{
"type": "command",
"command": "sh .muse/hooks/arcjet-hook.sh muse-code user-prompt-submit",
"commandWindows": "powershell -NoProfile -File .muse/hooks/arcjet-hook.ps1 -Vendor muse-code -Event user-prompt-submit",
"timeout": 10
}]
}]
}
}
Store the Arcjet key so the hook script can read it even when Muse Code clears the environment — no changes to shell profiles needed.
copy
$ echo 'ajkey_xxxxxxxxxxxxxxxx' > ~/.config/muse/arcjet.key
chmod 600 ~/.config/muse/arcjet.key
Test that the hook script correctly POSTs a sample PreToolUse payload to Arcjet and returns an allow or deny decision before committing it.
copy
$ echo '{"tool":"bash","input":{"command":"ls"}}' | sh .muse/hooks/arcjet-hook.sh muse-code pre-tool-use
Adds Muse Code integration via arcjet-hook.sh (Linux/macOS) and arcjet-hook.ps1 (Windows) hook scripts that POST hook payloads to https://decide.arcjet.com/v1/agent-hooks/<vendor>?event=<event> with Authorization: Bearer $ARCJET_KEY and X-Arcjet-Principal headers.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds .muse/hooks.json configuration supporting PreToolUse, PermissionRequest, UserPromptSubmit, PostToolUse, PostToolUseFailure, Stop , SubagentStart, SubagentStop, SessionStart, SessionEnd, PreCompact, and PostCompact hook events, using command and commandWindows fields to dispatch to the hook script on all platforms. Supports three Muse Code hook deployment scopes: project-level (.muse/hooks.json), user-level (hooks block in ~/.config/muse/settings.json), and managed (file at managed_hooks_path), each with different trust and removal controls. Reads ARCJET_KEY from ~/.config/muse/arcjet.key as a fallback when Muse Code's cleared environment does not pass the variable through. Enforces deny responses per Muse Code's vendor-specific shapes: permissionDecision: deny for PreToolUse, behavior: deny for PermissionRequest, and decision: block for UserPromptSubmit.
5 details in Everything
Arcjet adds coding agent support for Claude Code, GitHub Copilot, Muse Code, OpenAI Codex, and Cursor︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
Arcjet adds Google ADK ADK Google's Agent Development Kit, an open-source framework for building and orchestrating AI agents; cyber tools use it to compose multi-agent workflows with built-in tool-calling and state management. adapters for Python and JavaScript to gate agent tool calls before side effects run︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Gate every tool call in a Google ADK Runner with prompt-injection prompt-injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. and PII PII Personally Identifiable Information — data that can identify a specific individual, such as names, email addresses, or SSNs. Cyber tools flag or redact it to limit exposure and meet privacy regulations. detection, correlated to the caller's conversation ID.
python
copy
from arcjet.guard.google_adk import guard_plugin, google_adk_context, launch_arcjet
from arcjet import DetectPromptInjection, LocalDetectSensitiveInfo, TokenBucket
aj = launch_arcjet(
rules=[
TokenBucket(refill_rate=10, interval_seconds=60, max_tokens=100),
DetectPromptInjection(),
LocalDetectSensitiveInfo(),
]
)
app_context = {"session_id": conversation_id}
plugin = guard_plugin(
aj,
session_id=lambda call: google_adk_context(app_context).get("correlation_id"),
on_guard_error="deny",
)
runner = Runner(app_name="my-agent", agent=agent, plugins=[plugin])
Gate a single agent's tool calls via before_tool_callback when you hold the LlmAgent directly rather than the Runner.
python
copy
from arcjet.guard.google_adk import guard_tool, launch_arcjet
from arcjet import DetectPromptInjection, TokenBucket
aj = launch_arcjet(rules=[DetectPromptInjection(), TokenBucket(refill_rate=5, interval_seconds=60, max_tokens=50)])
agent = LlmAgent(
name="order-agent",
before_tool_callback=guard_tool(
aj,
action=lambda call: f"{call['tool_name']}.invoked",
session_id=lambda call: conversation_id,
on_guard_error="deny",
),
)
Adds arcjet[google-adk] Python package (requires google-adk>=2.0.0,<3, CPython 3.10+) with helpers imported from arcjet.guard.google_adk: guard_tool, guard_plugin, google_adk_context, launch_arcjet, and launch_arcjet_sync for sync frameworks.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds guard_tool as an LlmAgent.before_tool_callback that denies a tool call by returning a skip dict — assign via LlmAgent(before_tool_callback=...) — with on_guard_error defaulting to 'deny' (fail-closed). Adds guard_plugin as a Runner plugin that gates all tool calls runner-wide via before_tool_callback; first-win semantics mean it must be listed first, and tool names already covered by guard_tool should be listed in exclude to avoid double-calling Guard. Adds google_adk_context helper that reads a caller-owned correlation_id, then session_id, then conversation_id (camel-case aliases also accepted) without minting IDs or reading tool_context.session.id. Supports on_guard_error option on guard_tool and guard_plugin (defaults to 'deny'); denial reasons are PROMPT_INJECTION, SENSITIVE_INFO, and ERROR, with rate-limit denials additionally setting a retry delay.
5 details in Everything
Arcjet adds a Cloudflare Think adapter with guardHooks() and cloudflareThinkContext() to enforce policy on agent tool calls︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ TRY IT
Attach Arcjet Guard to every tool call in a Cloudflare Think agent, enforcing rate limits on a specific tool and blocking PII PII Personally Identifiable Information — data that can identify a specific individual, such as names, email addresses, or SSNs. Cyber tools flag or redact it to limit exposure and meet privacy regulations. in free-text arguments before execute runs.
copy
$ import { launchArcjet, tokenBucket, localDetectSensitiveInfo } from "@arcjet/guard";
import { guardHooks } from "@arcjet/guard/cloudflare-think/v0";
import { Think } from "@cloudflare/think";
import { tool } from "ai";
import { z } from "zod";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 10, intervalSeconds: 60, maxTokens: 20 });
const detectPii = localDetectSensitiveInfo();
const inputSchema = z.object({ orderNumber: z.string(), note: z.string() });
const hooks = guardHooks(arcjet, {
action: "order.looked-up",
onGuardError: "deny",
rules: ({ toolName, input }) => {
if (toolName !== "lookup_order") return [];
const { orderNumber, note } = inputSchema.parse(input);
return [lookupLimit({ key: orderNumber, requested: 1 }), detectPii(note)];
},
});
export class OrderAgent extends Think<Env> {
beforeToolCall = hooks.beforeToolCall;
}
Screen a user's inbound message for prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. before starting a Think turn, failing closed so the turn never begins on a denied or failed-open decision.
copy
$ import { launchArcjet, detectPromptInjection } from "@arcjet/guard";
import { cloudflareThinkContext } from "@arcjet/guard/cloudflare-think/v0";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const inbound = detectPromptInjection();
async function screenInbound(conversationId: string, userText: string) {
const appContext = { sessionId: conversationId };
const decision = await arcjet.guard({
label: "message.received",
rules: [inbound(userText)],
...cloudflareThinkContext({ context: appContext }),
});
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) {
throw new Error("message blocked");
}
}
Adds guardHooks() from @arcjet/guard/cloudflare-think/v0 to gate every beforeToolCall invocation in a Cloudflare Think agent, returning { action: 'substitute', output } (default) or { action: 'block', reason } on deny without ever executing the tool.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds cloudflareThinkContext() from @arcjet/guard/cloudflare-think/v0 to supply caller-owned correlation IDs (correlationId, sessionId, conversationId) to direct guard() calls for inbound text screening. Adds ArcjetDenialResult type export from @arcjet/guard/cloudflare-think/v0 with fields arcjetDenied, reason (RATE_LIMIT, PROMPT_INJECTION, SENSITIVE_INFO, ERROR), message, retryable, and retryAfterSeconds for structured denial payloads the model receives as a tool result. Supports onDeny: 'block' option on guardHooks() to switch from the default substitute envelope to Think's native block envelope on real DENY decisions (unavailability always substitutes regardless). Supports onGuardError: 'deny' | 'allow' option on guardHooks() to configure fail-closed or fail-open behavior when the Guard service is unavailable.
+1 more
show less
Supports per-call dynamic rules, actor, inputs, metadata, and sessionId options on guardHooks() as functions of { toolName, input } so policy can vary by which tool is invoked.
6 details in Everything
Arcjet Guard adds Cloudflare Think integration, Google ADK ADK Google's Agent Development Kit, an open-source framework for building and orchestrating AI agents; cyber tools use it to compose multi-agent workflows with built-in tool-calling and state management. (Python/JS) support, and HITL HITL Human in the loop, involving a person in an otherwise automated workflow. The person can review, approve, correct or supply information before work continues. via request_confirmation︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Wire Arcjet Guard into a Google ADK agent (Python) to intercept tool calls before they execute.
python
copy
import arcjet.guard.google_adk as aj_guard
agent = Agent(
...,
before_tool_callback=aj_guard.guard_plugin,
)
Adds @arcjet/guard/cloudflare-think/v0 integration, enabling Arcjet Guard to run on Cloudflare Think with a default substitute action ({ action: "substitute", output: ArcjetDenialResult }) and optional onDeny: "block".︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds arcjet.guard.google_adk support for Google ADK in both JavaScript and Python, hookable via before_tool_callback and guard_plugin. Adds request_confirmation (HITL — human-in-the-loop human-in-the-loop Human in the loop, involving a person in an otherwise automated workflow. The person can review, approve, correct or supply information before work continues. ) capability to Arcjet Guard, allowing tools to pause execution and require confirmation before proceeding. Adds guard_plugin and plugins surface on the Runner, enabling Arcjet Guard to be wired into agent runtimes as a plugin.
4 details in Everything
Arcjet adds Claude Code hook integration to enforce policies and record AI coding-agent session activity.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
Adds https://decide.arcjet.com/v1/agent-hooks/claude-code API endpoints for Claude Code hooks, covering events including post-tool-use-failure, notification, subagent-start, subagent-stop, task-created, task-completed, error, teammate-idle, cwd-changed, directory-added, file-changed, pre-compact, post-compact, pre-model-switch, post-model-switch, elicitation, and elicitation-result.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds PreModelSwitch hook enforcement via https://decide.arcjet.com/v1/agent-hooks/claude-code?event=pre-model-switch to enforce allowed-model policies on Claude Code sessions — a denied switch keeps the current model in place. Supports managed-settings.json as a drop-in configuration template that wires all Claude Code enforcement and activity-recording hooks to Arcjet without changing developer workflow. Supports integration via the Claude Compliance API or Claude Code's native OpenTelemetry OpenTelemetry An open standard and set of tools for collecting traces, metrics and logs from applications. It lets services export observability data without tying instrumentation to one monitoring vendor. export to Arcjet through managed settings, as an alternative when hooks cannot be deployed (note: hooks are the only path that enforces policies).
4 details in Everything
Arcjet now supports protecting custom AI agent pipelines across 14 named frameworks including LangChain, CrewAI, and OpenAI Agents.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
Adds custom AI agent protection for tool calls, queue consumers, and agentic pipelines — covering Claude Agent SDK, Claude Managed Agents, Cloudflare Think, CrewAI, Genkit, Google ADK ADK Google's Agent Development Kit, an open-source framework for building and orchestrating AI agents; cyber tools use it to compose multi-agent workflows with built-in tool-calling and state management. , LangChain, LangGraph, Mastra, Microsoft Agent Framework, OpenAI Agents, Strands Agents, TanStack AI, and Vercel AI SDK/Eve — enforcing bot protection and security policies across all agent interactions.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
1 detail in Everything
Arcjet adds Google ADK ADK Google's Agent Development Kit, an open-source framework for building and orchestrating AI agents; cyber tools use it to compose multi-agent workflows with built-in tool-calling and state management. and Cloudflare Think integrations for agent security.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ TRY IT
Install Arcjet with Google ADK support to protect a Python-based AI agent.
copy
$ pip install "arcjet[google-adk]"
Install Arcjet guard alongside Cloudflare Think to secure a JavaScript-based AI agent on Cloudflare.
copy
$ npm i @arcjet/guard @cloudflare/think
Adds google-adk integration, installable via pip install "arcjet[google-adk]".︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds @cloudflare/think integration for Cloudflare Think (JavaScript-only), installable via npm i @arcjet/guard @cloudflare/think.
2 details in Everything
Arcjet remote policies gain label validation helpers and a new AJ1023 warning code for invalid labels.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Check whether a remote policy failed open (label not matched) so your code can log or alert rather than silently allowing traffic.
javascript
copy
const decision = await aj.protect(req);
if (decision.hasFailedOpen()) {
console.warn('Remote policy label not matched — guard skipped, request allowed by default');
}
Adds validateGuardLabel / validate_guard_label / ValidateGuardLabel helpers to let practitioners verify a policy label before sending it.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds ArcjetInvalidLabelError (thrown by adapter factories in @arcjet/guard >= 1.13.0 / >= 1.2.0) and ErrInvalidLabel return value when a label fails validation. Introduces warning code AJ1023, emitted by capture() when an invalid label is detected but the request is still sent. Exposes hasFailedOpen() / has_failed_open() / HasFailedOpen() on policy results to indicate when an unmatched label causes the guard to skip execution with a fail-open ALLOW.
4 details in Everything
Arcjet adds a Go SDK for Microsoft Agent Framework with GuardTool, GuardTools, and GuardMiddleware helpers to policy-gate agent tool calls and screen prompts.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Guard a single high-risk tool (e.g. issuing a refund) so the model cannot invoke it without an Arcjet policy decision first.
go
copy
guarded, err := agentframework.GuardTool(guard, issueRefund, agentframework.ToolPolicy{
Action: "refund.issued",
Actor: func(ctx context.Context, args json.RawMessage) (string, error) {
return userID, nil
},
OnDeny: func(d arcjet.GuardDecision) any {
return refundResult{Status: "refused", Reason: string(d.Reason)}
},
})
Screen every inbound user message for prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. before the provider is called, and gate all named tools in one middleware attachment.
go
copy
screen, err := agentframework.GuardMiddleware(guard, agentframework.MiddlewareConfig{
Inbound: &agentframework.InboundPolicy{
Action: "message.received",
Rules: func(_ context.Context, text string) ([]arcjet.GuardRuleInput, error) {
return []arcjet.GuardRuleInput{promptScan.Text(text)}, nil
},
},
Tools: func(t tool.Tool) (agentframework.ToolPolicy, bool) {
switch t.Name() {
case "issue_refund":
return agentframework.ToolPolicy{Action: "refund.issued", Actor: actor}, true
case "lookup_order":
return agentframework.ToolPolicy{Action: "order.looked-up", Actor: actor}, true
}
return agentframework.ToolPolicy{}, false
},
})
Group all Guard decisions from a multi-turn conversation into a single Sequence in the Arcjet Console by attaching a correlation ID before each agent run.
go
copy
ctx = arcjet.ContextWithCorrelationID(ctx, conversationID)
resp, err := a.RunText(ctx, prompt).Collect()
Adds github.com/arcjet/arcjet-go/agentframework nested Go module (requires Go 1.26) installable via go get github.com/arcjet/arcjet-go/agentframework@latest to bring Arcjet guard capabilities to Microsoft Agent Framework agents.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds agentframework.GuardTool() and agentframework.MustGuardTool() to wrap a single tool.FuncTool so that every model-chosen call passes through an Arcjet policy before the function executes. Adds agentframework.GuardTools() to apply a policy selector across an entire tool list at once, covering tools that arrive at runtime (e.g. from mcptool.ListTools). Adds agentframework.GuardMiddleware() with MiddlewareConfig.Tools to gate every tool an agent run carries — including tools added after setup — without wrapping each one by hand. Adds agentframework.GuardMiddleware() with MiddlewareConfig.Inbound / InboundPolicy to screen user-role message text for prompt injection before the provider is called, returning a single assistant denial message without ever reaching the model.
+4 more
show less
Adds ToolPolicy struct with fields Action , Actor , Inputs , Rules , CorrelationID, Metadata , OnGuardError, and OnDeny to configure per-tool policy behavior, including custom denial results via OnDeny func(arcjet.GuardDecision) any. Returns arcjet.GuardDenialResult as a tool result (not a Go error) on denial, carrying JSON fields arcjetDenied, reason, message, retryable, and retryAfterSeconds consistent across Go, JavaScript, and Python SDKs. Adds arcjet.ContextWithCorrelationID() to attach a conversation ID to a context so all Guard decisions from one conversation are grouped as a single Sequence in the Arcjet Console. Adds agentframework.CorrelationIDStateKey session state key so a correlation ID stored on the session is picked up by GuardMiddleware when none is present on the context.
9 details in Everything
Arcjet adds OpenAI Codex integration, intercepting prompts and tool calls via hooks to enforce policy and audit agent activity.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Wire Arcjet into every Codex hook event for a repository so all prompts and tool calls are observed and policy-enforced without changing developer workflow.
.codex/hooks.json
copy
{
"hooks": {
"PreToolUse": [{"hooks": [{"type": "command", "command": "sh .codex/hooks/arcjet-hook.sh codex pre-tool-use", "commandWindows": "powershell -NoProfile -File .codex/hooks/arcjet-hook.ps1 -Vendor codex -Event pre-tool-use", "timeout": 10}]}],
"UserPromptSubmit": [{"hooks": [{"type": "command", "command": "sh .codex/hooks/arcjet-hook.sh codex user-prompt-submit", "commandWindows": "powershell -NoProfile -File .codex/hooks/arcjet-hook.ps1 -Vendor codex -Event user-prompt-submit", "timeout": 10}]}]
}
}
Deploy Arcjet hooks as admin-enforced managed requirements on Linux/macOS so developers cannot remove them, and restrict the device to managed hooks only.
/etc/codex/requirements.toml
copy
allow_managed_hooks_only = true
[features]
hooks = true
[hooks]
managed_dir = "/etc/codex/hooks"
windows_managed_dir = 'C:\ProgramData\OpenAI\Codex\hooks'
[[hooks.PreToolUse]]
[[hooks.PreToolUse.hooks]]
type = "command"
command = "sh /etc/codex/hooks/arcjet-hook.sh codex pre-tool-use"
command_windows = 'powershell -NoProfile -File C:\ProgramData\OpenAI\Codex\hooks\arcjet-hook.ps1 -Vendor codex -Event pre-tool-use'
timeout = 10
Test that the hook script correctly forwards a Codex payload to Arcjet and returns a policy decision.
copy
$ echo '{"tool":"exec_command","input":{"cmd":"ls"}}' | ARCJET_KEY=<your-key> sh .codex/hooks/arcjet-hook.sh codex pre-tool-use
Adds arcjet-hook.sh (Linux/macOS) and arcjet-hook.ps1 (Windows) hook scripts that pipe Codex hook payloads to https://decide.arcjet.com/v1/agent-hooks/codex?event=<event> via the ARCJET_KEY environment variable.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds .codex/hooks.json configuration supporting Codex hook events PreToolUse, PermissionRequest, UserPromptSubmit, PostToolUse, Stop , SessionStart, SessionEnd, SubagentStart, SubagentStop, and PreCompact — each wired to the Arcjet hook script via command and commandWindows fields. Adds requirements.toml managed-install support at /etc/codex/requirements.toml (Linux/macOS) or %ProgramData%\OpenAI\Codex\requirements.toml (Windows), with managed_dir, windows_managed_dir, allow_managed_hooks_only, and per-event command / command_windows fields for admin-enforced policy that developers cannot override. Adds X-Arcjet-Principal request header (populated from $USER or $USERNAME) to attribute each Codex session to an individual developer in Arcjet audit records. Supports OpenAI Codex tool names apply_patch, exec_command, spawn_agent, and update_plan, which Arcjet maps to the canonical action types file_write, shell, agent, and other respectively.
5 details in Everything
Arcjet adds GitHub Copilot agent-hook API endpoints for runtime security across prompt, tool-call, and session lifecycle events.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
Adds https://decide.arcjet.com/v1/agent-hooks/copilot API endpoints with event query parameters — pre-tool-use, permission-request, user-prompt-submit, user-prompt-transformed, post-tool-use, stop, session-start, and session-end — to enforce policy and detect prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. at each stage of a GitHub Copilot agent session.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Omitting the surface parameter causes Arcjet to record unknown, enabling a single hook file to serve both the CLI and cloud coding agent surfaces without mislabeling sessions.
2 details in Everything
Arcjet: Adds failClosed: true support on preToolUse and beforeSubmitPrompt hook entries to block agent actions when the policy decision fails or times out.
└──▷ USE IT
Commit the project-scoped hook config so Cursor fires Arcjet on every tool call and prompt, with hard blocking (failClosed: true) on enforcement events.
.cursor/hooks.json
copy
{
"version": "1",
"hooks": {
"preToolUse": [
{
"command": "sh .cursor/hooks/arcjet-hook.sh cursor pre-tool-use",
"timeout": 5000,
"failClosed": true
}
],
"beforeSubmitPrompt": [
{
"command": "sh .cursor/hooks/arcjet-hook.sh cursor user-prompt-submit",
"timeout": 5000,
"failClosed": true
}
],
"postToolUse": [
{
"command": "sh .cursor/hooks/arcjet-hook.sh cursor post-tool-use",
"timeout": 5000
}
]
}
}
Verify a Cursor session is being observed by Arcjet after installing the hooks — check the Activity view in the Arcjet Console.
📍 In the Cursor IDE: open View › Output and select the Hooks channel to confirm the Arcjet commands loaded. Then start an agent session and ask it to list a directory. Open the Arcjet Console › Activity and confirm the session and tool call appear.
Supports four deployment scopes — Enterprise MDM MDM Mobile device management, centrally configuring and managing enrolled devices. Administrators use it to apply settings, distribute applications and enforce device security policies. (/Library/Application Support/Cursor/hooks.json, /etc/cursor/hooks.json, C:\ProgramData\Cursor\hooks.json), Team dashboard, Project (.cursor/hooks.json), and User (~/.cursor/hooks.json) — with higher-priority sources overriding lower ones. Enables policy enforcement over Cursor tool calls (Shell , Read , Write , Grep , Delete , Task , MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. ), mapping them to Arcjet kinds shell, file_read, file_write, and agent. Supports denial responses for pre-tool-use ({ "permission": "deny", "user_message": "RULE_ID", "agent_message": "RULE_ID" }) and user-prompt-submit ({ "continue": false, "user_message": "RULE_ID" }) events. Integrates with Cursor cloud agents by committing .cursor/hooks.json and .cursor/hooks/arcjet-hook.sh to the repository; requires adding decide.arcjet.com to egress allowlists when network egress is restricted.
4 details in Everything
Arcjet: Supports omitting the surface parameter in hook URLs, with Arcjet recording unknown when absent, to avoid mislabeling traffic from terminal, IDE extensions, Desktop, and cloud surfaces that share a single managed-settings file.
Arcjet launches agent runtime security with observe, enforce, and audit capabilities for AI agents.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Guard a payment refund tool so the agent can never execute it when prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. is detected on a destructive action.
rego
copy
package arcjet.guard
import rego.v1
deny contains "injection-on-destructive-tool" if {
input.signals.prompt_injection.incoming_message.detected
input.values.destructive
}
Wrap an existing refund tool with Arcjet Guards in a JS agent so the tool never runs on a DENY decision.
javascript
copy
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY });
const refund = guardTool(arcjet, refundTool, {
action: "order.refunded",
actor: user.id,
inputs: ({ amount }) => ({
amount: policyInput.server.number(amount),
role: policyInput.server.string(user.role),
}),
});
Supports ingesting agent activity via OpenTelemetry OpenTelemetry An open standard and set of tools for collecting traces, metrics and logs from applications. It lets services export observability data without tying instrumentation to one monitoring vendor. events without code changes, enabling real-time visualization and analysis in the Arcjet Console.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Pulls agent activity data from the Claude Compliance API for teams using Claude. Introduces Arcjet Guards — Rego and Open Policy Agent-powered deterministic policies applied to tools, APIs, database calls, and input variables, configurable via the Arcjet Console, CLI, MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. , or API without code redeployment. Adds native integrations for agent frameworks including Claude Agents SDK, Claude Managed Agents, OpenAI Agents SDK, LangChain, LangFuse, Strands, Mastra, and Microsoft's Agent Framework. Collects audit evidence for every policy execution — including inputs, results, and session metadata — with options for cloud, single-tenant, or private VPC VPC A logically isolated virtual network inside a public cloud provider's infrastructure, letting tools deploy resources with private addressing, controlled routing, and firewall rules without managing physical hardware. storage, and real-time log export to external detection systems.
5 details in Everything
Introducing Agent Runtime Security: Observe, Enforce, Audit (2026-09-17)
Sep 17, 2026
· release notes
Arcjet introduces Agent Runtime Security with OpenTelemetry OpenTelemetry An open standard and set of tools for collecting traces, metrics and logs from applications. It lets services export observability data without tying instrumentation to one monitoring vendor. ingestion, Rego policy enforcement via API/CLI/MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. , and native SDKs for multiple agent frameworks.
└──▷ USE IT
Define a Rego policy to prevent external email recipients and bulk attachments for agent tool calls.
text
copy
package arcjet.guard
deny contains "external-recipient" if {
parts := split(input.values.recipient, "@")
count(parts) == 2
not lower(parts[1]) in input.values.team_domains
}
deny contains "bulk-attachments" if {
input.values.attachments > 10
}
Guard a refund tool in JS to enforce policy before execution using the native SDK.
javascript
copy
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const refund = guardTool(arcjet, refundTool, {
action: "order.refunded",
actor: user.id,
inputs: ({ amount }) => ({
amount: policyInput.server.number(amount),
role: policyInput.server.string(user.role),
}),
});
Ingests agent activity using OpenTelemetry events or the Claude Compliance API for real-time observation Enforces deterministic security policies defined in Rego via Arcjet's Console, MCP, CLI, or API to control tools, APIs, and database calls Provides native SDK integrations for JS, Python, and Go with support for Claude Agents SDK, OpenAI Agents SDK, LangChain, LangFuse, Strands, Mastra, and Microsoft’s Agent Framework
3 details in Everything
Arcjet adds a Guards policy authoring system with visual builder, plain-English generation, Rego Rego A declarative policy language created by the Open Policy Agent project, used to express authorization and compliance rules as code so cyber tools can evaluate structured data against those rules consistently. editor, and a full MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. server API︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
Adds list-guard-policies, get-guard-policy, validate-guard-policy, evaluate-guard-policy, describe-guard-policy, suggest-guard-policies, put-guard-policy, delete-guard-policy, and retry-guard-policy MCP server tools for authoring, validating, and publishing guard policies from a coding agent.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds a guard() SDK call (generated per-policy in JavaScript, Go, and Python) that sends typed inputs to evaluate a named policy by label at runtime. Introduces Guard policies — site-scoped, label-selected rules that change what a guarded action can do without modifying application code, authored in the Arcjet Console or over MCP. Adds a visual policy builder for authoring flat condition lists over declared SERVER inputs and detector results, with AND/OR joining and type-appropriate comparisons, generating read-only Rego automatically. Adds a plain-English policy authoring mode that turns a natural-language description into a builder specification (and then deterministically into Rego), with every generated rule defaulting to dry run before publication.
+6 more
show less
Adds traffic-derived policy suggestions that propose rule sets and Guard labels from recorded input shapes (names, kinds, exposures) without involving a model. Adds stored policy tests — JSON documents supplying a sample inputJson and expectRuleIds — that are versioned with the policy revision and run as a publication gate via the official Open Policy Agent evaluator. Adds validate and evaluate operations: validate compiles the policy and runs all stored tests reporting every failure with source position; evaluate runs one sample input through the Open Policy Agent evaluator without writing anything. Adds a four-gate publication pipeline that compiles the policy, runs stored tests through Open Policy Agent, cross-checks the edge interpreter, then mirrors the compiled artifact to the edge with an acknowledgement record. Adds immutable policy revisions recording the compiled bundle and compiler/profile versions, with rollback that republishes the original bytes. Supports two policy kinds: application policies (selected by the label a guard() call passes, inputs declared by the application) and coding agent policies (selected by tool call or prompt, inputs built from the agent hook payload).
11 details in Everything
Arcjet introduces Rego Rego A declarative policy language created by the Open Policy Agent project, used to express authorization and compliance rules as code so cyber tools can evaluate structured data against those rules consistently. -based agent guard policies evaluated at the edge with a sandboxed OPA OPA Open Policy Agent, a CNCF-graduated policy engine that evaluates rules written in the Rego language against JSON-structured data, letting tools enforce authorization and compliance decisions without hardcoding logic. subset and fail-closed semantics.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Block outbound emails containing a credit card number in the body when the recipient is outside your team's domains — combining a sensitive_info detector result with a domain membership check.
rego
copy
package arcjet.guard
import rego.v1
deny contains "card-number-to-external" if {
"CREDIT_CARD_NUMBER" in input.signals.sensitive_info.body_scan.entities
not lower(split(input.values.recipient, "@")[1]) in input.values.team_domains
}
Block a prompt-injection prompt-injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. finding only when the agent tool is destructive — letting the same detection pass on read-only tools to avoid over-blocking.
rego
copy
package arcjet.guard
import rego.v1
deny contains "injection-on-destructive-tool" if {
input.signals.prompt_injection.incoming_message.detected
input.values.destructive
}
Safely roll out a new bulk-attachment rule in dry-run mode so it is evaluated and logged without blocking any calls until you are confident in the logic.
rego
copy
package arcjet.guard
import rego.v1
# Rule declared with mode: DRY_RUN in the policy config
deny contains "bulk-attachments" if {
input.values.attachments > 5
}
Supports writing agent guard policies in Rego (Open Policy Agent language) under the arcjet.guard package, compiled to an OPA intermediate representation at publish time and interpreted at the edge on every guard call.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Policies read a structured input document with four top-level fields: label, actor, values (declared server-side inputs), and signals (detector results keyed by detector ID). Detector results are readable under input.signals.<detector-id>.<input-name>.detected and .entities, enabling context-sensitive logic (e.g. block prompt_injection only when input.values.destructive is also true). Policy rules must use the partial-set form deny contains "<rule-id>" if { … } inside package arcjet.guard; using a complete rule (deny := …) is rejected with AJR1010, and undeclared or computed rule IDs fail publication with AJR1011. Enforces a versioned built-in allowlist covering comparison, membership (in, some, every), arithmetic (excluding div and rem), strings (concat, split, lower, upper, startswith, endswith, contains, indexof, substring, trim*, replace, strings.reverse), collections (count, sort, all, any, array.*, intersection, union, set_diff, comprehensions), objects (object.get/keys/remove/union/filter/subset), type checks, and conversion (to_number, format_int); everything else — including regex.*, http.send, time, rand, sprintf, json, crypto, io.jwt, print, trace, and with — is rejected at validation time.
+5 more
show less
Evaluation is metered per guard call to 100,000 interpreter steps, 100,000 collection elements, and a block depth of 64; exceeding any limit causes a live rule to fail closed. Built-in errors (e.g. empty search string in indexof, wrong-type operand) abort evaluation and report AJP1007 on the rule, failing closed for live rules; other evaluation faults report AJP1006. Requires stored tests (sample input document + expected rule IDs) for any policy with a live expression rule; publication is blocked with AJV2015 if none exist. Dry-run-only policies can be saved without tests. Supports DRY_RUN mode per rule: evaluated and recorded without contributing to the deny decision, enabling safe rollout of new policy logic. A LIVE rule that depends on a detector that produced no verdict reports AJP1004 and is not evaluated, preventing a missing signal from being silently treated as not detected .
10 details in Everything
Arcjet adds agent activity observability via OpenTelemetry OpenTelemetry An open standard and set of tools for collecting traces, metrics and logs from applications. It lets services export observability data without tying instrumentation to one monitoring vendor. ingest and Claude Compliance API polling — no code changes required.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ TRY IT
Stream Claude Code audit telemetry to Arcjet from a developer workstation, including prompt text and tool arguments for a full audit trail.
copy
$ export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_ENDPOINT=https://decide.arcjet.com
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer ARCJET_KEY"
export OTEL_LOG_USER_PROMPTS=1
export OTEL_LOG_TOOL_DETAILS=1
Roll out OTLP OTLP OpenTelemetry Protocol, a standard way to send traces, metrics and logs between applications, collectors and monitoring systems. It lets telemetry move across compatible tools. telemetry to every developer in the org without relying on individual shell configs — ship via Claude Code managed settings.
json
copy
{
"env": {
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
"OTEL_LOGS_EXPORTER": "otlp",
"OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
"OTEL_EXPORTER_OTLP_ENDPOINT": "https://decide.arcjet.com",
"OTEL_EXPORTER_OTLP_HEADERS": "Authorization=Bearer ARCJET_KEY",
"OTEL_LOG_USER_PROMPTS": "1",
"OTEL_LOG_TOOL_DETAILS": "1"
}
}
Connect the Claude Compliance API so Arcjet polls Anthropic for your org's session activity and transcripts with no per-client configuration.
📍 In the Arcjet Console: open your team, then go to Settings › Integrations › Claude Compliance. Select your Claude product, enter your Compliance Access Key (prefix: sk-ant-api01-) or Admin API key (prefix: sk-ant-admin01-), select the destination site, then select Connect.
Adds CLAUDE_CODE_ENABLE_TELEMETRY, OTEL_LOGS_EXPORTER, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS, OTEL_LOG_USER_PROMPTS, and OTEL_LOG_TOOL_DETAILS environment variables to stream Claude Code audit telemetry to Arcjet's OTLP ingest endpoint at https://decide.arcjet.com.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Accepts OTLP exports over HTTP (http/protobuf and http/json) at https://decide.arcjet.com — logs via POST /v1/logs become capture events, traces via POST /v1/traces become capture events (best effort), and metrics via POST /v1/metrics are acknowledged but not recorded. Supports org-wide rollout of Claude Code telemetry through Claude Code managed settings, shipping the OTLP env vars centrally instead of relying on each developer's shell. Adds Claude Compliance API integration: paste one Anthropic key (sk-ant-api01- Compliance Access Key or sk-ant-admin01- Admin API Admin API A dedicated HTTP interface that exposes management and configuration operations separately from the data plane, letting cyber tools automate provisioning, policy changes, or monitoring without touching the main user-facing service. key) in Console under Settings > Integrations > Claude Compliance and Arcjet polls Anthropic for organization activity and session transcripts. Ingested agent activity (from OpenTelemetry or the Compliance API) surfaces in the Console Activity view alongside guard and protect decisions, with sender, asserted principal, and correlation ID to follow a session end-to-end.
+1 more
show less
Captures OTLP record fields into Arcjet capture events: action maps to event name, session.id maps to correlationId, all other attributes map to metadata with types preserved; metadata limits are 128 keys, 32 KiB per serialized value, depth of 10, with security-relevant keys retained first on truncation.
6 details in Everything
Arcjet: Adds a response field on guardApproval() accepting a GuardApprovalResponsePolicy (action, rules, metadata, onGuardError) to authorize and rate-limit who may approve a parked HITL HITL Human in the loop, involving a person in an otherwise automated workflow. The person can review, approve, correct or supply information before work continues. request.
└──▷ USE IT
Block prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. on an inbound Eve channel webhook before the agent session starts, using the conversation ID as a stable correlation handle.
javascript
copy
import { defineChannel, POST } from 'eve/channels';
import { detectPromptInjection } from '@arcjet/guard';
import { guardInbound } from '@arcjet/guard/vercel-eve/v0';
import { arcjet } from '../arcjet.js';
export default defineChannel({
routes: [
POST('/webhook', async (req, args) => {
const { message, conversationId } = await req.json();
const verdict = await guardInbound(arcjet, message, {
rules: [detectPromptInjection()(message)],
action: 'message.received',
correlationId: conversationId,
});
if (!verdict.allowed) {
return new Response(JSON.stringify({ error: verdict.message }), {
status: verdict.outcome === 'UNAVAILABLE' ? 503 : 403,
});
}
const session = await args.from(conversationId).send(message, { auth: null });
return new Response(JSON.stringify({ sessionId: session.id }));
}),
],
});
Rate-limit per-session calls to an OpenAPI OpenAPI A vendor-neutral specification for describing REST APIs in a machine-readable format, maintained by the OpenAPI Initiative. Cyber tools use it to auto-generate clients, documentation, and validation from a single source of truth. connection and require a human approver, then rate-limit the approver too.
javascript
copy
import { defineOpenAPIConnection } from 'eve/connections';
import { tokenBucket } from '@arcjet/guard';
import { guardApproval } from '@arcjet/guard/vercel-eve/v0';
import { arcjet } from '../arcjet.js';
const sessionLimit = tokenBucket({ bucket: 'weather-session', refillRate: 10, intervalSeconds: 60, maxTokens: 10 });
const approverLimit = tokenBucket({ bucket: 'weather-approver', refillRate: 5, intervalSeconds: 60, maxTokens: 5 });
export default defineOpenAPIConnection({
description: 'Weather API',
spec: 'https://api.example.com/openapi.json',
approval: guardApproval(arcjet, {
action: 'weather.fetched',
rules: (ctx) => [sessionLimit({ key: ctx.session.id, requested: 1 })],
onAllow: 'user-approval',
response: {
action: 'weather.approved',
rules: (ctx) => [approverLimit({ key: ctx.responder.principalId, requested: 1 })],
},
}),
operations: { allow: ['GetForecast'] },
});
Scaffold the Arcjet Guard Eve integration automatically inside a Claude Code agent session.
copy
$ mkdir -p ~/.claude/skills
cp -r node_modules/@arcjet/guard/skills/integrate-arcjet-guard-eve ~/.claude/skills/
# Then inside Claude Code:
# /integrate-arcjet-guard-eve
Adds a packaged coding-agent skill at node_modules/@arcjet/guard/skills/integrate-arcjet-guard-eve — copy it to ~/.claude/skills/ and run /integrate-arcjet-guard-eve in Claude Code to scaffold the integration automatically.
1 detail in Everything
Arcjet: Adds @arcjet/guard/vercel-ai/v7 export (no unversioned @arcjet/guard/vercel-ai alias) requiring Node.js >=22.21.0 <23 || >=24.5.0, ai >=7 <8, and @ai-sdk/provider-utils >=5 <6 as optional peers.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ TRY IT
Rate-limit a model-invoked email tool per user so the model receives a structured denial — not an exception — when the bucket is exhausted.
copy
$ import { launchArcjet, tokenBucket, policyInput } from '@arcjet/guard';
import { guardTool, aiToolsContext, createAgentContext } from '@arcjet/guard/vercel-ai/v7';
import { generateText, stepCountIs, tool } from 'ai';
import { z } from 'zod';
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const emailLimit = tokenBucket({ bucket: 'email', refillRate: 5, intervalSeconds: 60, maxTokens: 10 });
const sendEmail = guardTool(
arcjet,
tool({
description: 'Send an email',
inputSchema: z.object({ recipient: z.string().email(), body: z.string() }),
execute: ({ recipient, body }) => emailProvider.send({ to: recipient, body }),
}),
{
action: 'email.sent',
actor: (_, ctx) => ctx.actor,
rules: (input) => [emailLimit({ key: input.recipient, requested: 1 })],
inputs: ({ recipient, body }) => ({
recipient: policyInput.server.string(recipient),
body: policyInput.local.string(body),
}),
onGuardError: 'deny',
},
);
const context = createAgentContext({ correlationId: userId });
return generateText({
model: 'openai/gpt-4o-mini',
prompt,
tools: { sendEmail },
toolsContext: aiToolsContext(context, { sendEmail }),
stopWhen: stepCountIs(10),
});
Screen a user prompt for injection attacks before handing it to the model, failing closed if the Guard call fails open.
copy
$ import { launchArcjet, detectPromptInjection } from '@arcjet/guard';
import { createAgentContext } from '@arcjet/guard/vercel-ai/v7';
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const context = createAgentContext({ correlationId: conversationId });
const inbound = detectPromptInjection();
const decision = await arcjet.guard({
label: 'message.received',
rules: [inbound(prompt)],
correlationId: context.correlationId,
});
if (decision.conclusion === 'DENY' || decision.hasFailedOpen()) {
throw new Error('message blocked');
}
Install the Claude Code skill so an agent can automatically wire Arcjet Guard into a Vercel AI SDK project.
copy
$ npm install @arcjet/guard ai @ai-sdk/provider-utils
mkdir -p ~/.claude/skills
cp -r node_modules/@arcjet/guard/skills/integrate-arcjet-guard-agents ~/.claude/skills/
Adds createAgentContext() helper to build a typed agent context with a correlationId (1–256 printable ASCII characters; auto-generates a ULID if omitted) and metadata. Adds captureAction() helper to record that an action occurred without blocking — never denies. Adds guardTool() option inputs accepting policyInput.server.* or policyInput.local.* builders (e.g. policyInput.server.string() , policyInput.server.stringList() ) to pass named values readable by remote policies under input.values. Adds detectPromptInjection() rule from @arcjet/guard for use with direct arcjet.guard() calls to screen inbound prompts before generateText, with decision.hasFailedOpen() to detect fail-open outcomes.
4 details in Everything
Arcjet: Supports per-call dynamic action, rules, actor, inputs, metadata, and sessionId options on guardMiddleware() as functions of { toolName, input } for per-tool policy customization.
└──▷ USE IT
Gate every tool call in a TanStack AI chat with prompt-injection prompt-injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. screening on inbound text and per-tool rate limiting and PII PII Personally Identifiable Information — data that can identify a specific individual, such as names, email addresses, or SSNs. Cyber tools flag or redact it to limit exposure and meet privacy regulations. detection on tool arguments.
typescript
copy
import { launchArcjet, detectPromptInjection, localDetectSensitiveInfo, tokenBucket } from "@arcjet/guard";
import { guardMiddleware, tanstackAiContext } from "@arcjet/guard/tanstack-ai/v0";
import { chat, toolDefinition } from "@tanstack/ai";
import { z } from "zod";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 10, intervalSeconds: 60, maxTokens: 10 });
const detectPii = localDetectSensitiveInfo({ deny: ["EMAIL", "PHONE_NUMBER", "IP_ADDRESS", "CREDIT_CARD_NUMBER"] });
const inbound = detectPromptInjection();
const appContext = { sessionId: conversationId };
// Screen inbound text before chat()
const decision = await arcjet.guard({
label: "message.received",
rules: [inbound(userText)],
...tanstackAiContext({ context: appContext }),
});
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) {
throw new Error("message blocked");
}
// Gate tool calls with guardMiddleware
return chat({
adapter,
messages: [{ role: "user", content: userText }],
tools: [lookupOrder],
context: appContext,
middleware: [
guardMiddleware(arcjet, {
action: "order.looked-up",
sessionId: conversationId,
onGuardError: "deny",
rules: ({ toolName, input }) => {
if (toolName !== "lookup_order") return [];
const { orderNumber, note } = lookupOrderInput.parse(input);
return [lookupLimit({ key: orderNumber, requested: 1 }), detectPii(note)];
},
}),
],
});
Stop the entire chat run (rather than just skipping the tool) when Arcjet denies a tool call — useful when downstream steps depend on the tool's result.
typescript
copy
import { guardMiddleware } from "@arcjet/guard/tanstack-ai/v0";
import { chat } from "@tanstack/ai";
import { arcjet } from "./arcjet.js";
chat({
adapter,
messages: [{ role: "user", content: userText }],
tools: [lookupOrder],
context: { sessionId: conversationId },
middleware: [
guardMiddleware(arcjet, {
sessionId: conversationId,
onDeny: "abort",
}),
],
});
Installs via @arcjet/guard with @tanstack/ai (>=0.8.0 <1) as an optional peer, requiring Node.js >=22.21.0 <23 || >=24.5.0.
1 detail in Everything
Arcjet: Adds @arcjet/guard/strands-agents/v1 (JS/TS) adapter — import path @arcjet/guard/strands-agents/v1 — exposing guardTool() , guardHooks() , and strandsAgentContext() to enforce Arcjet policy on Strands Agents authored and MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. tools before their callbacks execute.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Gate an authored Python Strands tool so the handler never runs when Arcjet returns DENY — for example to enforce rate limits on an email-sending tool.
python
copy
from arcjet.guard.strands_agents import guard_tool
from arcjet.guard import launch_arcjet
from strands import tool
arcjet = launch_arcjet(key=ARCJET_KEY)
@tool
def send_email(to: str, body: str) -> str:
# side-effecting work here
...
guarded_send_email = guard_tool(
guard=arcjet,
tool=send_email,
action="email.sent",
on_guard_error="deny",
)
Gate all MCP and unwrapped tools in a JS agent with a single hooks plugin so no tool call fires before policy clears it.
typescript
copy
import { launchArcjet, guardHooks } from '@arcjet/guard/strands-agents/v1';
import { Agent } from '@strands-agents/sdk';
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const agent = new Agent({
tools: [myMcpTool, myOtherTool],
plugins: [
guardHooks(arcjet, {
action: ({ toolName }) => `${toolName}.invoked`,
onGuardError: 'deny',
}),
],
});
await agent.invoke('Send a summary email to the team');
Adds arcjet.guard.strands_agents (Python) adapter — installed via arcjet[strands-agents] — exposing guard_tool, guard_hooks, and strands_agent_context for the same enforcement surface in Python agents. Adds guardTool() helper that wraps a Strands tool({ callback }) and returns a plain ArcjetDenialResult on DENY without throwing or calling event.interrupt() . Adds guardHooks() Plugin that registers BeforeToolCallEvent as an invoke-wide gate, setting event.cancel to a JSON-stringified ArcjetDenialResult on DENY; skips tools already wrapped with guardTool() so MCP and unwrapped tools are still gated. Adds guard_tool Python helper that gates an authored @tool / DecoratedFunctionTool; on DENY or when on_guard_error is 'deny' returns an ArcjetDenialResult dict and never invokes the handler. Adds guard_hooks Python hook provider (passed via Agent(hooks=[...]) ) that sets event.cancel_tool to a JSON-stringified ArcjetDenialResult on DENY, with fail-closed default on guard error.
+1 more
show less
Requires Node.js >=22.21.0 <23 || >=24.5.0 and @strands-agents/sdk >=1.1.0 <2 for the JS adapter; Python adapter requires arcjet 1.1.0 or later, strands-agents>=1.11.0,<2, and CPython 3.10 or later.
6 details in Everything
Arcjet: Adds @arcjet/guard/openai-agents/v0 JavaScript adapter that wraps FunctionTool.invoke via guardTool() , blocking the authored handler on DENY before any side effect runs.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Gate an authored function-tool handler so the side effect never runs when Arcjet denies the call, such as blocking a rate-limited email-send tool.
javascript
copy
import { launchArcjet } from "@arcjet/guard";
import { guardTool } from "@arcjet/guard/openai-agents/v0";
import { tool } from "@openai/agents";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const sendEmail = tool({
name: "send_email",
execute: async ({ to, body }) => { /* sends email */ },
});
export const guardedSendEmail = guardTool(arcjet, sendEmail, {
action: "email.sent",
onGuardError: "deny",
});
Screen user text for prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. before starting an OpenAI Agents run, blocking the run on DENY or a failed-open decision.
python
copy
from agents import Runner
from arcjet.guard import DetectPromptInjection, launch_arcjet
from arcjet.guard.openai_agents import openai_agents_context
arcjet = launch_arcjet(key=ARCJET_KEY)
inbound = DetectPromptInjection()
app_context = {"session_id": conversation_id}
derived = openai_agents_context(app_context)
decision = await arcjet.guard(
label="message.received",
rules=[inbound(user_text)],
correlation_id=derived.correlation_id,
)
if decision.conclusion == "DENY" or decision.has_failed_open():
raise RuntimeError("message blocked")
await Runner.run(agent, user_text, context=app_context)
Arcjet: Adds actor and inputs options to guardTool, guardProcessor, and guardHooks, accepting a value or a per-call resolver; inputs must be built with policyInput.server.* or policyInput.local.* — plain values are rejected.
└──▷ USE IT
Screen every inbound prompt and outbound response for injection attacks on a Mastra agent using guardProcessor in inputProcessors/outputProcessors.
javascript
copy
import { Agent } from '@mastra/core/agent';
import { guardProcessor } from '@arcjet/guard/mastra/v1';
import { detectPromptInjection } from '@arcjet/guard';
import { arcjet } from './arcjet.js';
const inbound = guardProcessor(arcjet, {
action: 'message.received',
rules: ({ text }) => [detectPromptInjection()(text)],
});
const outbound = guardProcessor(arcjet, {
action: 'message.completed',
rules: ({ text }) => [detectPromptInjection()(text)],
});
export const agent = new Agent({
id: 'support-agent',
name: 'support-agent',
instructions: 'Help the user.',
model: 'openai/gpt-4o',
inputProcessors: [inbound],
outputProcessors: [outbound],
});
Rate-limit and PII PII Personally Identifiable Information — data that can identify a specific individual, such as names, email addresses, or SSNs. Cyber tools flag or redact it to limit exposure and meet privacy regulations. -scan a Mastra authored tool so sensitive free-text args are checked and the model receives a structured denial rather than a generic error.
javascript
copy
import { createTool } from '@mastra/core/tools';
import { z } from 'zod';
import { guardTool } from '@arcjet/guard/mastra/v1';
import { tokenBucket, localDetectSensitiveInfo } from '@arcjet/guard';
import { arcjet } from './arcjet.js';
const lookupLimit = tokenBucket({ bucket: 'lookups', refillRate: 10, intervalSeconds: 60, maxTokens: 20 });
const detectPii = localDetectSensitiveInfo({ deny: ['EMAIL', 'PHONE_NUMBER', 'IP_ADDRESS', 'CREDIT_CARD_NUMBER'] });
export const lookupOrder = guardTool(
arcjet,
createTool({
id: 'lookup-order',
description: 'Look up an order by ID',
inputSchema: z.object({ orderId: z.string(), note: z.string() }),
async execute({ orderId, note }) { return { orderId, note, status: 'shipped' }; },
}),
{
action: 'order.looked-up',
rules: (input) => [lookupLimit({ key: input.orderId, requested: 1 }), detectPii(input.note)],
},
);
Enforce per-tool rate limits on MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. tools the agent did not author, using guardHooks with a dynamic action slug per tool name.
javascript
copy
import { guardHooks } from '@arcjet/guard/mastra/v1';
import { tokenBucket } from '@arcjet/guard';
import { arcjet } from './arcjet.js';
const mcpLimit = tokenBucket({ bucket: 'mcp-access', refillRate: 5, intervalSeconds: 60, maxTokens: 10 });
export const hooks = guardHooks(arcjet, {
action: ({ toolName }) => `${toolName}.invoked`,
rules: ({ toolName }) => [mcpLimit({ key: toolName, requested: 1 })],
});
// Pass hooks to the Agent constructor:
// new Agent({ ..., ...hooks })
Supports MASTRA_THREAD_ID_KEY and MASTRA_RESOURCE_ID_KEY set on RequestContext before generate/stream to correlate agent turns without calling createAgentContext.
1 detail in Everything
Arcjet: Adds a @arcjet/guard/langgraph/v1 import path (versioned, no unversioned alias) for the LangGraph adapter, requiring @langchain/langgraph >=1 <2, @langchain/core >=1 <2, and Node.js >=22.21.0 <23 || >=24.5.0.
└──▷ USE IT
Enforce a rate-limit and PII PII Personally Identifiable Information — data that can identify a specific individual, such as names, email addresses, or SSNs. Cyber tools flag or redact it to limit exposure and meet privacy regulations. detection policy on an authored tool before it executes — deny the call without throwing so ToolNode can wrap the result into a valid ToolMessage.
text
copy
import { tool } from "@langchain/core/tools";
import { z } from "zod";
import { guardTool, langgraphAgentContext } from "@arcjet/guard/langgraph/v1";
import { tokenBucket, localDetectSensitiveInfo } from "@arcjet/guard";
import { arcjet } from "./arcjet.js";
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 5, intervalSeconds: 60, maxTokens: 10 });
const detectPii = localDetectSensitiveInfo({ deny: ["EMAIL", "PHONE_NUMBER", "CREDIT_CARD_NUMBER"] });
export const lookupOrder = guardTool(
arcjet,
tool(
async ({ orderNumber, note }) => ({ orderNumber, note, status: "shipped" }),
{
name: "lookup_order",
description: "Look up an order by number",
schema: z.object({ orderNumber: z.string(), note: z.string() }),
},
),
{
action: "order.looked-up",
rules: (input) => [lookupLimit({ key: input.orderNumber, requested: 1 }), detectPii(input.note)],
},
);
Guard all tools in a ToolNode (including MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. tools discovered at runtime) under a single rate-limit policy, using a per-tool action label.
text
copy
import { ToolNode } from "@langchain/langgraph/prebuilt";
import { guardToolNode } from "@arcjet/guard/langgraph/v1";
import { tokenBucket } from "@arcjet/guard";
import { arcjet } from "./arcjet.js";
const mcpLimit = tokenBucket({ bucket: "mcp-access", refillRate: 10, intervalSeconds: 60, maxTokens: 20 });
export const tools = guardToolNode(
arcjet,
new ToolNode([lookupOrder, ...mcpTools]),
{
action: ({ toolName }) => `${toolName}.invoked`,
rules: ({ toolName }) => [mcpLimit({ key: toolName, requested: 1 })],
},
);
// Then: graph.addNode("tools", tools);
Screen a user message for prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. before handing it to the graph, failing closed if the guard could not be evaluated.
text
copy
import { detectPromptInjection } from "@arcjet/guard";
import { langgraphAgentContext } from "@arcjet/guard/langgraph/v1";
import { arcjet } from "./arcjet.js";
const config = { configurable: { thread_id: conversationId } };
const inbound = detectPromptInjection();
const decision = await arcjet.guard({
label: "message.received",
rules: [inbound(userText)],
...langgraphAgentContext(config),
});
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) {
throw new Error("message blocked");
}
await graph.invoke({ messages: [{ role: "user", content: userText }] }, config);
Arcjet adds LangChain agent guard adapter for JS/TS and Python, enforcing policy at every tool call boundary︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Gate an individual LangChain tool so the action never runs if Arcjet policy denies it — drop-in replacement requiring no downstream changes.
javascript
copy
import { guardTool } from '@arcjet/guard/langchain/v1';
import { tool } from '@langchain/core/tools';
import { z } from 'zod';
import { arcjet } from './arcjet.js';
const sendEmail = tool(
async ({ to, body }) => { /* send email */ },
{ name: 'send_email', schema: z.object({ to: z.string(), body: z.string() }) }
);
const guardedSendEmail = guardTool(arcjet, sendEmail, {
action: 'email.sent',
});
Apply per-tool policies across a Python agent, refusing misconfigured tool names at init time and enforcing rate-limit rules at runtime.
python
copy
from arcjet.guard import launch_arcjet
from arcjet.guard.langchain import ArcjetMiddleware, ToolPolicy, guard_tool
from langchain_openai import ChatOpenAI
arcjet = launch_arcjet(key=ARCJET_KEY)
middleware = ArcjetMiddleware(
guard=arcjet,
policies={
'send_email': ToolPolicy(action='email.sent'),
'query_db': ToolPolicy(action='database.queried'),
},
tools=[send_email_tool, query_db_tool],
)
agent = create_agent(model=ChatOpenAI(model='gpt-4o'), tools=[send_email_tool, query_db_tool], middleware=middleware)
Adds @arcjet/guard/langchain/v1 JavaScript/TypeScript adapter (requires @arcjet/guard 1.11.0+, Node.js >=22.21.0 <23 || >=24.5.0) exposing guardTool() , guardMiddleware() , and langchainContext() to enforce policy before LangChain tool calls run.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds arcjet[langchain] Python extra providing guard_tool, ArcjetCaptureHandler, and ArcjetAsyncCaptureHandler for wrapping individual BaseTool instances and observing chains without blocking. Adds arcjet[langchain-agents] Python extra providing ArcjetMiddleware and ToolPolicy for agent-level gating via create_agent (requires langchain>=1.3,<2 and langgraph>=1.2,<2). Adds guardMiddleware() JS/TS helper passed via createAgent({ middleware }) whose wrapToolCall hook gates every tool invocation, returning a real ToolMessage with JSON denial payload on DENY. Adds langchainContext() JS/TS helper that resolves configurable.thread_id, then caller-owned sessionId/conversationId, then init.sessionId/init.correlationId for consistent session correlation across interrupted/resumed runs.
+3 more
show less
Adds guard_tool Python helper that raises ArcjetToolDeniedError on denial and ArcjetToolUnavailableError when Guard is unavailable, honoring the tool's existing handle_tool_error behavior. Adds ArcjetMiddleware Python helper accepting guard, policies (tool-name-to-ToolPolicy map), and tools (same sequence passed to create_agent) for agent-wide policy enforcement. Supports inbound prompt-injection prompt-injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. screening via direct client.guard({ label, rules, ...langchainContext(config) }) with decision.hasFailedOpen() check before calling agent.invoke or agent.stream.
8 details in Everything
Arcjet: Adds guardPlugin() from @arcjet/guard/google-adk/v2 as a Runner-wide policy gate that hooks beforeToolCallback on new Runner({ plugins }) or new InMemoryRunner({ plugins }) , returning an ArcjetDenialResult deny dict to block tool execution or undefined to allow it — without throwing.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ TRY IT
Gate every tool call in a Google ADK ADK Google's Agent Development Kit, an open-source framework for building and orchestrating AI agents; cyber tools use it to compose multi-agent workflows with built-in tool-calling and state management. runner with prompt-injection prompt-injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. screening on inbound text and rate-limiting + PII PII Personally Identifiable Information — data that can identify a specific individual, such as names, email addresses, or SSNs. Cyber tools flag or redact it to limit exposure and meet privacy regulations. detection on tool arguments.
copy
$ import { launchArcjet, detectPromptInjection, localDetectSensitiveInfo, tokenBucket } from "@arcjet/guard";
import { guardPlugin, googleAdkContext } from "@arcjet/guard/google-adk/v2";
import { FunctionTool, InMemoryRunner, LlmAgent } from "@google/adk";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 10, intervalSeconds: 60, maxTokens: 100 });
const detectPii = localDetectSensitiveInfo({ deny: ["EMAIL", "PHONE_NUMBER", "CREDIT_CARD_NUMBER"] });
const inbound = detectPromptInjection();
// Screen user text before runAsync
const appContext = { sessionId: conversationId };
const decision = await arcjet.guard({
label: "message.received",
rules: [inbound(userText)],
...googleAdkContext(appContext),
});
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) throw new Error("message blocked");
// Run with guardPlugin gating all tool calls
const runner = new InMemoryRunner({
agent,
appName: "orders",
plugins: [
guardPlugin(arcjet, {
action: "order.looked-up",
sessionId: conversationId,
onGuardError: "deny",
rules: ({ toolName, input }) => [
lookupLimit({ key: input.orderNumber, requested: 1 }),
detectPii(input.note),
],
}),
],
});
await runner.runAsync({ userId: conversationId, sessionId: conversationId, newMessage: { parts: [{ text: userText }] } });
Arcjet: Adds the sessionId option on guardTool() and guardMiddleware() as a caller-owned fallback for tool-time correlation when the tool options or hook context do not carry one.
└──▷ USE IT
Guard a defineTool action with rate limiting and PII PII Personally Identifiable Information — data that can identify a specific individual, such as names, email addresses, or SSNs. Cyber tools flag or redact it to limit exposure and meet privacy regulations. detection so the handler never runs on a denied call.
typescript
copy
import { launchArcjet, tokenBucket, localDetectSensitiveInfo } from '@arcjet/guard';
import { guardTool } from '@arcjet/guard/genkit/v1';
import { genkit, z } from 'genkit';
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const ai = genkit({ /* model plugin */ });
const lookupLimit = tokenBucket({ bucket: 'lookups', refillRate: 10, intervalSeconds: 60, maxTokens: 10 });
const detectPii = localDetectSensitiveInfo({ deny: ['EMAIL', 'PHONE_NUMBER', 'IP_ADDRESS', 'CREDIT_CARD_NUMBER'] });
const lookupOrder = guardTool(
arcjet,
ai.defineTool(
{ name: 'lookup_order', description: 'Look up an order by number', inputSchema: z.object({ orderNumber: z.string(), note: z.string() }) },
async ({ orderNumber, note }) => ({ orderNumber, note, status: 'shipped' })
),
{
action: 'order.looked-up',
onGuardError: 'deny',
rules: (input) => [lookupLimit({ key: input.orderNumber, requested: 1 }), detectPii(input.note)],
}
);
Screen inbound user text for prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. before calling ai.generate() , failing closed if Guard is unavailable.
typescript
copy
import { detectPromptInjection } from '@arcjet/guard';
import { genkitContext } from '@arcjet/guard/genkit/v1';
import { arcjet } from './arcjet.js';
const inbound = detectPromptInjection();
const appContext = { sessionId: conversationId };
const decision = await arcjet.guard({
label: 'message.received',
rules: [inbound(userText)],
...genkitContext({ context: appContext }),
});
if (decision.conclusion === 'DENY' || decision.hasFailedOpen()) {
throw new Error('message blocked');
}
Gate every tool call in a generate() run — including injected or MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. tools — by attaching guardMiddleware via use.
typescript
copy
const response = await ai.generate({
prompt: userText,
tools: [lookupOrder],
use: [guardMiddleware(arcjet, { sessionId: conversationId })],
context: appContext,
});
Supports rules, actor, inputs, and metadata options on both guardTool() and guardMiddleware() , accepting static values or per-call resolver functions of the parsed tool input. Requires @arcjet/guard 1.11.0 or later, Genkit >=1.0.0 <2 (verified against 1.41.0, with guardMiddleware needing Genkit >= 1.33 for the generateMiddleware tool hook), and Node.js >=22.21.0 <23 || >=24.5.0.
2 details in Everything
Arcjet: Adds sanitize_tool_name helper that normalises tool names to match CrewAI's tools= filter convention (e.g. Send Email and send_email resolve to the same tool).
└──▷ USE IT
Gate every tool call a crew makes — block on PII PII Personally Identifiable Information — data that can identify a specific individual, such as names, email addresses, or SSNs. Cyber tools flag or redact it to limit exposure and meet privacy regulations. in free-text args and enforce a token-bucket rate limit — before any side effect runs.
python
copy
from arcjet.guard import LocalDetectSensitiveInfo, TokenBucket, launch_arcjet_sync
from arcjet.guard.crewai import free_text_arguments, register_arcjet_hooks
arcjet = launch_arcjet_sync(key=ARCJET_KEY)
lookup_limit = TokenBucket(refill_rate=1, interval_seconds=60, max_tokens=10, bucket="lookups")
detect_pii = LocalDetectSensitiveInfo(deny=["EMAIL", "PHONE_NUMBER", "CREDIT_CARD_NUMBER"])
handle = register_arcjet_hooks(
guard=arcjet,
tools=["lookup_order"],
action="order.looked-up",
rules=lambda arguments, _ctx: [
lookup_limit(key="orders", requested=1),
detect_pii(free_text_arguments(arguments)["note"]),
],
on_guard_error="deny",
)
Screen user-supplied text for prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. before handing off to a crew, failing closed if Guard cannot be evaluated.
python
copy
from arcjet.guard import DetectPromptInjection, launch_arcjet_sync
arcjet = launch_arcjet_sync(key=ARCJET_KEY)
inbound = DetectPromptInjection()
decision = arcjet.guard(
label="message.received",
rules=[inbound(user_text)],
)
if decision.conclusion == "DENY" or decision.has_failed_open():
raise RuntimeError("message blocked")
crew.kickoff(inputs={"request": user_text})
Wrap a standalone BaseTool you invoke directly (outside a crew) so Arcjet policy still runs and blocks on DENY.
python
copy
from arcjet.guard import TokenBucket, launch_arcjet_sync
from arcjet.guard.crewai import guard_tool
arcjet = launch_arcjet_sync(key=ARCJET_KEY)
lookup_limit = TokenBucket(refill_rate=1, interval_seconds=60, max_tokens=10, bucket="lookups")
guarded_lookup = guard_tool(
guard=arcjet,
tool=lookup_order,
action="order.looked-up",
rules=[lookup_limit(key="orders", requested=1)],
on_guard_error="deny",
)
guarded_lookup.run({"order_id": "ORD-42", "note": "priority customer"})
Adds ArcjetCrewAIHooks handle returned by register_arcjet_hooks, with a handle.unregister() method (also callable as unregister_arcjet_hooks(handle) ) to remove the process-wide hook, e.g. in tests. Supports actor, inputs, rules, metadata, correlation_id, and policies options on register_arcjet_hooks for attaching identity, typed remote-policy inputs, SDK rules, capture metadata, caller-owned sequence IDs, and per-tool overrides to every gated tool call.
2 details in Everything
└──▷ USE IT
Block prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. on every user turn before the Claude Managed Agent session ever sees the text — the only pre-model gate this harness exposes.
typescript
copy
import Anthropic from "@anthropic-ai/sdk";
import { detectPromptInjection } from "@arcjet/guard";
import { claudeManagedAgentsContext, guardEvents } from "@arcjet/guard/claude-managed-agents/v0";
import { arcjet } from "./arcjet.js";
const client = new Anthropic();
export async function sendTurn(sessionId: string, conversationId: string, userText: string) {
const inbound = await guardEvents(
arcjet,
{
events: [{ type: "user.message", content: [{ type: "text", text: userText }] }],
inbound: {
action: "message.received",
rules: ({ text }) => [detectPromptInjection()(text)],
},
context: claudeManagedAgentsContext({ correlationId: conversationId }),
},
(body) => client.beta.sessions.events.send(sessionId, body)
);
if (!inbound.allowed) {
return inbound.message;
}
}
Gate a custom tool your app executes so a DENY stops the handler and posts the denial result back to the session automatically.
typescript
copy
import { guardCustomTool, claudeManagedAgentsContext } from "@arcjet/guard/claude-managed-agents/v0";
import { arcjet } from "./arcjet.js";
const guardedSendEmail = guardCustomTool(arcjet, {
action: "email.sent",
context: claudeManagedAgentsContext({ correlationId: conversationId }),
onGuardError: "deny",
run: async (event) => {
// your custom tool logic here
},
});
Arcjet: Adds guardTool(client, toolDefinition, policy) from @arcjet/guard/claude-agent-sdk/v0 to wrap authored Claude Agent SDK tools, returning an MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. CallToolResult with isError: true and ArcjetDenialResult on structuredContent when the policy denies the call.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ TRY IT
Block prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. attacks before the model sees user input — gate every conversation turn at UserPromptSubmit using a per-conversation UUID UUID A universally unique identifier, usually a 128-bit value used to identify an object across systems. It is designed to make independently generated identifiers unlikely to collide. for correlation.
copy
$ import { query } from "@anthropic-ai/claude-agent-sdk";
import { randomUUID } from "node:crypto";
import { guardHooks } from "@arcjet/guard/claude-agent-sdk/v0";
import { detectPromptInjection } from "@arcjet/guard";
import { arcjet } from "./arcjet.js";
const sessionId = conversationId ?? randomUUID();
for await (const message of query({
prompt: userText,
options: {
sessionId,
hooks: guardHooks(arcjet, {
sessionId,
inbound: {
action: "message.received",
rules: ({ prompt }) => [detectPromptInjection()(prompt)],
},
}),
},
})) {
void message;
}
Block prompt injection on inbound user text for a Python Claude Agent SDK agent, failing open during outages so the agent keeps answering.
python
copy
from claude_agent_sdk import ClaudeAgentOptions, query
from arcjet.guard import DetectPromptInjection, launch_arcjet
from arcjet.guard.claude_agent_sdk import guard_hooks
arcjet = launch_arcjet(key=ARCJET_KEY)
inbound = DetectPromptInjection()
options = ClaudeAgentOptions(
session_id=conversation_id,
hooks=guard_hooks(
guard=arcjet,
session_id=conversation_id,
on_guard_error="allow",
inbound={
"action": "message.received",
"rules": lambda arguments: [inbound(arguments["prompt"])],
},
),
)
async for message in query(prompt=user_text, options=options):
pass
Automate wiring Arcjet Guard into a Claude Agent SDK project using the bundled Claude Code skill, avoiding manual integration steps.
copy
$ mkdir -p ~/.claude/skills
cp -r node_modules/@arcjet/guard/skills/integrate-arcjet-guard-claude-agent-sdk ~/.claude/skills/
# Then in Claude Code:
/integrate-arcjet-guard-claude-agent-sdk
Adds claudeAgentContext() from @arcjet/guard/claude-agent-sdk/v0 to read a caller-owned UUID session_id from hook input or options.sessionId for request correlation. Adds guard_tool from arcjet.guard.claude_agent_sdk (Python) to gate authored @tool handlers, returning JSON of ArcjetDenialResult in content with is_error: True on DENY. Supports exclude option on guardHooks (JS) and guard_hooks (Python) to skip tools already wrapped with guardTool/guard_tool; accepts { server, name } for authored MCP tools or a bare string for built-ins such as 'Bash'.
3 details in Everything
Arcjet adds Agent guards — deterministic policy checks before tool-handler actions in JavaScript, Python, and Go.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
Introduces Agent guards, a new enforcement point that places a deterministic Arcjet policy check immediately before agentic actions (tool handlers, queue consumers, workflow steps, MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. tools) — with no HTTP request required.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Supports writing Agent guard policies in Rego over caller-supplied inputs, with access to Arcjet's own detector findings as facts for conditions. Enables sensitive-information detection inline, over strings that never leave your process, as part of an Agent guard decision. Covers JavaScript, Python, and Go SDKs for the direct enforcement call, with framework integrations available for JavaScript and Python. Supports securing Claude Code and GitHub Copilot tool calls with Agent guard policies and no SDK required.
5 details in Everything
Arcjet adds coding agent policies: Rego Rego A declarative policy language created by the Open Policy Agent project, used to express authorization and compliance rules as code so cyber tools can evaluate structured data against those rules consistently. -based rules that intercept tool calls and prompts from Claude Code and GitHub Copilot︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Block any shell command that pipes a curl or wget download directly into a shell interpreter — a classic supply-chain attack vector from a coding agent.
rego
copy
package arcjet.guard
import rego.v1
deny contains "piped-installer" if {
input.values.tool_kind == "shell"
contains(input.values.command, "|")
some fetcher in input.values.command_tokens
lower(fetcher) in {"curl", "wget"}
some shell in input.values.command_tokens
lower(shell) in {"sh", "bash", "zsh", "dash"}
}
Restrict outbound web fetches to an approved domain list so a coding agent cannot exfiltrate data or pull from arbitrary hosts.
rego
copy
package arcjet.guard
import rego.v1
deny contains "unlisted-domain" if {
input.values.tool_kind == "web"
some host in input.values.domains
not host in {"docs.arcjet.com", "github.com", "raw.githubusercontent.com"}
}
Block a developer prompt that contains a credit card number or SSN before it reaches the model, using Arcjet's built-in sensitive information detector.
rego
copy
package arcjet.guard
import rego.v1
prompt_events := {
"user-prompt-submit",
"user-prompt-expansion",
"user-prompt-transformed"
}
deny contains "prompt-has-sensitive-info" if {
input.values.event in prompt_events
input.signals.sensitive_info.pii.detected
}
Adds input.values.<name> contract for coding-agent policy rules, exposing fields like tool_kind, tool_name, command, command_tokens, paths, domains, mcp_server, mcp_tool, and prompt in Rego policies under the coding-agent/v1 contract version.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds tool_kind normalization across Claude Code and GitHub Copilot, mapping vendor-specific tool names to canonical values: shell, file_read, file_write, web, mcp, agent, or other. Adds input.signals.prompt_injection.injection.detected signal for use in Rego policies to block turns where prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. is detected in prompt events (user-prompt-submit, user-prompt-expansion, user-prompt-transformed). Adds input.signals.sensitive_info.pii.detected signal for use in Rego policies to block prompts containing sensitive entity types such as CREDIT_CARD_NUMBER and SSN. Adds X-Arcjet-Principal header support for asserting developer identity in coding agent hooks, available as input.values.principal in Rego.
+2 more
show less
Adds coding agent policy authoring in the Arcjet Console under Policies, with a visual rule builder, stored test inputs, and per-rule dry-run mode before enforcement goes live. Supports attaching multiple policies to the same agent event; Arcjet runs all in one round trip and applies the most restrictive decision.
7 details in Everything
Arcjet now secures GitHub Copilot sessions via hooks in .github/hooks/arcjet.json, intercepting prompts and tool calls without workflow changes.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Enforce Arcjet policy on every Copilot CLI tool call and prompt in a repository — drop this file in and export your key to get immediate visibility and enforcement.
.github/hooks/arcjet.json
copy
{
"version": "hooks",
"hooks": {
"preToolUse": [{
"type": "http",
"url": "https://decide.arcjet.com/v1/agent-hooks/copilot?event=pre-tool-use&surface=cli",
"headers": {
"Authorization": "Bearer $ARCJET_KEY",
"X-Arcjet-Principal": "$USER"
},
"allowedEnvVars": ["ARCJET_KEY", "USER"],
"timeoutSec": 5
}]
}
}
Adds .github/hooks/arcjet.json hook configuration to intercept GitHub Copilot preToolUse, postToolUse, permissionRequest, userPromptSubmitted, userPromptTransformed, agentStop, sessionStart, and sessionEnd events via HTTP calls to https://decide.arcjet.com/v1/agent-hooks/copilot.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Supports administrator-enforced policy lock by placing the hook config at /etc/github-copilot/policy.d/arcjet.json (root-owned, not group- or world-writable), preventing users from disabling hooks in Copilot CLI. Authenticates hooks via the ARCJET_KEY environment variable passed as Authorization: Bearer $ARCJET_KEY, and attributes sessions to developers via the X-Arcjet-Principal header using $USER (or $USERNAME on Windows). Supports cloud coding agent enforcement by running .github/hooks/*.json from the repository's default branch once the file is merged, with a required firewall allow rule for decide.arcjet.com. Enables session and tool-call audit trail viewable in the Arcjet Console under the Activity section.
5 details in Everything
Arcjet now integrates with Claude Code via managed-settings hooks to observe, enforce policy, and record every prompt and tool call.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Deploy Arcjet enforcement hooks to every developer's machine on macOS via the endpoint-managed settings file so policy applies regardless of individual developer configuration.
/Library/Application Support/ClaudeCode/managed-settings.json
copy
{
"hooks": {
"PreToolUse": [{
"hooks": [{
"type": "http",
"url": "https://decide.arcjet.com/v1/agent-hooks/claude-code?event=pre-tool-use&surface=cli",
"headers": {
"Authorization": "Bearer $ARCJET_KEY",
"X-Arcjet-Principal": "$USER"
},
"allowedEnvVars": ["ARCJET_KEY", "USER"],
"timeout": 5
}]
}]
},
"env": { "ARCJET_KEY": "ajkey_REPLACE_ME" },
"allowedHttpHookUrls": ["https://decide.arcjet.com/*"],
"allowManagedHooksOnly": true,
"permissions": { "disableBypassPermissionsMode": "disable" }
}
Verify that Arcjet hooks are correctly installed and firing in a Claude Code session before rolling out to the wider team.
copy
$ claude doctor
# Then inside a Claude Code session:
/status
/hooks
Record each hook firing and its result to a local file when troubleshooting why a policy decision is not being applied as expected.
copy
$ claude --debug-file /tmp/arcjet-hooks-debug.log
Adds allowManagedHooksOnly, allowedHttpHookUrls, and disableBypassPermissionsMode lockdown keys to managed-settings.json to prevent developers from injecting their own hooks or bypassing the agent permission flow. Delivers hooks via endpoint-managed settings at /etc/claude-code/managed-settings.json (Linux/WSL WSL Windows Subsystem for Linux, a Microsoft-built compatibility layer that lets a Linux environment run directly on Windows without a virtual machine, giving cyber tools access to Linux binaries, filesystems, and syscalls on a Windows host. ), /Library/Application Support/ClaudeCode/managed-settings.json (macOS), or C:\Program Files\ClaudeCode\managed-settings.json (Windows), as well as server-managed settings via the claude.ai Admin console and repository-level .claude/settings.json. Supports supplying the Arcjet key via an env block in managed-settings.json (exposing $ARCJET_KEY to all agent subprocesses) or as a literal Authorization: Bearer ajkey_… header value that never enters the agent environment. Supports cloud sessions (claude --cloud) by reading .claude/settings.json and server-managed settings, requiring decide.arcjet.com be added to Allowed domains under a Custom network access level. Supports third-party model providers (CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX, CLAUDE_CODE_USE_FOUNDRY, ANTHROPIC_BASE_URL) with the guidance to use endpoint-managed settings when these variables are set, since they cause Claude Code to skip the server-managed settings fetch.
+2 more
show less
Enables hook and policy verification via /status (lists setting sources), /hooks (lists configured hooks with source and URL), claude doctor (server-managed fetch outcome), and claude --debug-file <path> (records each hook firing and result). Supports telemetry-only mode via the Claude Compliance API or Claude Code's native OpenTelemetry OpenTelemetry An open standard and set of tools for collecting traces, metrics and logs from applications. It lets services export observability data without tying instrumentation to one monitoring vendor. export to Arcjet through managed settings, as an alternative when hook deployment is not possible.
7 details in Everything
Arcjet adds agentless policy enforcement for coding agents (Claude Code, GitHub Copilot) with prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. screening and full session audit.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
Adds policy enforcement for coding agent tool calls via HTTP hooks, evaluating inputs including tool_name, tool_kind, command, command_tokens, paths, domains, mcp_server, and mcp_tool at the pre-tool-use and permission-request execution points — no SDK or code changes required.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Screens developer prompts for prompt injection and sensitive information at the user-prompt-submit and user-prompt-expansion execution points. Supports reconciling sessions against OpenTelemetry OpenTelemetry An open standard and set of tools for collecting traces, metrics and logs from applications. It lets services export observability data without tying instrumentation to one monitoring vendor. or the Compliance API to identify sessions that ran with no hook decisions. Executes policies at the edge across 300+ data center locations with real-time policy updates, and a single policy applies across all supported coding agents without per-agent configuration.
4 details in Everything
Arcjet adds agent guard integrations, guard() /capture() primitives, content moderation, and framework-specific tool-call wrappers across 13 AI agent SDKs.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
Adds guard() method for protecting tool calls, queue consumers, and agentic pipelines without an HTTP request, returning an enforcement decision inline.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds capture() method to record that an allowed agent action happened for audit-trail visibility without affecting decisions. Adds clientIpDetails() / findIpDetails() (JavaScript), client_ip_details() (Python), and ClientIPDetails() (Go) to inspect client IP provenance provenance Evidence recording where a software artifact came from and how it was built, including its source and build process. It helps a consumer check that the artifact matches a trusted build. before shipping to production. Adds agent guard integrations for 13 AI frameworks — Vercel AI SDK (guardTool, aiToolsContext, guardAction), LangChain JavaScript/Python (guardTool, guardMiddleware, guard_action, ArcjetMiddleware), CrewAI Python (register_arcjet_hooks, guard_tool), LangGraph JavaScript (guardTool, guardToolNode), Genkit JavaScript (guardTool, guardMiddleware), Google ADK ADK Google's Agent Development Kit, an open-source framework for building and orchestrating AI agents; cyber tools use it to compose multi-agent workflows with built-in tool-calling and state management. JavaScript (guardPlugin), Strands Agents JavaScript/Python (guardTool, guardHooks, guard_tool, guard_hooks), TanStack AI JavaScript (guardMiddleware), Vercel Eve JavaScript (guardInbound, guardTool, guardApproval), and Mastra JavaScript (guardProcessor, guardTool, guardHooks) — each with inbound guard() before the framework's primary execution method. Adds content moderation (moderateContent() in JavaScript, ModerateContent() in Python, GuardModerateContent in Go) as a Guard-only building block for detecting harmful content in untrusted text.
+8 more
show less
Adds an AI protection feature set with four playbooks: abuse protection, budget control, data loss prevention, and prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. — each with examples for every HTTP SDK and agent adapter. Adds an Arcjet CLI for creating sites, retrieving keys, inspecting requests, and managing rules from the terminal. Adds an MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. server management surface at https://api.arcjet.com/mcp (OAuth) for the same rule management operations available in the CLI. Adds an Arcjet plugin bundling skills, MCP, and coding rules for Claude Code and Cursor. Adds SDK-scoped stable quick-start URLs (e.g. https://docs.arcjet.com/sdk/next/get-started/) replacing ?f= query parameters for all supported frameworks including Next.js, Astro, Bun, Deno, Fastify, NestJS, Node.js, Nuxt, Python + FastAPI, Python + Flask, React Router, Remix, and SvelteKit. Adds agent guard quick-start routes using the URL shape guards/quick-start, rate-limiting/quick-start, sensitive-info/quick-start, content-moderation/quick-start, prompt-injection/quick-start, and ai-protection/data-loss-prevention for all agent adapters. Adds ipSrc (JavaScript), ip_src (Python), and WithIPSrc (Go) manual IP source override fields, with validation that rejects invalid values and warns on 0.0.0.0/0 or ::/0. Adds client_ip_provenance debug-level logging and an unverified-header label when Arcjet falls back to forwarding headers for client IP detection.
13 details in Everything
Arcjet: Adds agent runtime security for LLM LLM A large language model, a neural network trained on large amounts of text to recognize patterns and generate language. Applications use it for tasks such as answering, summarizing and writing code. tool calls via the arcjet-go SDK (go get github.com/arcjet/arcjet-go@v1.0.0-rc.2), enforcing budgets, blocking prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. , and protecting personal information.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ TRY IT
Install the Arcjet Go SDK to add agent guard calls to a Go-based LLM agent.
copy
$ go get github.com/arcjet/arcjet-go@v1.0.0-rc.2
Exposes list-guard-policies over MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. to enumerate a site's guard policies and their labels. Exposes get-guard-policy over MCP to retrieve a policy's full contract — its label, every declared input, its kind and exposure — so the agent can write calls that match exactly. Exposes describe-guard-policy over MCP to author a new guard policy from a natural-language description or a pre-built draft with an execute-on setting and contract. Exposes validate-guard-policy over MCP to compile a draft policy and run its stored tests before publishing. Exposes put-guard-policy over MCP to publish a validated guard policy, making it live without a deployment.
+2 more
show less
Supports actor inputs on every framework adapter — in both JavaScript and Python — as a value or a per-call resolver function, enabling per-actor policy enforcement. Guard policies are authored in Rego over declared inputs and can read Arcjet's own detector findings as facts; a live rule requires at least one stored test before publishing.
7 details in Everything
Arcjet Guard adds Vercel Eve agent integration with guardTool() , guardApproval() , guardInbound() , and arcjetHooks() helpers︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ TRY IT
Block prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. at the channel boundary before an agent session starts, using a stable conversation ID so the inbound decision correlates with the session.
copy
$ import { defineChannel, POST } from 'eve/channels';
import { detectPromptInjection } from '@arcjet/guard';
import { guardInbound } from '@arcjet/guard/vercel-eve/v0';
import { arcjet } from '../arcjet.js';
export default defineChannel({
routes: [
POST('/webhook', async (req, args) => {
const { message, conversationId } = await req.json();
const verdict = await guardInbound(arcjet, message, {
rules: [detectPromptInjection()(message)],
action: 'message.received',
correlationId: conversationId,
});
if (!verdict.allowed) {
return new Response(JSON.stringify({ error: verdict.message }), {
status: verdict.outcome === 'UNAVAILABLE' ? 503 : 403,
});
}
const session = await args.from(conversationId).send(message, { auth: null });
return new Response(JSON.stringify({ sessionId: session.id }));
}),
],
});
Rate-limit an authored tool per order ID using a token bucket, throwing on deny so Eve surfaces it as a failed action result.
copy
$ import { defineTool } from 'eve/tools';
import { z } from 'zod';
import { tokenBucket } from '@arcjet/guard';
import { guardTool } from '@arcjet/guard/vercel-eve/v0';
import { arcjet } from '../arcjet.js';
const lookupLimit = tokenBucket({ bucket: 'lookups', refillRate: 10, intervalSeconds: 60, maxTokens: 10 });
export default guardTool(
arcjet,
defineTool({
description: 'Look up an order by ID',
inputSchema: z.object({ orderId: z.string() }),
async execute(input) {
return { orderId: input.orderId, status: 'shipped' };
},
}),
{
action: 'order.looked-up',
rules: (input) => [lookupLimit({ key: input.orderId, requested: 1 })],
},
);
Gate an OpenAPI OpenAPI A vendor-neutral specification for describing REST APIs in a machine-readable format, maintained by the OpenAPI Initiative. Cyber tools use it to auto-generate clients, documentation, and validation from a single source of truth. connection with a per-session rate limit and require human approval before the call proceeds, rate-limiting the approver separately.
copy
$ import { defineOpenAPIConnection } from 'eve/connections';
import { tokenBucket } from '@arcjet/guard';
import { guardApproval } from '@arcjet/guard/vercel-eve/v0';
import { arcjet } from '../arcjet.js';
const sessionLimit = tokenBucket({ bucket: 'weather-session', refillRate: 5, intervalSeconds: 60, maxTokens: 5 });
const approverLimit = tokenBucket({ bucket: 'weather-approver', refillRate: 5, intervalSeconds: 60, maxTokens: 5 });
export default defineOpenAPIConnection({
description: 'Weather API',
spec: 'https://api.example.com/openapi.json',
approval: guardApproval(arcjet, {
action: 'weather.fetched',
rules: (ctx) => [sessionLimit({ key: ctx.session.id, requested: 1 })],
onAllow: 'user-approval',
response: {
action: 'weather.approved',
rules: (ctx) => [approverLimit({ key: ctx.responder.principalId, requested: 1 })],
},
}),
operations: { allow: ['GetForecast'] },
});
Adds guardTool() from @arcjet/guard/vercel-eve/v0 to wrap an authored Eve tool's execute, throwing ArcjetDeniedError on DENY or returning ArcjetDenialResult when onDeny: 'result' is set.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds guardApproval() from @arcjet/guard/vercel-eve/v0 to gate OpenAPI connections, MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. connections, and defineDynamic tools before execution, with optional onAllow: 'user-approval' for human-in-the-loop human-in-the-loop Human in the loop, involving a person in an otherwise automated workflow. The person can review, approve, correct or supply information before work continues. gating and a response field (GuardApprovalResponsePolicy) to authorize the responder. Adds guardInbound() from @arcjet/guard/vercel-eve/v0 to screen inbound text on an Eve channel (agent/channels/*.ts) before the agent session starts, requiring a stable correlationId (e.g. conversation ID) so the decision joins the session later. Adds arcjetHooks() from @arcjet/guard/vercel-eve/v0 to observe Eve agent lifecycle events (eve.session-started and others) for audit trails, correlated by session ID. Adds launchArcjet() from @arcjet/guard as the single client entrypoint, configured with ARCJET_KEY, to be launched once at module scope and reused across all helpers.
+3 more
show less
Adds detectPromptInjection() rule (from @arcjet/guard) for use with guardInbound() to block prompt injection at the channel boundary before a turn begins. Adds a Claude Code skill at node_modules/@arcjet/guard/skills/integrate-arcjet-guard-eve (invoked as /integrate-arcjet-guard-eve) that automates adding Arcjet Guard to an Eve project. Supports tokenBucket() SDK rule from @arcjet/guard as an inline rules option on all helpers, enabling per-key rate limiting on tool calls and connections.
8 details in Everything
Arcjet adds a Vercel AI SDK v7 agent guard integration to intercept and policy-gate AI tool calls before execution︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Rate-limit and policy-gate a model-invoked email tool per user so the email provider never runs on a DENY.
typescript
copy
import { launchArcjet, tokenBucket, policyInput } from '@arcjet/guard';
import { guardTool, aiToolsContext, createAgentContext } from '@arcjet/guard/vercel-ai/v7';
import { generateText, stepCountIs, tool } from 'ai';
import { z } from 'zod';
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const emailLimit = tokenBucket({ bucket: 'email', refillRate: 5, intervalSeconds: 3600, maxTokens: 10 });
const sendEmail = guardTool(
arcjet,
tool({
description: 'Send an email',
inputSchema: z.object({ recipient: z.string().email(), body: z.string() }),
execute: ({ recipient, body }) => emailProvider.send({ to: recipient, body }),
}),
{
action: 'email.sent',
actor: user.id,
rules: () => [emailLimit({ key: user.id, requested: 1 })],
inputs: ({ recipient, body }) => ({
recipient: policyInput.server.string(recipient),
body: policyInput.local.string(body),
}),
},
);
const context = createAgentContext({ correlationId: user.id });
return generateText({
model: 'openai/gpt-4o-mini',
prompt,
tools: { sendEmail },
toolsContext: aiToolsContext(context, { sendEmail }),
stopWhen: stepCountIs(5),
});
Gate an app-invoked GitHub review submission with a token-bucket rate limit before the API call fires.
typescript
copy
import { guardAction, ArcjetDeniedError, createAgentContext, securityMetadata } from '@arcjet/guard/vercel-ai/v7';
import { tokenBucket } from '@arcjet/guard';
import { arcjet } from './arcjet.js';
const submitLimit = tokenBucket({ bucket: 'reviews', refillRate: 2, intervalSeconds: 3600, maxTokens: 5 });
const context = createAgentContext({ correlationId: conversationId });
try {
await guardAction(
arcjet,
context,
{
action: 'review.submitted',
rules: [submitLimit({ key: review.repoId, requested: 1 })],
metadata: securityMetadata({ destination: 'github', reversibility: 'compensable' }),
},
() => github.pulls.createReview({ owner, repo, pull_number: prNumber, body: reviewText }),
);
} catch (error) {
if (error instanceof ArcjetDeniedError) {
console.warn('denied:', error.decision.reason);
}
}
Install the Claude Code skill to automate wiring Arcjet Guard into a Vercel AI SDK project.
copy
$ npm install @arcjet/guard ai @ai-sdk/provider-utils
mkdir -p ~/.claude/skills
cp -r node_modules/@arcjet/guard/skills/integrate-arcjet-guard-agents ~/.claude/skills/
# Then in Claude Code:
# /integrate-arcjet-guard-agents
Adds @arcjet/guard/vercel-ai/v7 export with guardTool() , aiToolsContext() , createAgentContext() , guardAction() , captureAction() , and securityMetadata() helpers for securing Vercel AI SDK agent tool calls.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds guardTool() to wrap AI SDK tool({ execute }) handlers so Arcjet policy runs before execute; on DENY the model receives a structured ArcjetDenialResult ({ arcjetDenied, reason, message, retryable, retryAfterSeconds? }) instead of the tool running. Adds guardAction() to gate app-invoked (non-model) functions, throwing ArcjetDeniedError on policy denial and ArcjetGuardUnavailableError when the policy cannot be evaluated. Adds captureAction() for recording that an action occurred without blocking, enabling audit-trail events for side effects like Slack notifications. Adds createAgentContext() to build a run-scoped context carrying a correlationId (1-256 printable ASCII characters) and metadata, threaded into generateText, streamText, and guardAction calls.
+7 more
show less
Adds aiToolsContext() to map an ArcjetAgentContext onto the toolsContext parameter of generateText / streamText, scoped only to tools wrapped by guardTool. Adds securityMetadata() to map fields (user, agent, workflow, dataClass, destination, reversibility, resource) onto Arcjet wire keys for richer policy context. Supports onGuardError option ('deny' default or 'allow') on guardTool to control fail-open vs. fail-closed behavior when the Guard service is unavailable. Supports onDeny option on guardTool to reshape the denial payload the model receives on a real DENY (does not run for outages). Adds policyInput.server.* and policyInput.local.* builders for the inputs option on guardTool, distinguishing server-trusted values from model-supplied arguments. Adds ArcjetDenialResult and ArcjetDeniedError / ArcjetGuardUnavailableError importable from @arcjet/guard/vercel-ai/v7 for typed error handling in agent workflows. Requires Node.js >=22.21.0 <23 || >=24.5.0; peers ai (>=7 <8) and @ai-sdk/provider-utils (>=5 <6) are optional but required for the integration.
12 details in Everything
Arcjet adds a TanStack AI adapter with guardMiddleware() and tanstackAiContext() for securing AI agent tool calls︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ TRY IT
Gate every tool call in a TanStack AI chat session, applying per-tool rate limiting and PII PII Personally Identifiable Information — data that can identify a specific individual, such as names, email addresses, or SSNs. Cyber tools flag or redact it to limit exposure and meet privacy regulations. detection before any tool executes.
copy
$ import { launchArcjet, tokenBucket, localDetectSensitiveInfo } from "@arcjet/guard";
import { guardMiddleware } from "@arcjet/guard/tanstack-ai/v0";
import { chat } from "@tanstack/ai";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 10, intervalSeconds: 60, maxTokens: 10 });
const detectPii = localDetectSensitiveInfo({ deny: ["EMAIL", "PHONE_NUMBER", "CREDIT_CARD_NUMBER"] });
const stream = chat({
adapter,
messages: [{ role: "user", content: userText }],
tools: [lookupOrder],
context: { sessionId: conversationId },
middleware: [
guardMiddleware(arcjet, {
action: ({ toolName }) => `${toolName}.invoked`,
sessionId: conversationId,
onGuardError: "deny",
onDeny: "abort",
rules: ({ toolName, input }) =>
toolName === "lookup_order"
? [lookupLimit({ key: input.orderNumber, requested: 1 }), detectPii(input.note)]
: [],
})
],
});
Screen user-supplied text for prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. before calling chat() , failing closed if the guard is unreachable.
copy
$ import { detectPromptInjection } from "@arcjet/guard";
import { tanstackAiContext } from "@arcjet/guard/tanstack-ai/v0";
import { arcjet } from "./arcjet.js";
const inbound = detectPromptInjection();
const appContext = { sessionId: conversationId };
const decision = await arcjet.guard({
label: "message.received",
rules: [inbound(userText)],
...tanstackAiContext({ context: appContext }),
});
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) {
throw new Error("message blocked");
}
Adds guardMiddleware() from @arcjet/guard/tanstack-ai/v0 as a chat({ middleware }) gate that intercepts every tool call via onBeforeToolCall, denying by returning { type: 'skip', result } with an ArcjetDenialResult before the tool's execute runs.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds tanstackAiContext() from @arcjet/guard/tanstack-ai/v0 to correlate guard calls to a caller-owned session, reading correlationId, sessionId, or conversationId from chat({ context }) — never minting or reading TanStack-generated IDs like threadId, requestId, streamId, or traceId. Adds onDeny: 'abort' option on guardMiddleware() to stop the entire chat run with { type: 'abort', reason } instead of the default skip behavior. Adds action option on guardMiddleware() accepting a static resource.verb string or a function of { toolName, input } to label events in the Arcjet Console and select a remote policy. Adds rules option on guardMiddleware() accepting SDK rules or a function of { toolName, input } to apply per-tool policies such as tokenBucket() , localDetectSensitiveInfo() , and detectPromptInjection() .
+2 more
show less
Exposes ArcjetDenialResult as a named type export from @arcjet/guard/tanstack-ai/v0, with fields arcjetDenied, reason (RATE_LIMIT, PROMPT_INJECTION, SENSITIVE_INFO, ERROR), message, retryable, and retryAfterSeconds. Requires @tanstack/ai >=0.8.0 <1 as an optional peer and Node.js >=22.21.0 <23 || >=24.5.0 for the TanStack AI adapter.
7 details in Everything
Arcjet: Adds @arcjet/guard/strands-agents/v1 (JavaScript/TypeScript) and arcjet[strands-agents] (Python) adapters that integrate Arcjet policy enforcement into Strands Agents tool handlers via guardTool() / guard_tool and guardHooks() / guard_hooks.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ TRY IT
Gate an authored Strands Agents tool against prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. and rate limits before it executes any side effects.
copy
$ import { launchArcjet, detectPromptInjection, tokenBucket } from "@arcjet/guard";
import { guardTool } from "@arcjet/guard/strands-agents/v1";
import { Agent, tool } from "@strands-agents/sdk";
import { z } from "zod";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const sendEmail = guardTool(arcjet, tool({
description: "Send an email",
parameters: z.object({ to: z.string(), body: z.string() }),
callback: async ({ to, body }) => { /* send */ }
}), {
action: "email.sent",
rules: ({ to }) => [detectPromptInjection(), tokenBucket({ capacity: 10, refillRate: 1, interval: "1m" })]
});
const agent = new Agent({ tools: [sendEmail] });
Gate all MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. and unwrapped tools in a Python Strands agent via the hooks provider without modifying each tool individually.
python
copy
from arcjet.guard import launch_arcjet
from arcjet.guard.strands_agents import guard_hooks
from strands import Agent
arcjet = launch_arcjet(key=ARCJET_KEY)
agent = Agent(
hooks=[guard_hooks(
guard=arcjet,
action="tool.invoked",
on_guard_error="deny"
)]
)
Adds guardTool() helper (JS) and guard_tool helper (Python) to wrap authored @tool / tool({ callback }) handlers so the original callback never runs on DENY. Adds guardHooks() plugin (JS) and guard_hooks hook provider (Python) that register on BeforeToolCallEvent to gate unwrapped and MCP tools agent-wide without modifying individual tool handlers. Adds strandsAgentContext() (JS) and strands_agent_context (Python) helpers that extract a caller-owned correlationId / correlation_id, sessionId / session_id, or requestId / request_id from invocationState for session correlation in the Arcjet Console.
3 details in Everything
Arcjet Guard adds OpenAI Agents adapters for JS/TS (@arcjet/guard/openai-agents/v0) and Python (arcjet.guard.openai_agents) to gate function-tool handlers.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ TRY IT
Screen user-supplied text for prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. before starting the agent run, blocking the run on DENY or a failed-open guard response.
copy
$ import { detectPromptInjection } from '@arcjet/guard';
import { openaiAgentsContext } from '@arcjet/guard/openai-agents/v0';
import { run } from '@openai/agents';
import { arcjet } from './arcjet.js';
const inbound = detectPromptInjection();
const appContext = { sessionId: conversationId };
const decision = await arcjet.guard({
label: 'message.received',
rules: [inbound(userText)],
...openaiAgentsContext({ context: appContext, conversationId })
});
if (decision.conclusion === 'DENY' || decision.hasFailedOpen()) {
throw new Error('message blocked');
}
await run(agent, userText, { context: appContext });
Install the Claude Code skill to let an AI coding agent automatically wire Arcjet Guard into an OpenAI Agents project.
copy
$ npm install @arcjet/guard @openai/agents
mkdir -p ~/.claude/skills
cp -r node_modules/@arcjet/guard/skills/integrate-arcjet-guard-openai-agents ~/.claude/skills/
# Then in Claude Code:
# /integrate-arcjet-guard-openai-agents
Adds @arcjet/guard/openai-agents/v0 JavaScript/TypeScript adapter that wraps FunctionTool.invoke via guardTool() to allow or deny authored tool calls before any side effect runs, returning ArcjetDenialResult ({ arcjetDenied, reason, message, retryable, retryAfterSeconds?︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ }) on DENY. Adds arcjet.guard.openai_agents Python adapter (installed via arcjet[openai-agents], requires openai-agents>=0.19.0,<1 and CPython 3.10+) with guard_tool() that attaches policy gating on FunctionTool.tool_input_guardrails and calls reject_content(...) on DENY. Adds openaiAgentsContext() (JS) and openai_agents_context() (Python) helpers to extract a caller-owned correlation ID from runContext.context — reading correlationId, then sessionId, then conversationId, then groupId — without minting a new ID. Adds guard_tool Python helper options including actor, inputs, rules, metadata, correlation_id, and on_guard_error ('deny' default or 'allow') for fine-grained per-tool policy control. Adds guardTool() JS helper options action, rules, metadata, onGuardError ('deny' default or 'allow'), and onDeny to reshape the denial object stringified onto the tool result.
+1 more
show less
Supports inbound prompt-injection screening before Runner.run() via a direct guard() call with detectPromptInjection() / DetectPromptInjection() , using openaiAgentsContext() / openai_agents_context() for correlation, independent of the tool gate.
6 details in Everything
Arcjet: Adds guardProcessor() from @arcjet/guard/mastra/v1 for use in Mastra inputProcessors / outputProcessors, calling abort() (tripwire) on DENY and screening later agentic steps via processInputStep so tool continuations cannot skip the inbound gate.
└──▷ USE IT
Gate MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. or toolset tools you did not author with per-tool rate limiting via guardHooks, then correlate agent turns to a conversation and user ID.
javascript
copy
import { guardHooks, mastraAgentContext } from '@arcjet/guard/mastra/v1';
import { tokenBucket } from '@arcjet/guard';
import { RequestContext, MASTRA_THREAD_ID_KEY, MASTRA_RESOURCE_ID_KEY } from '@mastra/core/request-context';
import { arcjet } from './arcjet.js';
const mcpLimit = tokenBucket({ bucket: 'mcp-access', refillRate: 5, intervalSeconds: 10, maxTokens: 10 });
export const hooks = guardHooks(arcjet, {
action: ({ toolName }) => `${toolName}.invoked`,
rules: ({ toolName }) => [mcpLimit({ key: toolName, requested: 1 })],
});
// Correlate the turn to a conversation and user before calling generate()
const requestContext = new RequestContext();
requestContext.set(MASTRA_THREAD_ID_KEY, conversationId);
requestContext.set(MASTRA_RESOURCE_ID_KEY, userId);
await agent.generate(message, { requestContext });
Adds guardHooks() from @arcjet/guard/mastra/v1 providing beforeToolCall / afterToolCall for MCP, workspace, or toolset tools not wrapped by guardTool; beforeToolCall returns { proceed: false, output } on DENY, while afterToolCall is observe-only. Adds mastraAgentContext() to read correlation IDs from MASTRA_THREAD_ID_KEY, then resource, then workflow.runId on RequestContext without minting a new ID. All helpers accept an action string (or a function of { toolName } for hooks), rules, metadata, and onGuardError ('deny' default or 'allow') options. Helpers default to onGuardError: 'deny' (fail-closed): if the guard cannot be evaluated, the tool does not run and processInput aborts the turn. Supports combining SDK rules in code (e.g. tokenBucket, localDetectSensitiveInfo, detectPromptInjection) with remote policies selected by the action slug in the Arcjet Console.
5 details in Everything
Arcjet: Adds guardToolNode() from @arcjet/guard/langgraph/v1 to guard a ToolNode in place — including MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. and runtime-discovered tools — defaulting to onGuardError: 'deny' (fail-closed) and skipping already-branded guardTool tools to avoid double-calling.
└──▷ USE IT
Block prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. in user input before handing off to a LangGraph agent — fail closed if the guard itself fails open.
javascript
copy
import { detectPromptInjection } from "@arcjet/guard";
import { langgraphAgentContext } from "@arcjet/guard/langgraph/v1";
import { arcjet } from "./arcjet.js";
const config = { configurable: { thread_id: conversationId } };
const inbound = detectPromptInjection();
const decision = await arcjet.guard({
label: "message.received",
rules: [inbound(userText)],
...langgraphAgentContext(config)
});
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) {
throw new Error("message blocked");
}
await graph.invoke({ messages: [{ role: "user", content: userText }] }, config);
Rate-limit and PII PII Personally Identifiable Information — data that can identify a specific individual, such as names, email addresses, or SSNs. Cyber tools flag or redact it to limit exposure and meet privacy regulations. -screen an authored LangGraph tool so the underlying function never runs when a policy denies the call.
javascript
copy
import { tool } from "@langchain/core/tools";
import { z } from "zod";
import { guardTool } from "@arcjet/guard/langgraph/v1";
import { tokenBucket, localDetectSensitiveInfo } from "@arcjet/guard";
import { arcjet } from "./arcjet.js";
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 10, intervalSeconds: 60, maxTokens: 10 });
const detectPii = localDetectSensitiveInfo({ deny: ["EMAIL", "PHONE_NUMBER", "IP_ADDRESS", "CREDIT_CARD_NUMBER"] });
export const lookupOrder = guardTool(
arcjet,
tool(
async ({ orderNumber, note }) => ({ orderNumber, note, status: "shipped" }),
{
name: "lookup_order",
description: "Look up an order by number",
schema: z.object({ orderNumber: z.string(), note: z.string() })
}
),
{
action: "order.looked-up",
rules: (input) => [lookupLimit({ key: input.orderNumber, requested: 1 }), detectPii(input.note)]
}
);
Guard all tools in a ToolNode — including MCP tools — with a shared rate limit, so even runtime-discovered tools are policy-gated before they execute.
javascript
copy
import { ToolNode } from "@langchain/langgraph/prebuilt";
import { guardToolNode } from "@arcjet/guard/langgraph/v1";
import { tokenBucket } from "@arcjet/guard";
import { arcjet } from "./arcjet.js";
const mcpLimit = tokenBucket({ bucket: "mcp-access", refillRate: 5, intervalSeconds: 60, maxTokens: 20 });
export const tools = guardToolNode(
arcjet,
new ToolNode([lookupOrder, ...mcpTools]),
{
action: ({ toolName }) => `${toolName}.invoked`,
rules: ({ toolName }) => [mcpLimit({ key: toolName, requested: 1 })]
}
);
// Then: graph.addNode("tools", tools)
Adds langgraphAgentContext() from @arcjet/guard/langgraph/v1 to correlate guard calls using configurable.thread_id, then the run ID, then configurable.checkpoint_ns — without minting an ID or calling createAgentContext. Supports inbound prompt-injection screening via a direct client.guard({ label, rules, ...langgraphAgentContext(config) }) call before graph.invoke, with decision.hasFailedOpen() available to fail closed when needed. Requires Node.js >=22.21.0 <23 || >=24.5.0 and peers @langchain/langgraph (>=1 <2) and @langchain/core (>=1 <2), installed via npm install @arcjet/guard @langchain/langgraph @langchain/core.
3 details in Everything
Arcjet: Adds @arcjet/guard/langchain/v1 JavaScript/TypeScript adapter with guardTool() , guardMiddleware() , and langchainContext() helpers to enforce Arcjet policies at LangChain tool call boundaries before side effects run.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Screen user-supplied text for prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. before the LangChain agent runs, failing closed if the Guard call itself fails open.
javascript
copy
import { detectPromptInjection } from '@arcjet/guard';
import { langchainContext } from '@arcjet/guard/langchain/v1';
import { arcjet } from './arcjet.js';
const inbound = detectPromptInjection();
const config = { configurable: { thread_id: conversationId } };
const decision = await arcjet.guard({
label: 'message.received',
rules: [inbound(userText)],
...langchainContext(config)
});
if (decision.conclusion === 'DENY' || decision.hasFailedOpen()) {
throw new Error('message blocked');
}
await agent.invoke({ input: userText }, config);
Wrap a LangChain tool with guardTool() so Arcjet evaluates a remote rate-limit policy before the tool's side effect runs, without changing the tool's call signature.
javascript
copy
import { launchArcjet } from '@arcjet/guard';
import { guardTool } from '@arcjet/guard/langchain/v1';
import { tool } from '@langchain/core/tools';
import { z } from 'zod';
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const sendEmail = tool(
async ({ to, body }) => { /* send email */ },
{ name: 'send_email', schema: z.object({ to: z.string(), body: z.string() }) }
);
const guardedSendEmail = guardTool(sendEmail, {
arcjet,
action: 'email.sent',
onGuardError: 'deny'
});
Install the Python LangChain agent guard extras and wrap tools with guard_tool to enforce Arcjet policies before each tool invocation.
copy
$ pip install 'arcjet[langchain-agents]'
Adds arcjet.guard.langchain Python adapter via arcjet[langchain] and arcjet[langchain-agents] extras, exposing guard_tool, ArcjetMiddleware, ToolPolicy, ArcjetCaptureHandler, and ArcjetAsyncCaptureHandler. Introduces guardMiddleware() (JavaScript) and ArcjetMiddleware + ToolPolicy (Python) as an invoke-wide gate passed to createAgent({ middleware }) /create_agent, with a wrapToolCall hook that denies by returning a real ToolMessage instead of calling the handler. Introduces langchainContext() helper to read configurable.thread_id, caller-owned sessionId/conversationId, or init.sessionId/init.correlationId for correlating Guard decisions across interrupted and resumed agent runs. Supports guard_tool, guard_action, and ToolPolicy Python options including actor, inputs, metadata, and on_guard_error, with distinct ArcjetDeniedError/ArcjetUnavailableError and ArcjetToolDeniedError/ArcjetToolUnavailableError exception families.
4 details in Everything
Arcjet adds a Google ADK ADK Google's Agent Development Kit, an open-source framework for building and orchestrating AI agents; cyber tools use it to compose multi-agent workflows with built-in tool-calling and state management. (TypeScript) integration with guardPlugin and googleAdkContext from @arcjet/guard/google-adk/v2︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ TRY IT
Gate every Google ADK tool call with Arcjet rate-limiting and PII PII Personally Identifiable Information — data that can identify a specific individual, such as names, email addresses, or SSNs. Cyber tools flag or redact it to limit exposure and meet privacy regulations. detection before FunctionTool.execute runs, failing closed on Guard errors.
copy
$ import { launchArcjet, tokenBucket, localDetectSensitiveInfo } from "@arcjet/guard";
import { guardPlugin } from "@arcjet/guard/google-adk/v2";
import { InMemoryRunner } from "@google/adk";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const runner = new InMemoryRunner({
agent,
appName: "orders",
plugins: [
guardPlugin(arcjet, {
action: "order.looked-up",
sessionId: conversationId,
onGuardError: "deny",
rules: ({ toolName, input }) => [
tokenBucket({ bucket: "lookups", refillRate: 10, intervalSeconds: 60, maxTokens: 100 })({ key: input.orderNumber, requested: 1 }),
localDetectSensitiveInfo({ deny: ["EMAIL", "PHONE_NUMBER", "CREDIT_CARD_NUMBER"] })(input.note),
],
})
],
});
await runner.runAsync({ userId: conversationId, sessionId: conversationId, newMessage: { parts: [{ text: userText }] } });
Screen inbound user text for prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. before starting a Google ADK runner, failing closed when the Guard call itself fails open.
copy
$ import { launchArcjet, detectPromptInjection } from "@arcjet/guard";
import { googleAdkContext } from "@arcjet/guard/google-adk/v2";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const inbound = detectPromptInjection();
const appContext = { sessionId: conversationId };
const decision = await arcjet.guard({
label: "message.received",
rules: [inbound(userText)],
...googleAdkContext(appContext),
});
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) {
throw new Error("message blocked");
}
await runner.runAsync({ userId: conversationId, sessionId: conversationId, newMessage: { parts: [{ text: userText }] } });
Adds guardPlugin() from @arcjet/guard/google-adk/v2 as a Runner-wide beforeToolCallback gate for Google ADK JS (@google/adk v2), denying tool calls by returning an ArcjetDenialResult dict before FunctionTool.execute runs — supports action, rules, metadata, sessionId, and onGuardError options.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds googleAdkContext() from @arcjet/guard/google-adk/v2 to correlate direct guard() inbound calls with a caller-owned ID read from correlationId, then sessionId, then conversationId on the object passed to it. Requires Node.js >=22.21.0 <23 || >=24.5.0 and @google/adk >=2 <3 (optional peer) for the new integration; import path is the versioned @arcjet/guard/google-adk/v2 with no unversioned alias.
3 details in Everything
Arcjet: Adds guardTool() from @arcjet/guard/genkit/v1 to wrap a Genkit ToolAction returned by ai.defineTool() , enforcing remote policy before any side effect runs and returning an ArcjetDenialResult as toolResponse.output on DENY instead of throwing.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Wrap a defineTool action with per-call rate limiting and PII PII Personally Identifiable Information — data that can identify a specific individual, such as names, email addresses, or SSNs. Cyber tools flag or redact it to limit exposure and meet privacy regulations. detection so the tool never executes on a policy denial.
javascript
copy
import { guardTool } from '@arcjet/guard/genkit/v1';
import { tokenBucket, localDetectSensitiveInfo } from '@arcjet/guard';
import { arcjet } from './arcjet.js';
const lookupOrder = guardTool(
arcjet,
ai.defineTool(
{ name: 'lookup_order', description: 'Look up an order by number',
inputSchema: z.object({ orderNumber: z.string(), note: z.string() }) },
async ({ orderNumber, note }) => ({ orderNumber, note, status: 'shipped' })
),
{
action: 'order.looked-up',
onGuardError: 'deny',
rules: (input) => [
tokenBucket({ bucket: 'lookups', refillRate: 10, intervalSeconds: 60, maxTokens: 100 })({ key: input.orderNumber, requested: 1 }),
localDetectSensitiveInfo({ deny: ['EMAIL', 'PHONE_NUMBER', 'CREDIT_CARD_NUMBER'] })(input.note)
]
}
);
Screen user-supplied text for prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. before calling ai.generate() , failing closed if Guard is unavailable.
javascript
copy
import { detectPromptInjection } from '@arcjet/guard';
import { genkitContext } from '@arcjet/guard/genkit/v1';
import { arcjet } from './arcjet.js';
const inbound = detectPromptInjection();
const appContext = { sessionId: conversationId };
const decision = await arcjet.guard({
label: 'message.received',
rules: [inbound(userText)],
...genkitContext({ context: appContext })
});
if (decision.conclusion === 'DENY' || decision.hasFailedOpen()) {
throw new Error('message blocked');
}
return ai.generate({
prompt: userText,
tools: [lookupOrder],
use: [guardMiddleware(arcjet, { sessionId: conversationId })],
context: appContext
});
Adds guardMiddleware() from @arcjet/guard/genkit/v1 as a generateMiddleware tool hook passed via ai.generate({ use }) , gating all tool invocations — including MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. and injected tools that cannot be looked up — while skipping already-branded guardTool actions. Adds @arcjet/guard/genkit/v1 versioned import path for the Genkit JS adapter (Genkit >=1.0.0 <2), requiring @arcjet/guard 1.11.0 or later and Node.js >=22.21.0 <23 || >=24.5.0; guardMiddleware requires Genkit >= 1.33 for the generateMiddleware tool hook.
2 details in Everything
Arcjet adds official CrewAI Python integration with register_arcjet_hooks, guard_tool, and free_text_arguments helpers for agent tool gating︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Gate every tool call a crew makes — deny on policy violation or Guard failure — while scanning free-text arguments for PII PII Personally Identifiable Information — data that can identify a specific individual, such as names, email addresses, or SSNs. Cyber tools flag or redact it to limit exposure and meet privacy regulations. before the side effect runs.
python
copy
from arcjet.guard import LocalDetectSensitiveInfo, TokenBucket, launch_arcjet_sync
from arcjet.guard.crewai import free_text_arguments, register_arcjet_hooks
arcjet = launch_arcjet_sync(key=ARCJET_KEY)
lookup_limit = TokenBucket(refill_rate=10, interval_seconds=60, max_tokens=10, bucket="lookups")
detect_pii = LocalDetectSensitiveInfo(deny=["EMAIL", "PHONE_NUMBER", "IP_ADDRESS", "CREDIT_CARD_NUMBER"])
handle = register_arcjet_hooks(
guard=arcjet,
tools=["lookup_order"],
action="order.looked-up",
on_guard_error="deny",
rules=lambda arguments, _ctx: [
lookup_limit(key="orders", requested=1),
detect_pii(free_text_arguments(arguments)["note"]),
],
)
Screen user-supplied text for prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. before handing it to a crew, failing closed if Guard is unavailable.
python
copy
from arcjet.guard import DetectPromptInjection, launch_arcjet_sync
arcjet = launch_arcjet_sync(key=ARCJET_KEY)
inbound = DetectPromptInjection()
decision = arcjet.guard(
label="message.received",
rules=[inbound(user_text)],
)
if decision.conclusion == "DENY" or decision.has_failed_open():
raise RuntimeError("message blocked")
crew.kickoff(inputs={"request": user_text})
Wrap a BaseTool you invoke directly (outside a crew) so it raises ArcjetDeniedError on DENY rather than silently running.
python
copy
from arcjet.guard import TokenBucket, launch_arcjet_sync
from arcjet.guard.crewai import guard_tool
arcjet = launch_arcjet_sync(key=ARCJET_KEY)
lookup_limit = TokenBucket(refill_rate=10, interval_seconds=60, max_tokens=10, bucket="lookups")
guarded_lookup = guard_tool(
guard=arcjet,
tool=lookup_order,
action="order.looked-up",
rules=[lookup_limit(key="orders", requested=1)],
on_guard_error="deny",
)
guarded_lookup.run({"order_id": "ord_123", "note": "urgent"})
Adds arcjet.guard.crewai module with register_arcjet_hooks, guard_tool, free_text_arguments, sanitize_tool_name, unregister_arcjet_hooks, ArcjetCrewAIHooks, and ToolPolicy helpers for securing CrewAI agent tool calls.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds register_arcjet_hooks to register a process-wide PRE_TOOL_CALL hook that enforces Arcjet policy before any crew, LiteAgent, or MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. adapter tool runs; raises HookAborted(reason=..., source='arcjet') on DENY. Adds guard_tool to wrap a standalone CrewAI BaseTool you invoke directly via run() or arun() , raising ArcjetDeniedError on DENY and ArcjetUnavailableError when Guard cannot be evaluated. Adds free_text_arguments helper that returns a tool's free-text arguments with opaque id keys (tool_call_id, trace_id, and any *_id key) stripped, for safe use with rules like LocalDetectSensitiveInfo. Adds launch_arcjet_sync as the required synchronous client launcher for CrewAI hooks, distinct from the async launch_arcjet.
+4 more
show less
Adds ToolPolicy for per-tool policy overrides inside register_arcjet_hooks, accepting action (required), plus rules, actor, inputs, and metadata. Adds tools option to register_arcjet_hooks to gate only named tools rather than every tool a crew executes; tool names are sanitized to match CrewAI's tools= filter. Adds action option to register_arcjet_hooks and guard_tool accepting a resource.verb slug (e.g. email.sent) that selects the matching remote policy and names the event in the Arcjet Console. Supports crewai>=1.15.3,<2 on Python 3.10 to 3.13; requires arcjet>=1.0.0; arcjet[crewai] extra is intentionally absent due to a critical RCE RCE Remote code execution, the ability to run attacker-chosen code on another system through a vulnerability. Its impact depends on the permissions and access that code receives. in chromadb (CVE-2026-45829 in the CrewAI dependency chain).
9 details in Everything
Arcjet Guard adds agent security helpers for Claude Managed Agents, covering inbound prompt screening and custom-tool gating in JS and Python.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄
└──▷ USE IT
Screen a user's inbound message for prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. before the Claude Managed Agents session sees it — block the turn without sending if Arcjet denies.
typescript
copy
import Anthropic from "@anthropic-ai/sdk";
import { detectPromptInjection } from "@arcjet/guard";
import { claudeManagedAgentsContext, guardEvents } from "@arcjet/guard/claude-managed-agents/v0";
import { arcjet } from "./arcjet.js";
const client = new Anthropic();
async function sendTurn(sessionId: string, conversationId: string, userText: string) {
const inbound = await guardEvents(
arcjet,
{
events: [{ type: "user.message", content: [{ type: "text", text: userText }] }],
inbound: {
action: "message.received",
rules: ({ text }) => [detectPromptInjection()(text)],
},
context: claudeManagedAgentsContext({ correlationId: conversationId }),
},
(body) => client.beta.sessions.events.send(sessionId, body)
);
if (!inbound.allowed) return inbound.message;
}
Gate a custom tool your app executes on agent.custom_tool_use, blocking the handler and returning a structured denial when Arcjet denies the call.
python
copy
from arcjet.guard import launch_arcjet, DetectPromptInjection
from arcjet.guard.claude_managed_agents import guard_custom_tool
arcjet = launch_arcjet(key=ARCJET_KEY)
async def run_my_tool(event, send, anthropic_session_id):
# your tool logic here
return {"result": "ok"}
protected_tool = guard_custom_tool(
guard=arcjet,
action="my_tool.executed",
run=run_my_tool,
session_id=CONVERSATION_ID,
on_guard_error="deny",
)
Adds guardEvents / guard_events helper to screen inbound user.message events for prompt injection before sessions.events.send is called, blocking the turn on DENY so the model never sees the prompt.︀︄︇︀︅︉︀︅︀︀︃︀︀︅︅︀︅︊︀︄︆︀︃︄ Adds guardCustomTool / guard_custom_tool helper to intercept agent.custom_tool_use events and block handler execution on DENY, returning the denial as user.custom_tool_result with is_error set. Adds claudeManagedAgentsContext / claude_managed_agents_context helper to attach a caller-owned correlation ID (e.g. a conversation ID) to Guard calls without minting a new ID. Adds @arcjet/guard/claude-managed-agents/v0 (JavaScript, requires @anthropic-ai/sdk >=0.86.0 <1 and Node.js >=22.21.0 <23 || >=24.5.0) and arcjet[claude-managed-agents] (Python, requires arcjet 1.1.0+, anthropic>=0.92.0,<2, CPython 3.10+) as new integration packages for Claude Managed Agents. Supports inbound.rules on guardEvents (JavaScript) as a function of { text, events } to supply SDK rules such as detectPromptInjection() per-call, alongside a top-level rules callable on guard_events (Python).
5 details in Everything
Arcjet: Adds guardTool(client, toolDefinition, policy) (JS) and guard_tool (Python) helpers that wrap an authored tool() / @tool handler and return an ArcjetDenialResult (isError: true / is_error: True) on DENY without running the handler.
└──▷ TRY IT
Block prompt-injection prompt-injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. attempts before the model sees user input — gate every conversation turn in a Claude Agent SDK loop.
copy
$ import { query } from '@anthropic-ai/claude-agent-sdk';
import { randomUUID } from 'node:crypto';
import { guardHooks } from '@arcjet/guard/claude-agent-sdk/v0';
import { detectPromptInjection } from '@arcjet/guard';
import { arcjet } from './arcjet.js';
const sessionId = conversationId ?? randomUUID();
for await (const message of query({
prompt: userText,
options: {
sessionId,
hooks: guardHooks(arcjet, {
sessionId,
inbound: {
action: 'message.received',
rules: ({ prompt }) => [detectPromptInjection()(prompt)],
},
}),
},
})) {
void message;
}
Install the Claude Code skill and wire Arcjet Guard into an existing Claude Agent SDK project in one command.
copy
$ mkdir -p ~/.claude/skills && cp -r node_modules/@arcjet/guard/skills/integrate-arcjet-guard-claude-agent-sdk ~/.claude/skills/
# Then inside Claude Code:
# /integrate-arcjet-guard-claude-agent-sdk
Adds guardHooks() (JS) and guard_hooks() (Python) helpers that register UserPromptSubmit, PreToolUse, and PostToolUse hooks to screen inbound prompts for injection and deny unwrapped built-in and MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. tools. Adds guard_hooks options inbound, tools, exclude, actor, inputs, rules, metadata, session_id, correlation_id, and on_guard_error to control which surfaces are gated and how errors are handled. Supports the detectPromptInjection() rule inside guardHooks / guard_hooks inbound to block prompt-injection attempts in UserPromptSubmit before the model sees the prompt.
3 details in Everything
Arcjet adds agent guards for Google ADK ADK Google's Agent Development Kit, an open-source framework for building and orchestrating AI agents; cyber tools use it to compose multi-agent workflows with built-in tool-calling and state management. , TanStack AI, and Claude Managed Agents across JavaScript and Python
└──▷ USE IT
Enforce Arcjet policy on every Google ADK tool call by installing guardPlugin first in the plugin list before running an agent.
javascript
copy
import { guardPlugin } from '@arcjet/guard/google-adk/v2';
const runner = new Runner({
agent: new LlmAgent({ ... }),
plugins: [guardPlugin(), ...otherPlugins],
});
await runner.runAsync(session);
Protect Claude Managed Agents custom tools and inbound user messages using the Arcjet hosted-harness guard extras.
copy
$ pip install 'arcjet[claude-managed-agents]'
# anthropic>=0.92.0,<2 is the required peer
Adds @arcjet/guard/google-adk/v2 package with guardPlugin() as a BasePlugin for Google ADK JS agents, enforcing policy on beforeToolCallback and defaulting to fail-closed on error. Adds @arcjet/guard/tanstack-ai/v0 package with onBeforeToolCall middleware hook for TanStack AI chat({ middleware }) workflows, with optional onDeny: 'abort' to stop the run. Adds @arcjet/guard/claude-managed-agents/v0 package with guardCustomTool, guardEvents, and claudeManagedAgentsContext (JS) and guard_custom_tool, guard_events, claude_managed_agents_context (Python) for Anthropic's hosted Claude Managed Agents harness, covering inbound user.message events, events.send, and custom tools on agent.custom_tool_use. Adds arcjet.guard.claude_managed_agents Python extra (arcjet[claude-managed-agents]) requiring anthropic>=0.92.0,<2 for Claude Managed Agents support. Adds arcjet.guard.strands_agents Python extra (arcjet[strands-agents]) requiring strands-agents>=1.11.0,<2, with guard_hooks emitting BeforeToolCallEvent.cancel_tool set to True or a JSON string to deny.
+2 more
show less
Extends Google ADK guard to cover LlmAgent workflows calling authored FunctionTool handlers from Runner.runAsync, with guardPlugin placed first in the plugin list. Extends Claude Managed Agents guard to enforce gates on inbound user.message and events.send, and custom tools via agent.custom_tool_use, while noting built-in tools run under Anthropic with always_allow by default.
7 details in Everything
Arcjet guards now wrap AI agent frameworks including LangChain, CrewAI, OpenAI Agents, and 12 others in JS and Python.
Adds agent wrapping support for Vercel AI SDK, LangChain, CrewAI, LangGraph, Genkit, Google ADK ADK Google's Agent Development Kit, an open-source framework for building and orchestrating AI agents; cyber tools use it to compose multi-agent workflows with built-in tool-calling and state management. , OpenAI Agents, Strands Agents, TanStack AI, Vercel Eve, Mastra, Claude Agent SDK, and Claude Managed Agents in JavaScript and Python.
1 detail in Everything
Arcjet adds a Guard adapter for Claude Managed Agents, covering inbound messages and custom tools in JavaScript and Python.
Adds @arcjet/guard/claude-managed-agents/v0 adapter for JavaScript (requires @anthropic-ai/sdk >=0.86.0 <1) exposing guardEvents, guardCustomTool, and claudeManagedAgentsContext helpers to screen inbound user.message before events.send and gate custom tools on agent.custom_tool_use. Adds arcjet[claude-managed-agents] Python extra (requires anthropic>=0.92.0,<2) exposing guard_events, guard_custom_tool, and claude_managed_agents_context for the same inbound-message and custom-tool guarding via arcjet.guard.claude_managed_agents. On custom-tool DENY, instructs callers to return user.custom_tool_result with is_error set, providing a concrete denial-handling contract for Claude Managed Agents sessions.
3 details in Everything
Arcjet bot protection now supports remote rules from the dashboard or MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. server for incident response without code changes.
Adds remote bot rules configurable from the Arcjet dashboard or MCP server, applying site-wide bot allow/deny policies without code changes or redeployment — useful for blocking a newly spotted bot category during an incident. Supports per-rule allow or deny actions on individual bots or entire bot categories, with an empty allow list blocking every detected bot. Enables hybrid bot policy: developers manage route-specific bot rules in code while SecOps SecOps The practice of merging security and IT operations teams so that threat detection, response, and infrastructure management share the same workflows, giving cyber tools a single pipeline from alert to remediation. teams apply site-wide changes via remote rules for incident response.
3 details in Everything
Arcjet adds an optional on-device Rampart NER model backend for sensitive-info detection, with configurable confidence, accelerator, and custom recognizers
└──▷ TRY IT
Install the Rampart NER backend alongside the Arcjet SDK to enable on-device detection of names, addresses, SSNs, and tax IDs.
copy
$ pip install "arcjet[sensitive-info-rampart]"
Configure the sensitive-info rule to use the Rampart backend with a raised confidence threshold and GPU GPU A graphics processing unit, a processor that performs many calculations in parallel. AI workloads use GPUs to speed up model training and inference. acceleration, denying everything the model can detect.
python
copy
from arcjet import sensitive_info, Mode
from arcjet_sensitive_info_rampart import rampart, RampartOptions, rampart_entities
rule = sensitive_info(
mode=Mode.LIVE,
backend=rampart(
RampartOptions(
min_confidence=0.7,
providers=("CUDAExecutionProvider", "CPUExecutionProvider"),
max_input_chars=50_000,
)
),
deny=rampart_entities(),
)
Adds arcjet[sensitive-info-rampart] pip extra that installs the Rampart on-device NER model (~14.7 MB, ~18.5M parameters, 4-bit quantized quantized Storing a model's weights at lower numeric precision, such as 8 or 4 bits instead of 16, so it needs less memory and runs faster. It trades a small amount of accuracy for fitting on smaller hardware. , 512-token context) as an alternative detection backend for the sensitive-info rule. Adds a rampart() backend option to the sensitive-info rule that detects names, addresses, and government or financial identifiers beyond the built-in WebAssembly WebAssembly A binary instruction format that runs in browsers and server runtimes at near-native speed, letting developers compile C, Rust, or other languages into a portable, sandboxed module any compliant host can execute. engine. Adds rampart_entities() helper (Python) to conveniently reference all entity types the Rampart model detects, usable directly in the detect list. Adds RampartOptions configuration object (Python) with tunable fields: min_confidence (default 0.5), providers (ONNX Runtime ONNX Runtime An engine that runs models saved in ONNX format across CPUs, GPUs and other accelerators. It optimizes model execution for the chosen hardware. execution providers, default (CPUExecutionProvider ,)), max_input_chars (default 100,000 — input is truncated to this before inference to bound CPU CPU The main general-purpose processor in a computer. It runs application instructions and coordinates work with memory and other hardware. cost), default_recognizers (deterministic recognizers run alongside the model; pass () to use the model alone), and model_path (directory of model files, default: bundled). Extends the SensitiveInfoEntityType enum with backend-only types — GIVEN_NAME, SURNAME, SSN, URL, TAX_ID, and more — detectable only when a supporting backend such as rampart() is configured; listing one without a compatible backend raises an error.
+3 more
show less
Supports a detect allow-list / deny-list on the sensitive-info rule: with a deny-list Arcjet blocks any detected type not named in the list; with an allow-list Arcjet allows any detected type not named in the list. Supports custom detection via a user-supplied function that receives a list of tokens and returns a same-length list of null or entity-name strings; context window context window The maximum amount of text, counted in tokens, a model can take in and produce in one request. Everything the model knows about the task has to fit inside it. size is controlled by a separate option that defaults to 1. Rampart backend requires Node.js, Bun, or Deno in JS (not edge runtimes); runs on Python 3.10+ via ONNX ONNX An open file format for trained models, so a model built in one framework can run in another runtime or on other hardware. ONNX Runtime is the common engine that executes it. Runtime (onnxruntime) installed automatically by the pip extra.
8 details in Everything
Arcjet adds content moderation as a Guard rule, detecting harmful text in JS/TS, Python, and Go before it's stored or forwarded.
└──▷ USE IT
Screen user-submitted chat messages for harmful content in a Node.js service before persisting or forwarding them.
javascript
copy
import { launchArcjet, moderateContent } from "@arcjet/guard";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const moderate = moderateContent();
const decision = await arcjet.guard({
label: "tools.chat",
rules: [moderate(userMessage)],
});
if (decision.conclusion === "DENY" && decision.reason === "MODERATE_CONTENT") {
throw new Error("Harmful content detected – rephrase your message");
}
const result = moderate.result(decision);
console.log(result?.detected, result?.billing?.unit, result?.billing?.count);
Gate an LLM LLM A large language model, a neural network trained on large amounts of text to recognize patterns and generate language. Applications use it for tasks such as answering, summarizing and writing code. output pipeline in Go on content safety before the response is returned to the user, with usage metering.
go
copy
moderation, err := arcjet.GuardModerateContent(arcjet.GuardModerateContentOptions{
Mode: arcjet.ModeLive,
})
if err != nil {
return err
}
decision, err := guard.Guard(ctx, arcjet.GuardRequest{
Label: "tools.generate",
Rules: []arcjet.GuardRuleInput{moderation.Text(userMessage)},
})
if err != nil {
return err
}
if decision.IsDenied() && decision.Reason == arcjet.ReasonModerateContent {
return errors.New("content flagged by moderation")
}
if result := moderation.Result(decision); result != nil && result.Billing != nil {
fmt.Printf("charged %d %s\n", result.Billing.Count, result.Billing.Unit)
}
Adds moderateContent() (JS/TS), ModerateContent() (Python), and arcjet.GuardModerateContent() / GuardModerateContentOptions (Go) as new Guard rules that detect harmful text in untrusted input before it is stored, displayed, or forwarded. Exposes decision.conclusion === 'DENY' / decision.reason === 'MODERATE_CONTENT' (JS/TS), decision.conclusion == 'DENY' / decision.reason == 'MODERATE_CONTENT' (Python), and decision.IsDenied() / decision.Reason == arcjet.ReasonModerateContent (Go) to branch on moderation outcomes. Returns a result object with result.detected (bool), result.billing.unit (text_units), and result.billing.count so callers can track usage metering per Guard call. Available via npm install @arcjet/guard (JS/TS), pip install arcjet (Python), and go get github.com/arcjet/arcjet-go@latest (Go).
4 details in Everything
Arcjet adds content moderation as a Guard rule for scanning user messages, tool results, and model outputs before they leave your control.
└──▷ USE IT
Measure content moderation detections in dry-run mode in Go before enabling live blocking.
go
copy
moderation, err := arcjet.GuardModerateContent(arcjet.GuardModerateContentOptions{
Mode: arcjet.ModeDryRun,
})
if err != nil {
return err
}
decision, err := guard.Guard(ctx, arcjet.GuardRequest{
Label: "tools.generate",
Rules: []arcjet.GuardRuleInput{moderation.Text(userMessage)},
})
New moderateContent() (JavaScript/TypeScript) rule for guard() that evaluates untrusted text and returns ALLOW or DENY with reason MODERATE_CONTENT; mode defaults to LIVE and is optional. New ModerateContent() (Python) rule for guard() with the same binary detected verdict and optional text_units billing; mode defaults to LIVE. New GuardModerateContent (Go) rule for Guard() where Mode is required (ModeLive or ModeDryRun); omitting Mode returns ErrInvalidMode. Supports mode: 'DRY_RUN' / ModeDryRun to evaluate moderation without blocking, allowing teams to measure detection rates before switching to LIVE. Content moderation results expose a detected / Detected boolean and optional billing with unit (text_units) and count, accessible via moderate.result(decision) / moderation.Result(decision) .
+1 more
show less
Can be combined in a single guard() / Guard() call alongside rate limiting and prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. detection to apply multiple checks at the same action boundary.
6 details in Everything
Arcjet adds prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. detection for AI pipelines, blocking jailbreaks and instruction overrides before they reach the model.
New detectPromptInjection function (JavaScript) and detect_prompt_injection function (Python) scan user input and tool results for injection patterns — jailbreaks, role-play escapes, and instruction overrides — at the request boundary before entering model context. Binary verdict exposed via decision.reason.isPromptInjection() or decision.reason.injectionDetected in JavaScript, and decision.reason_v2.type == 'PROMPT_INJECTION' in Python. Accepts a required mode parameter; in Python, omitting mode or passing threshold= raises TypeError. Supports scanning prompt inputs and tool results inside agent pipelines without an HTTP request, complementing existing content moderation capabilities.
4 details in Everything
Arcjet Guards gains structured Metadata on requests, Capture events for visibility, and an in-process test client for Guard calls.
└──▷ USE IT
Attach structured Metadata to a Guard call for downstream analytics — invoice ID, amount, and correlation ID travel with the decision without affecting it.
javascript
copy
import { guard } from '@arcjet/guard';
const decision = await guard({
label: 'refund',
actor: { userId },
inputs: [{ GuardPolicyServerString: '...' }],
metadata: {
invoice: { id: 'inv_123', amount: 4200 },
plan: 'pro',
durationMs: 160,
success: true
},
correlationId: runID
});
Record that a permitted refund action occurred for visibility, without affecting any Guard decision or budget.
javascript
copy
import { capture } from '@arcjet/guard';
async function refund(limit, input) {
// No rule was evaluated — just record the event.
await capture({ action: 'refund.issued', metadata: { invoice: input } });
}
Assert that a refund handler emits the expected capture event in a unit test with no network calls.
javascript
copy
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { registerTestClient } from '@arcjet/guard/testing';
import { refund } from './refund.js';
test('refund captures an event', async () => {
using client = registerTestClient();
await refund('inv_1');
assert.equal(client.captures[0]?.action, 'refund.issued');
});
Adds a Metadata field to guard() / protect() and every Guard rule accepting string keys mapped to any JSON-serializable value (nested objects, arrays, numbers, booleans, null) for analytics and debugging — excluded from fingerprinting and decision cache keys. Server-enforced Metadata limits: max 128 top-level keys, 4 KiB per serialized value, 768 KiB total per request; dropped keys surface as warnings on decision.warnings / decision.Warnings (one per key). SDK encoding failures per language collected into a single AJ1017 warning: JavaScript drops undefined, functions, BigInt, and circular references; Python drops datetime, sets, NaN, and circular references; Go drops channels, funcs, cycles, NaN, and invalid UTF-8 strings. Adds capture() / Capture to record that an allowed action happened as visibility data — never changes a decision and never sets hasFailedOpen() / has_failed_open() . Adds unregisterArcjet() to clear the registered client (libraries must not call it); a second registerArcjet() call does not displace the first and logs AJ3004 on the incumbent's logger.
+7 more
show less
Adds @arcjet/guard/testing (JavaScript) and arcjet.testing.register_test_client() (Python) in-process test clients that record guard() and capture() calls in memory, return fail-open verdicts, and require no network — registerTestClient() throws if a client is already registered. JavaScript using registerTestClient() auto-unregisters at block exit via using; on Node.js 22, call arcjet.unregister() from a finally block instead. Sets timeout_ms on launch_arcjet() / launch_arcjet_sync() to control the client request timeout (default 2000 ms). Rule-level Metadata is merged shallowly with guard() -level Metadata: a duplicate key's entire value is replaced, never deep-merged. Adds CorrelationId field to Guard requests for correlating calls across services. When a call trips detectSensitiveInfo, a tokenBucket in the same rule array is still decremented — split rules across two guard() calls when a false positive must not drain a legitimate caller's budget. Python protect() surfaces AJ1017 Metadata encoding warnings at WARNING log level (no warnings field on protect() ); JavaScript reports them to Arcjet rather than throwing.
12 details in Everything
Arcjet adds capture() for non-gating security event tracing across JS/TS, Python, and Go SDKs
└──▷ USE IT
Record a completed refund as a security trace event after a guard decision, correlating it to the originating workflow run.
javascript
copy
arcjet.capture({
action: "refund.issued",
correlationId: runId,
decisionId: decision.id,
metadata: {
invoice: { id: "inv_123", amount: 4200 },
refunded: true,
},
});
Drain buffered capture events before a serverless invocation exits on Cloudflare Workers to avoid losing batched telemetry.
javascript
copy
export default {
async fetch(request, env, ctx) {
const response = await handle(request);
ctx.waitUntil(arcjet.flush());
return response;
},
};
Flush outstanding capture events in a Go service shutdown, then release wasm resources.
go
copy
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
guard.Flush(ctx)
_ = guard.Close(ctx)
Adds capture() method (JS/TS: arcjet.capture() , Python: aj.capture() , Go: guard.Capture() ) to record security-relevant actions — refunds, exports, tool calls — as visibility events without affecting guard() or protect() decisions. Accepts action (required, 'resource.verb' past-tense convention), plus optional correlationId/correlation_id, decisionId/decision_id, metadata (nested JSON), occurredAt/occurred_at, and (JS only) waitUntil for serverless invocation lifetime management. Adds flush() /Flush to drain the in-memory event queue before shutdown; JS accepts an optional deadline in milliseconds (e.g. arcjet.flush(250) ), Go accepts a context.Context; defaults to a one-second deadline. Go client additionally exposes Close , which flushes outstanding events then releases local wasm. Events are queued in memory (default: 1000-event queue, batches up to 50, 100 ms delay); dropped events surface as stable AJxxxx diagnostics on the client logger rather than silently failing.
+1 more
show less
Supports waitUntil platform hook in JS for Cloudflare Workers and other edge runtimes where the invocation may freeze; Vercel context is discovered automatically without passing waitUntil.
6 details in Everything
Arcjet adds agent guard adapters for LangGraph JS, Genkit, OpenAI Agents, Claude Agent SDK, Mastra, and Eve across JavaScript and Python
Adds @arcjet/guard/langgraph/v1 adapter with guardTool() for authored tool() /StructuredTool handlers and guardToolNode() for unwrapped and MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. tools executing inside ToolNode in LangGraph JS Graph API Graph API Microsoft's unified REST API for accessing data across Microsoft 365 services — mail, calendar, users, files — letting cyber tools query or monitor cloud environments through a single authenticated endpoint. agents. Adds @arcjet/guard/genkit/v1 adapter with guardTool() on returned ToolAction objects and guardMiddleware() as a { name, instantiate } generateMiddleware hook for Genkit JS flows. Adds @arcjet/guard/openai-agents/v0 adapter with guardTool() to wrap FunctionTool.invoke after tool({ execute }) for OpenAI Agents text Agent workflows. Adds @arcjet/guard/claude-agent-sdk/v0 adapter with guardTool() for authored tools and guardHooks({ inbound }) on UserPromptSubmit for inbound prompt screening in Claude Agent SDK agents; denials return an MCP CallToolResult with isError: true and payload on structuredContent. Adds @arcjet/guard/mastra/v1 adapter for Mastra createTool handlers.
+5 more
show less
Adds @arcjet/guard/vercel-eve/v0 adapter for Eve defineTool handlers; throws ArcjetDeniedError by default with opt-in onDeny: 'result' to return payload instead. Adds Python arcjet.guard.langchain adapter with guard_action/guard_action_sync for any Python callable, BaseTool wrapping via langchain-core>=1.2.5,<2, and ArcjetMiddleware + ToolPolicy for create_agent agents (install via arcjet[langchain-agents]). Adds ArcjetCaptureHandler/ArcjetAsyncCaptureHandler for observe-only (non-denying) LangChain callback integration. Standardizes denial payloads across all JavaScript adapters with arcjetDenied: true, reason, message, retryable, and retryAfterSeconds fields; only rate-limit denials set retryable: true. Exposes decision.hasFailedOpen() to detect when a direct guard() call at non-tool sites (before graph.invoke, generate() , or run() ) has failed open rather than closed.
10 details in Everything
└──▷ BREAKING ON UPGRADE
! Unversioned import aliases such as @arcjet/guard/vercel-ai do not resolve; only versioned paths (e.g. @arcjet/guard/langgraph/v1) are valid.
Arcjet Guard adds Vercel Eve and Mastra agent framework integrations with inbound screening, connection approvals, and lifecycle hooks
Adds Vercel Eve agent guard integration: gate zero-authored-tool agents via guardApproval() on OpenAPI OpenAPI A vendor-neutral specification for describing REST APIs in a machine-readable format, maintained by the OpenAPI Initiative. Cyber tools use it to auto-generate clients, documentation, and validation from a single source of truth. /MCP MCP Model Context Protocol, a standard for connecting AI applications to external tools and data sources. A compatible server exposes capabilities an assistant can use through a common interface. connections, screen inbound channel text with guardInbound() , and observe lifecycle events with void-returning hooks; defaults to onGuardError: 'deny'. Adds Mastra agent guard integration: screen inbound text with guardProcessor, gate authored tools with tool wrappers, gate MCP/workspace tools with guardHooks beforeToolCall (which can deny), with no guardInbound or guardApproval since Mastra uses processInput and human HITL HITL Human in the loop, involving a person in an otherwise automated workflow. The person can review, approve, correct or supply information before work continues. ; defaults to onGuardError: 'deny'.
2 details in Everything
SDK releases, security briefing improvements, new activity view (2026-07-03)
Jul 3, 2026
·
issue 014
Arcjet adds proxy support, Nosecone Nonce API, enhanced security briefings, and a new Console activity view.
└──▷ USE IT
Inject a CSP CSP Content Security Policy: a browser security mechanism, standardized by the W3C, that lets a web server declare which sources of scripts, styles, and other resources a page may load, blocking injection attacks. nonce into a custom script tag from a Next.js Server Component when the <Script> component is unavailable.
javascript
copy
import { nonce } from "@nosecone/next";
export default async function Page() {
const cspNonce = await nonce();
return <script nonce={cspNonce}>{`/* your inline script */`}</script>;
}
Adds proxy support to the JS SDK 1.6. Introduces an optimized Wasm bundle across JS, Go, and Python SDKs. Delivers the first numbered Go SDK release (0.1.0) with improved per-request caching. Adds a nonce function to the Nosecone Next.js library (v1.6.1), enabling nonce access from Server Components and Route Handlers. Expands weekly security briefing emails with detailed threat tagging, country breakdowns, and updated recommendations.
+1 more
show less
Opens a new activity view in the Arcjet Console, surfacing non-HTTP Guard events (e.g., agentic file-access events) alongside web requests.
6 details in Everything
SDK releases, security briefing improvements, new activity view (2026-07-03)
Jul 3, 2026
· release notes
Arcjet adds Nonce API to Nosecone and a Console Activity view for non-HTTP events.
└──▷ USE IT
Read CSP CSP Content Security Policy: a browser security mechanism, standardized by the W3C, that lets a web server declare which sources of scripts, styles, and other resources a page may load, blocking injection attacks. nonce directly in a Server Component or Route Handler when the built-in <Script> component support is insufficient.
code
copy
import { nonce } from "@nosecone/next";
export default async function Page() {
const cspNonce = await nonce();
return <script nonce={cspNonce}>{`/* ... */`}</script>;
}
View security events for agentic applications or background tasks that trigger Guards without an immediate HTTP request.
📍 In the Arcjet Console, navigate to the Activity view to see non-HTTP Guard events alongside web requests.
Adds nonce function to the @nosecone/next package to read the current request's CSP nonce from Server Components or Route Handlers
1 detail in Everything
Running PII detection locally with the Rampart NER model (2026-07-01)
Jul 1, 2026
· release notes
Arcjet adds pluggable backend to sensitiveInfo rule, enabling local Rampart NER model for names and addresses.
└──▷ USE IT
Switch the sensitive info detection rule from the default pattern matcher to a local NER model that can detect names and addresses.
code
copy
import arcjet, { sensitiveInfo } from "@arcjet/node";
import { rampart } from "@arcjet/sensitive-info-rampart";
const aj = arcjet({
key: process.env.ARCJET_KEY!,
rules: [
sensitiveInfo({
mode: "LIVE",
deny: ["EMAIL", "GIVEN_NAME", "SURNAME", "STREET_NAME", "SSN"],
backend: rampart(), // Uses the local Rampart NER model
}),
],
});
sensitiveInfo() rule now accepts a backend option to swap the default WebAssembly WebAssembly A binary instruction format that runs in browsers and server runtimes at near-native speed, letting developers compile C, Rust, or other languages into a portable, sandboxed module any compliant host can execute. detector for an external detection engine@arcjet/sensitive-info-rampart package provides the new rampart() function that configures the Arcjet SDK to use the local Rampart NER model for PII PII Personally Identifiable Information — data that can identify a specific individual, such as names, email addresses, or SSNs. Cyber tools flag or redact it to limit exposure and meet privacy regulations. detectionArcjetSensitiveInfoType entity enum widened to include GIVEN_NAME, SURNAME, and STREET_NAME
3 details in Everything
SDK performance releases and proxy detection (2026-06-12)
Jun 12, 2026
· release notes
Arcjet JS SDK adds trusted proxy configuration for Cloudflare and updates Wasm bot detection performance.
Rebuilds the secure Wasm bot detection component with per-request performance optimizations and reduced bundle size
1 detail in Everything
Announcing advanced bot signals to detect automation without CAPTCHAs (2026-06-03)
Jun 3, 2026
· release notes
Arcjet introduces Advanced Bot Signals to detect browser automation without CAPTCHAs using client-side telemetry.
Adds a new client-side signal collection mechanism that loads a WebAssembly WebAssembly A binary instruction format that runs in browsers and server runtimes at near-native speed, letting developers compile C, Rust, or other languages into a portable, sandboxed module any compliant host can execute. module in the browser and stores results in the aj_signals cookie for server-side validation Adds a named bot rejection reason ARCJET_SIGNALS returned by the bot detection rule when advanced signals indicate automation
2 details in Everything
Designing a CLI for AI agents (2026-06-02)
Jun 2, 2026
· release notes
Arcjet announces design principles for its agent-safe CLI, introducing structured error codes, confirmation envelopes, and token-based auth.
Implements distinct exit codes (0-4) with JSON error bodies on stderr to enable programmatic branching by AI agents Introduces a mutation confirmation protocol using --confirm flag that returns a JSON envelope in stdout before execution Adds automatic detection of non-TTY stdout to default to structured JSON output for agent workflows Supports the ARCJET_TOKEN environment variable as the primary authentication mechanism, bypassing browser flows for CI/agent environments Enables field filtering via --fields flag to limit JSON output top-level keys and reduce context window context window The maximum amount of text, counted in tokens, a model can take in and produce in one request. Everything the model knows about the task has to fit inside it. usage
5 details in Everything
Unified skills, Python docs, email management (2026-05-22)
May 22, 2026
· release notes
Arcjet unifies coding agent skills into a single arcjet/skills package with expanded Python docs and email notification management.
└──▷ TRY IT
Install the unified Arcjet agent skill for your coding environment.
copy
$ npx skills add arcjet/skills
Adds email notification management allowing individual subscription control for weekly briefings, quota usage, and trial progress Expands Python SDK documentation with examples for FastAPI and Flask
2 details in Everything
Serving AI models with Open Inference Protocol APIs (2026-05-20)
May 20, 2026
· release notes
Arcjet introduces prompt injection prompt injection An attack where text the model reads, such as a web page, file or tool result, carries instructions that override the user's. It is the main security risk for agents with tools. detection using hosted LLMs LLMs A large language model, a neural network trained on large amounts of text to recognize patterns and generate language. Applications use it for tasks such as answering, summarizing and writing code. via Open Inference Protocol v2
Adds a new prompt-injection-detect model-backed building block for identifying jailbreak attempts and adversarial inputs. Implements the Open Inference Protocol v2 (KServe v2) as the standard API contract between Python model services and the Go decision service.
2 details in Everything