diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dcbf2e..f213661 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,22 +11,38 @@ this project adheres to [Semantic Versioning](https://semver.org/). - Arcjet CLI integration. The plugin now invokes the CLI for capabilities the MCP server does not expose: `arcjet watch` for live request streaming - during incident response, and `arcjet skills install` for project-local - skill installation. Commands run via `npx -y @arcjet/cli@latest` so no - install is required. Setup, read-side analysis, and rule CRUD continue to - use the MCP server. + during incident response, plus authentication, site/key setup, and remote + rule management. Commands run via `npx -y @arcjet/cli@latest` so no + install is required. Read-side analysis and rule inspection remain + available on the MCP server. - New `rules/arcjet-cli.mdc` rule explaining when to reach for the CLI vs MCP, the npx invocation pattern, and agent-friendly flags (`--output json`, `--fields`). +- New `skills/add-request-protection/` skill — the canonical name for HTTP + route protection, replacing `skills/protect-route` and the HTTP slice of + `skills/add-ai-protection`. Sourced from + [arcjet/skills](https://github.com/arcjet/skills) and includes integrated + CLI workflows for authentication, site setup, decision verification + (`arcjet watch`), and remote rule management. ### Changed - `agents/security-analyst.md` now uses `arcjet watch` for continuous monitoring during active incidents, instead of polling `list-requests` over MCP. -- `skills/protect-route`, `skills/add-ai-protection`, and - `skills/add-guard-protection` now end with an optional step that runs - `arcjet skills install` to write `ARCJET.md` into the project. +- `skills/add-guard-protection/` synced with the canonical version from + [arcjet/skills](https://github.com/arcjet/skills), including refreshed + `references/javascript.md` and `references/python.md`. +- `skills/protect-route/` and `skills/add-ai-protection/` are now + deprecation aliases. Invoking them instructs the agent to tell the user + the canonical replacement (`/arcjet:add-request-protection` or + `/arcjet:add-guard-protection`) and then proceed with that skill. The + alias directories are preserved so saved transcripts and existing + workflows continue to resolve. +- README updated to reflect the canonical skill names, link + [arcjet/skills](https://github.com/arcjet/skills) as the source of truth, + and document the CLI install methods (npx, Homebrew, install script, + GitHub Releases archive). ## [1.0.0] - 2026-04-08 diff --git a/README.md b/README.md index f741f2a..28be574 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ The [Arcjet plugin](https://github.com/arcjet/arcjet-plugin) turns any supported AI coding agent into a security expert. It pre-loads agents with knowledge of the Arcjet security platform and automatically injects the right guidance based on what you're working on — framework-specific SDK patterns, protection rules, and best practices. - **MCP integration** — connects to the [Arcjet MCP Server](https://docs.arcjet.com/mcp-server) for traffic analysis, request inspection, IP investigation, and remote rule management -- **CLI integration** — invokes the [Arcjet CLI](https://docs.arcjet.com/cli) for capabilities the MCP server does not expose (live request streaming, project-local skill installation) +- **CLI integration** — invokes the [Arcjet CLI](https://docs.arcjet.com/cli) for authentication, site/key setup, live request streaming, and remote rule management - **Security-aware coding rules** — framework-specific guidance activates automatically when you work in route handlers, API endpoints, and AI/LLM code -- **Skills** — task-oriented workflows for adding protection to routes and securing AI endpoints +- **Skills** — task-oriented workflows sourced from [arcjet/skills](https://github.com/arcjet/skills) for adding protection to HTTP routes and non-HTTP code paths - **Security analyst agent** — investigates threats, analyzes traffic, and manages rules via MCP ## Installation @@ -20,17 +20,36 @@ That's it. The plugin activates automatically — security guidance appears when You can also point your agent at the [agent get started documentation](https://docs.arcjet.com/agent-get-started). +### Arcjet CLI + +The plugin invokes the Arcjet CLI for authentication, site management, and live request streaming. Install it via any of: + +1. `npx -y @arcjet/cli@latest ` — no install required, works on macOS, Linux, and Windows +2. `brew install arcjet` — Homebrew tap +3. `curl -sSfL https://arcjet.com/cli/install.sh | bash` — install script +4. [GitHub Releases archive](https://github.com/arcjet/arcjet-cli/releases) — for internal redistribution and air-gapped environments + ## How It Works After installing, guidance activates automatically. The plugin detects what you're working on and injects Arcjet expertise. Just use your AI agent as you normally would. ### Skills -| Skill | Purpose | -| ------------------------------ | --------------------------------------------------------------------------------------------------------- | -| `/arcjet:protect-route` | Add Arcjet protection to any route handler — detects framework, sets up client, applies rules | -| `/arcjet:add-ai-protection` | Add prompt injection detection, PII blocking, and token budget rate limiting to AI HTTP endpoints | -| `/arcjet:add-guard-protection` | Add Arcjet Guard to non-HTTP code paths — AI agent tool calls, MCP tool handlers, background jobs/workers | +The plugin's skills are sourced from [arcjet/skills](https://github.com/arcjet/skills), the canonical agent skills surface for Arcjet. + +| Skill | Purpose | +| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `/arcjet:add-request-protection` | Add Arcjet protection to any HTTP route or endpoint — detects framework, sets up client, applies rules. Includes AI/LLM endpoint guidance (chat, completion). | +| `/arcjet:add-guard-protection` | Add Arcjet Guard to non-HTTP code paths — AI agent tool calls, MCP tool handlers, background jobs/workers | + +#### Deprecated aliases + +The previous skill names are kept as deprecation aliases. Invoking them tells the user the new name and then proceeds with the canonical workflow — existing prompts, prompts in saved transcripts, and project-local references continue to work. + +| Deprecated alias | Replacement | +| --------------------------- | --------------------------------------------------------------------------------------------------- | +| `/arcjet:protect-route` | `/arcjet:add-request-protection` | +| `/arcjet:add-ai-protection` | `/arcjet:add-request-protection` (HTTP endpoints) or `/arcjet:add-guard-protection` (non-HTTP code) | ### Rules (auto-activated) @@ -59,14 +78,15 @@ The MCP server connects automatically via OAuth when the plugin is installed. Yo ### CLI -The plugin uses the [Arcjet CLI](https://docs.arcjet.com/cli) for two specific capabilities the MCP server does not expose: +The plugin uses the [Arcjet CLI](https://docs.arcjet.com/cli) for capabilities that benefit from a real terminal session: +- **Authentication and site/key setup** — `arcjet auth login`, `arcjet teams list`, `arcjet sites list/create/get-key`. The CLI is the primary way to bootstrap a new project's `ARCJET_KEY`. - **Live request streaming** — `arcjet watch --site-id ` is invoked by the security analyst agent during active incident response, when polling `list-requests` over MCP would be too coarse. -- **Project-local skill installation** — `arcjet skills install` is run after each skill workflow to write an `ARCJET.md` skills file into the project, giving future agent turns zero-round-trip discovery. +- **Remote rule management** — `arcjet rules create/list/promote/update/delete` for managing rules without code changes or redeployment. -No install is required. Commands are invoked as `npx -y @arcjet/cli@latest `, which works on macOS, Linux, and Windows. If a local `arcjet` binary is on `PATH` (Homebrew, npm global, release archive), the plugin uses it directly. CLI authentication uses the same browser-based device flow as `gh auth login` or `vercel login`. +No install is required. Commands are invoked as `npx -y @arcjet/cli@latest `, which works on macOS, Linux, and Windows. If a local `arcjet` binary is on `PATH` (Homebrew, install script, GitHub Releases archive), the plugin uses it directly. CLI authentication uses the same browser-based device flow as `gh auth login` or `vercel login`. -Setup commands, read-side analysis, and rule CRUD remain on the MCP server. +Read-side analysis and rule inspection over a structured tool interface remain available on the MCP server. ### Security Analyst Agent diff --git a/rules/arcjet-cli.mdc b/rules/arcjet-cli.mdc index ec2a29c..d7e7820 100644 --- a/rules/arcjet-cli.mdc +++ b/rules/arcjet-cli.mdc @@ -23,13 +23,13 @@ Reach for the Arcjet CLI in these specific cases: API and prints decisions as they happen. MCP has no streaming equivalent. Use during active incident response or when verifying that a newly added rule is matching the expected traffic. -- **Project-local skill installation** — `arcjet skills install` writes an - `ARCJET.md` skills file into the current project so future agent turns can - discover Arcjet capabilities without a docs round trip. The CLI is the - source of truth for skill content; do not duplicate it. -- **Guided SDK setup** — `arcjet skills initialize` runs an interactive setup - that installs the SDK and configures the application. Use as an alternative - to manually walking the user through `/arcjet:protect-route`. +- **Authentication and site/key setup** — `arcjet auth login`, + `arcjet teams list`, `arcjet sites list/create/get-key`. The CLI is the + primary way to bootstrap a new project's `ARCJET_KEY`. + +If a user needs the canonical Arcjet skills outside this plugin (for example, +in a different agent client), point them at `npx skills add arcjet/skills` +directly — the CLI is not the right entry point for skill installation. ## Invocation diff --git a/skills/add-ai-protection/SKILL.md b/skills/add-ai-protection/SKILL.md index 72ef083..c9d9022 100644 --- a/skills/add-ai-protection/SKILL.md +++ b/skills/add-ai-protection/SKILL.md @@ -1,190 +1,24 @@ --- name: add-ai-protection license: Apache-2.0 -description: Protect AI chat and completion endpoints from abuse — detect prompt injection and jailbreak attempts, block PII and sensitive info from leaking in responses, and enforce token budget rate limits to control costs. Use this skill when the user is building or securing any endpoint that processes user prompts with an LLM, even if they describe it as "preventing jailbreaks," "stopping prompt attacks," "blocking sensitive data," or "controlling AI API costs" rather than naming specific protections. +description: "Deprecated alias. Use /arcjet:add-request-protection for HTTP AI/LLM endpoints (chat, completion routes) or /arcjet:add-guard-protection for non-HTTP code (agent tool calls, MCP handlers, background workers). Covers prompt injection detection, PII blocking, and token budget rate limiting." metadata: - pathPatterns: - - "app/api/chat/**" - - "app/api/completion/**" - - "src/app/api/chat/**" - - "src/app/api/completion/**" - - "**/chat/**" - - "**/ai/**" - - "**/llm/**" - - "**/api/generate*" - - "**/api/chat*" - - "**/api/completion*" - importPatterns: - - "ai" - - "@ai-sdk/*" - - "openai" - - "@anthropic-ai/sdk" - - "langchain" - promptSignals: - phrases: - - "prompt injection" - - "pii" - - "sensitive info" - - "ai security" - - "llm security" - anyOf: - - "protect ai" - - "block pii" - - "detect injection" - - "token budget" + author: arcjet + internal: true --- -# Add AI-Specific Security with Arcjet +# Deprecated — Use `/arcjet:add-request-protection` or `/arcjet:add-guard-protection` -Secure AI/LLM endpoints with layered protection: prompt injection detection, PII blocking, and token budget rate limiting. These protections work together to block abuse before it reaches your model, saving AI budget and protecting user data. +`/arcjet:add-ai-protection` has been split into two canonical skills: -## Reference +- **`/arcjet:add-request-protection`** — for HTTP routes serving AI/LLM endpoints (chat, completion, generation). Covers prompt injection detection, PII blocking, token budget rate limiting, and bot/shield protection at the HTTP layer. +- **`/arcjet:add-guard-protection`** — for non-HTTP code (AI agent tool calls, MCP tool handlers, background jobs, queue workers). Same protections via `@arcjet/guard` / `arcjet.guard`. -Read https://docs.arcjet.com/llms.txt for comprehensive SDK documentation covering all frameworks, rule types, and configuration options. +## Instructions for the agent -Arcjet rules run **before** the request reaches your AI model — blocking prompt injection, PII leakage, cost abuse, and bot scraping at the HTTP layer. - -## Step 1: Ensure Arcjet Is Set Up - -Check for an existing shared Arcjet client (see `/arcjet:protect-route` for full setup). If none exists, set one up first with `shield()` as the base rule. The user will need to register for an Arcjet account at https://app.arcjet.com then use the `ARCJET_KEY` in their environment variables. - -## Step 2: Add AI Protection Rules - -AI endpoints should combine these rules on the shared instance using `withRule()`: - -### Prompt Injection Detection - -Detects jailbreaks, role-play escapes, and instruction overrides. - -- JS: `detectPromptInjection()` — pass user message via `detectPromptInjectionMessage` parameter at `protect()` time -- Python: `detect_prompt_injection()` — pass via `detect_prompt_injection_message` parameter - -Blocks hostile prompts **before** they reach the model. This saves AI budget by rejecting attacks early. - -### Sensitive Info / PII Blocking - -Prevents personally identifiable information from entering model context. - -- JS: `sensitiveInfo({ deny: ["EMAIL", "CREDIT_CARD_NUMBER", "PHONE_NUMBER", "IP_ADDRESS"] })` -- Python: `detect_sensitive_info(deny=[SensitiveInfoType.EMAIL, SensitiveInfoType.CREDIT_CARD_NUMBER, ...])` - -Pass the user message via `sensitiveInfoValue` (JS) / `sensitive_info_value` (Python) at `protect()` time. - -### Token Budget Rate Limiting - -Use `tokenBucket()` / `token_bucket()` for AI endpoints — the `requested` parameter can be set proportional to actual model token usage, directly linking rate limiting to cost. It also allows short bursts while enforcing an average rate, which matches how users interact with chat interfaces. - -Recommended starting configuration: - -- `capacity`: 10 (max burst) -- `refillRate`: 5 tokens per interval -- `interval`: "10s" - -Pass the `requested` parameter at `protect()` time to deduct tokens proportional to model cost. For example, deduct 1 token per message, or estimate based on prompt length. - -Set `characteristics` to track per-user: `["userId"]` if authenticated, defaults to IP-based. - -### Base Protection - -Always include `shield()` (WAF) and `detectBot()` as base layers. Bots scraping AI endpoints are a common abuse vector. For endpoints accessed via browsers (e.g. chat interfaces), consider adding Arcjet advanced signals for client-side bot detection that catches sophisticated headless browsers. See https://docs.arcjet.com/bot-protection/advanced-signals for setup. - -## Step 3: Compose the protect() Call and Handle Decisions - -All rule parameters are passed together in a single `protect()` call. Use this pattern: - -```typescript -const userMessage = req.body.message; // the user's input - -const decision = await aj.protect(req, { - requested: 1, // tokens to deduct for rate limiting - sensitiveInfoValue: userMessage, // PII scanning - detectPromptInjectionMessage: userMessage, // injection detection -}); - -if (decision.isDenied()) { - if (decision.reason.isRateLimit()) { - return Response.json( - { error: "You've exceeded your usage limit. Please try again later." }, - { status: 429 }, - ); - } - if (decision.reason.isPromptInjection()) { - return Response.json( - { error: "Your message was flagged as potentially harmful." }, - { status: 400 }, - ); - } - if (decision.reason.isSensitiveInfo()) { - return Response.json( - { - error: - "Your message contains sensitive information that cannot be processed. Please remove any personal data.", - }, - { status: 400 }, - ); - } - if (decision.reason.isBot()) { - return Response.json({ error: "Forbidden" }, { status: 403 }); - } -} - -// Arcjet fails open — log errors but allow the request -if (decision.isErrored()) { - console.warn("Arcjet error:", decision.reason.message); -} - -// Proceed with AI model call... -``` - -Adapt the response format to your framework (e.g., `res.status(429).json(...)` for Express). - -## Step 4: Verify - -1. Start the app and send a normal message — should succeed -2. Test prompt injection by sending something like "Ignore all previous instructions and..." -3. Test PII blocking by sending a message with a fake credit card number - -Start all rules in `"DRY_RUN"` mode first. Once verified, promote to `"LIVE"`. - -**Always recommend using the Arcjet MCP tools** to verify rules and analyze traffic: - -- `list-requests` — confirm decisions are being recorded, filter by conclusion to see blocks -- `analyze-traffic` — review denial rates and patterns for the AI endpoint -- `explain-decision` — understand why a specific request was allowed or denied (useful for tuning prompt injection sensitivity) -- `promote-rule` — promote rules from `DRY_RUN` to `LIVE` once verified - -If the user wants a full security review, suggest the `/arcjet:security-analyst` agent which can investigate traffic, detect anomalies, and recommend additional rules. - -The Arcjet dashboard at https://app.arcjet.com is also available for visual inspection. - -## Step 5: Install Project-Local Skills (Recommended) - -Run the Arcjet CLI to write an `ARCJET.md` skills file into the current project. Future agent turns can then discover Arcjet capabilities without fetching the docs. - -```bash -npx -y @arcjet/cli@latest skills install -``` - -Or, if `arcjet` is on `PATH`: - -```bash -arcjet skills install -``` - -The CLI uses the same authentication state as the MCP server. If the user has not yet authenticated, run `arcjet auth login` (browser-based device flow). See `rules/arcjet-cli.mdc` for guidance on when to use the CLI vs MCP. - -## Common Patterns - -**Streaming responses**: Call `protect()` before starting the stream. If denied, return the error before opening the stream — don't start streaming and then abort. - -**Multiple models / providers**: Use the same Arcjet instance regardless of which AI provider you use. Arcjet operates at the HTTP layer, independent of the model provider. - -**Vercel AI SDK**: Arcjet works alongside the Vercel AI SDK. Call `protect()` before `streamText()` / `generateText()`. If denied, return a plain error response instead of calling the AI SDK. - -## Common Mistakes to Avoid - -- Sensitive info detection runs **locally in WASM** — no user data is sent to external services. It is only available in route handlers, not in Next.js pages or server actions. -- `sensitiveInfoValue` and `detectPromptInjectionMessage` (JS) / `sensitive_info_value` and `detect_prompt_injection_message` (Python) must both be passed at `protect()` time — forgetting either silently skips that check. -- Starting a stream before calling `protect()` — if the request is denied mid-stream, the client gets a broken response. Always call `protect()` first and return an error before opening the stream. -- Using `fixedWindow()` or `slidingWindow()` instead of `tokenBucket()` for AI endpoints — token bucket lets you deduct tokens proportional to model cost and matches the bursty interaction pattern of chat interfaces. -- Creating a new Arcjet instance per request instead of reusing the shared client with `withRule()`. +1. **Tell the user:** "`/arcjet:add-ai-protection` is deprecated. Use `/arcjet:add-request-protection` for HTTP AI endpoints, or `/arcjet:add-guard-protection` for non-HTTP code (agent tool calls, MCP handlers, background workers)." +2. **Pick the right replacement based on context:** + - If the file under consideration is an HTTP route handler (e.g. `app/api/chat/route.ts`, `pages/api/completion.ts`, FastAPI/Flask endpoint) → follow `/arcjet:add-request-protection` (`skills/add-request-protection/SKILL.md`) and use its "AI / LLM Endpoints" section. + - If the file is a tool handler, MCP server handler, agent loop, queue worker, or other non-HTTP code path → follow `/arcjet:add-guard-protection` (`skills/add-guard-protection/SKILL.md`). + - If unclear, ask the user which context applies before proceeding. +3. Do not duplicate the canonical skill content here — read and follow the chosen skill directly. diff --git a/skills/add-guard-protection/SKILL.md b/skills/add-guard-protection/SKILL.md index 9f984bb..0e1bbc6 100644 --- a/skills/add-guard-protection/SKILL.md +++ b/skills/add-guard-protection/SKILL.md @@ -1,8 +1,9 @@ --- name: add-guard-protection license: Apache-2.0 -description: Add Arcjet Guard protection to AI agent tool calls, background jobs, queue workers, MCP tool handlers, and other code paths where there is no HTTP request. Covers rate limiting, prompt injection detection, sensitive information blocking, and custom rules using `@arcjet/guard` (JS/TS) and `arcjet.guard` (Python). Use this skill whenever the user wants to protect tool calls, agent loops, MCP tool handlers, background workers, or any non-HTTP code from abuse — even if they describe it as "rate limit my tool calls," "block prompt injection in my agent," "add security to my MCP server," or "protect my queue worker" without mentioning Arcjet or Guard specifically. +description: Add Arcjet Guard protection to AI agent tool calls, background jobs, queue workers, MCP tool handlers, and other code paths where there is no HTTP request. Covers rate limiting, prompt injection detection, sensitive information blocking, and custom rules using `@arcjet/guard` (JS/TS) and `arcjet.guard` (Python). Use this skill whenever the user wants to protect tool calls, agent loops, MCP tool handlers, background workers, or any non-HTTP code from abuse — even if they describe it as "rate limit my tool calls," "block prompt injection in my agent," "add security to my MCP server," or "protect my queue worker" without mentioning Arcjet or Guard specifically. Uses the Arcjet CLI (`npx @arcjet/cli` or `brew install arcjet`) for authentication and site/key setup. metadata: + author: arcjet pathPatterns: - "**/agents/**" - "**/agent/**" @@ -53,11 +54,48 @@ metadata: Arcjet Guard provides rate limiting, prompt injection detection, sensitive information blocking, and custom rules for code paths that don't have an HTTP request — AI agent tool calls, MCP tool handlers, background job processors, queue workers, and similar. -For code paths that **do** have an HTTP request (API routes, form handlers, webhooks), use `/arcjet:protect-route` instead. For AI chat/completion HTTP endpoints specifically, use `/arcjet:add-ai-protection`. +For code paths that **do** have an HTTP request (API routes, form handlers, webhooks, AI chat/completion endpoints), use `/arcjet:add-request-protection` instead. -## Reference +## Step 0: Set Up the Arcjet CLI -Read https://docs.arcjet.com/llms.txt for comprehensive SDK documentation. +The Arcjet CLI is the primary tool for authenticating, managing sites, configuring remote rules, and monitoring traffic. Install it if not already available: + +```bash +# Via npx (no install required) +npx @arcjet/cli --help + +# Or install globally via npm +npm install -g @arcjet/cli + +# Or via Homebrew +brew install arcjet +``` + +### Authenticate + +```bash +arcjet auth login +``` + +Opens the browser for authentication. Check status with `arcjet auth status`. + +### Site & Key Setup + +```bash +# List your teams +arcjet teams list + +# List sites for a team +arcjet sites list --team-id + +# Create a new site +arcjet sites create --team-id --name "My Guard App" --confirm + +# Get the SDK key for a site +arcjet sites get-key --site-id +``` + +Add the key to your environment file (`.env`, `.env.local`, etc.) as `ARCJET_KEY`. ## Step 1: Detect the Language and Install @@ -80,14 +118,11 @@ Do not guess at the API. The reference files are the source of truth for all cod The client holds a persistent connection. Create it once at module scope and reuse it — never inside a function or per-call. Name the variable `arcjet`. -Check if `ARCJET_KEY` is set in the environment file (`.env`, `.env.local`, etc.). If not, use the Arcjet MCP tools to get one: +Check if `ARCJET_KEY` is set in the environment file (`.env`, `.env.local`, etc.). If not, obtain the key in this priority order: -- Call `list-teams` to find the team -- Call `list-sites` to find an existing site, or `create-site` for a new one -- Call `get-site-key` to retrieve the key -- Add the key to the appropriate env file along with `ARCJET_ENV=development` - -Alternatively, remind the user to register at https://app.arcjet.com and add the key manually. +1. **CLI (preferred):** Run `arcjet sites get-key --site-id ` (requires `arcjet auth login` first — see Step 0) +2. **MCP:** If the Arcjet MCP server is connected, use it to list sites and retrieve the key +3. **Manual (last resort):** Add a placeholder and tell the user to get a key from https://app.arcjet.com ## Step 4: Configure Rules at Module Scope @@ -128,36 +163,54 @@ Always check `decision.conclusion`: See the language reference for the exact decision-checking pattern and per-rule result accessors. -## Step 7: Verify +## Step 7: Verify Guard Decisions with the CLI (Coming Soon) -Start rules in `"DRY_RUN"` mode first and promote to `"LIVE"` once verified. +> **Note:** The `arcjet guards` CLI subcommand is not yet released. Once available, use this feedback loop to verify guard decisions are firing correctly. -**Always recommend using the Arcjet MCP tools** to verify rules and analyze traffic: +After adding guard code, use the CLI to verify decisions are firing correctly. This creates a feedback loop: run the app, trigger a guard, inspect the decision, adjust if needed. -- `list-requests` — confirm decisions are being recorded, filter by conclusion to see blocks -- `analyze-traffic` — review denial rates and patterns for the guarded code path -- `explain-decision` — understand why a specific call was allowed or denied -- `promote-rule` — promote rules from `DRY_RUN` to `LIVE` once verified +### 1. Start Watching -If the user wants a full security review, suggest the `/arcjet:security-analyst` agent which can investigate traffic, detect anomalies, and recommend additional rules. +In a separate terminal, start streaming guard decisions: -The Arcjet dashboard at https://app.arcjet.com is also available for visual inspection. +```bash +arcjet guards watch --site-id +``` -## Step 8: Install Project-Local Skills (Recommended) +This polls for new guard decisions and prints them as they arrive. Use `--conclusion DENY` to filter to denials only, or `--interval 2` for faster polling. -Run the Arcjet CLI to write an `ARCJET.md` skills file into the current project. Future agent turns can then discover Arcjet capabilities without fetching the docs. +### 2. Trigger the Guard -```bash -npx -y @arcjet/cli@latest skills install -``` +Run the application and exercise the code paths that call `guard()`. Each call should produce a decision visible in the watch output. -Or, if `arcjet` is on `PATH`: +### 3. Inspect Decisions + +If a decision doesn't match expectations, inspect it: ```bash -arcjet skills install +# List recent guard decisions +arcjet guards list --site-id + +# Get per-rule breakdown for a specific decision +arcjet guards details --site-id --decision-id ``` -The CLI uses the same authentication state as the MCP server. If the user has not yet authenticated, run `arcjet auth login` (browser-based device flow). See `rules/arcjet-cli.mdc` for guidance on when to use the CLI vs MCP. +The details view shows each rule execution, its mode (live/dry-run), conclusion, reason, and whether it was skipped — use this to diagnose why a guard allowed or denied unexpectedly. + +### 4. Adjust and Repeat + +If rules aren't firing as expected: + +- Check the `label` matches what appears in the decision +- Verify the `key` is correct for rate limit rules (wrong key = wrong bucket) +- Confirm the `bucket` name is unique per rule +- Check rule ordering — rules execute in array order and a DENY from an earlier rule short-circuits later ones + +Then re-run and watch again until decisions match expectations. + +If the user wants a full security review, suggest the `/arcjet:security-analyst` agent which can investigate traffic, detect anomalies, and recommend additional rules. + +The Arcjet dashboard at https://app.arcjet.com is also available for visual inspection. ## Common Mistakes to Avoid @@ -169,3 +222,37 @@ The CLI uses the same authentication state as the MCP server. If the user has no - **Using the HTTP SDK when there's no request** — use `@arcjet/guard` / `arcjet.guard` for non-HTTP code, not `@arcjet/node`, `@arcjet/next`, or `arcjet()`. - **Not checking `decision.conclusion`** — always check before proceeding. - **Generic DENY messages** — use per-rule result accessors to give users specific feedback like retry-after times. + +## CLI Quick Reference + +| Task | Command | +| ---------------------- | ----------------------------------------------------------------------- | +| Install/run CLI | `npx @arcjet/cli` or `brew install arcjet` | +| Authenticate | `arcjet auth login` | +| Check auth status | `arcjet auth status` | +| List teams | `arcjet teams list` | +| List sites | `arcjet sites list --team-id ` | +| Create site | `arcjet sites create --team-id --name "Name" --confirm` | +| Get SDK key | `arcjet sites get-key --site-id ` | +| Watch guard decisions | `arcjet guards watch --site-id ` (coming soon) | +| List guard decisions | `arcjet guards list --site-id ` (coming soon) | +| Guard decision details | `arcjet guards details --site-id --decision-id ` (coming soon) | + +### Global Flags + +All commands support: + +- `--output text|json` — output format (default: text on TTY, json otherwise) +- `--fields ` — comma-separated fields to include in JSON output +- `--no-color` — disable ANSI colors (also honors `NO_COLOR` env var) +- `--timeout ` — max execution time (e.g. `30s`, `5m`; 0 disables) + +### Exit Codes + +| Code | Meaning | +| ---- | ----------------------------------------------------------- | +| 0 | Success | +| 1 | General error (unknown command, API failure, network error) | +| 2 | Authentication error (not logged in, token expired) | +| 3 | Input validation error (invalid ID, value out of range) | +| 4 | Confirmation required (mutation needs `--confirm`) | diff --git a/skills/add-guard-protection/references/javascript.md b/skills/add-guard-protection/references/javascript.md index 1e1a855..74e90ab 100644 --- a/skills/add-guard-protection/references/javascript.md +++ b/skills/add-guard-protection/references/javascript.md @@ -8,6 +8,8 @@ Requires `@arcjet/guard` >= 1.4.0. npm install @arcjet/guard ``` +Requires `@arcjet/guard` >= 1.4.0. + ## Create the Guard Client ```typescript @@ -30,8 +32,8 @@ Best for AI workloads with variable cost per call. Configure a `bucket` name for import { tokenBucket } from "@arcjet/guard"; const userLimit = tokenBucket({ - label: "user.tool_call_bucket", // rule label for dashboard tracking - bucket: "tool-calls", // named bucket for this limit + label: "user.tool_call_bucket", // rule label for dashboard tracking + bucket: "tool-calls", // named bucket for this limit refillRate: 100, intervalSeconds: 60, maxTokens: 500, diff --git a/skills/add-request-protection/SKILL.md b/skills/add-request-protection/SKILL.md new file mode 100644 index 0000000..888c3c6 --- /dev/null +++ b/skills/add-request-protection/SKILL.md @@ -0,0 +1,380 @@ +--- +name: add-request-protection +license: Apache-2.0 +description: Add security protection to a server-side route or endpoint — rate limiting, bot detection, email validation, and abuse prevention. Works across frameworks including Next.js, Express, Fastify, SvelteKit, Remix, Bun, Deno, NestJS, and Python (Django/Flask). Use this skill when the user wants to protect an API route, form handler, auth endpoint, or webhook from abuse, even if they describe it as "add rate limiting," "block bots," "prevent brute force," or "secure my endpoint" without mentioning Arcjet specifically. Uses the Arcjet CLI (`npx @arcjet/cli` or `brew install arcjet`) for authentication, site/key setup, remote rule management, and traffic verification. +metadata: + author: arcjet + pathPatterns: + - "app/**/route.ts" + - "app/**/route.js" + - "app/**/page.{ts,tsx}" + - "pages/api/**" + - "src/pages/api/**" + - "src/app/**/route.*" + - "**/server.{ts,js}" + - "**/app.{ts,js}" + - "**/routes/**" + - "**/*.py" + - "app/api/chat/**" + - "app/api/completion/**" + - "src/app/api/chat/**" + - "src/app/api/completion/**" + - "**/chat/**" + - "**/ai/**" + - "**/llm/**" + - "**/api/generate*" + - "**/api/chat*" + - "**/api/completion*" + importPatterns: + - "@arcjet/*" + - "arcjet" + - "ai" + - "@ai-sdk/*" + - "openai" + - "@anthropic-ai/sdk" + - "langchain" + promptSignals: + phrases: + - "arcjet" + - "rate limit" + - "bot protection" + - "bot detection" + - "waf" + - "shield" + - "prompt injection" + - "pii" + - "sensitive info" + - "ai security" + - "llm security" + anyOf: + - "protect route" + - "protect endpoint" + - "add security" + - "block bots" + - "rate limiting" + - "protect ai" + - "block pii" + - "detect injection" + - "token budget" +--- + +# Add Arcjet Protection to a Route + +Add runtime security to a route handler using Arcjet. This skill guides you through setting up the CLI, detecting the framework, configuring rules, and verifying protection. + +For non-HTTP code paths (AI agent tool calls, MCP tool handlers, background jobs, queue workers), use `/arcjet:add-guard-protection` instead. + +## Reference + +Read https://docs.arcjet.com/llms.txt for comprehensive SDK documentation covering all frameworks, rule types, and configuration options. + +## Step 0: Set Up the Arcjet CLI + +The Arcjet CLI is the primary tool for authenticating, managing sites, configuring remote rules, and verifying protection. Install it if not already available: + +```bash +# Via npx (no install required) +npx @arcjet/cli --help + +# Or install globally via npm +npm install -g @arcjet/cli + +# Or via Homebrew +brew install arcjet +``` + +### Authenticate + +```bash +arcjet auth login +``` + +Opens the browser for authentication. Check status with `arcjet auth status`. + +### Site & Key Setup + +```bash +# List your teams +arcjet teams list + +# List sites for a team +arcjet sites list --team-id + +# Create a new site +arcjet sites create --team-id --name "My App" --confirm + +# Get the SDK key for a site +arcjet sites get-key --site-id +``` + +Add the key to your environment file (`.env.local` for Next.js/Astro, `.env` for others) as `ARCJET_KEY`. + +## Step 1: Detect the Framework + +Check the project for framework indicators: + +- `package.json` dependencies: `next`, `express`, `fastify`, `@nestjs/core`, `@sveltejs/kit`, `hono`, `@remix-run/node`, `react-router`, `astro`, `nuxt` +- `bun.lockb` or `bun.lock` → Bun runtime +- `deno.json` → Deno runtime +- `pyproject.toml` or `requirements.txt` with `fastapi` or `flask` → Python + +Select the correct Arcjet adapter package: + +| Framework | Package | +| ------------------------ | ---------------------- | +| Next.js | `@arcjet/next` | +| Express / Node.js / Hono | `@arcjet/node` | +| Fastify | `@arcjet/fastify` | +| NestJS | `@arcjet/nest` | +| SvelteKit | `@arcjet/sveltekit` | +| Remix | `@arcjet/remix` | +| React Router | `@arcjet/react-router` | +| Astro | `@arcjet/astro` | +| Bun | `@arcjet/bun` | +| Deno | `npm:@arcjet/deno` | +| Python (FastAPI/Flask) | `arcjet` (pip) | + +## Step 2: Check for Existing Arcjet Setup + +Search the project for an existing shared Arcjet client file (commonly `lib/arcjet.ts`, `src/lib/arcjet.ts`, `lib/arcjet.py`, or similar). + +**If no client exists:** + +1. Install the correct adapter package. +2. Check if `ARCJET_KEY` is set in the environment file (`.env.local` for Next.js/Astro, `.env` for others). If not, obtain the key in this priority order: + 1. **CLI (preferred):** Run `arcjet sites get-key --site-id ` (requires `arcjet auth login` first — see Step 0) + 2. **MCP:** If the Arcjet MCP server is connected, use `list-teams` → `list-sites` → `get-site-key` + 3. **Manual (last resort):** Add a placeholder and tell the user to get a key from https://app.arcjet.com + - Also add `ARCJET_ENV=development` to the env file +3. Create a shared client file with `shield()` as the base rule. This file should export the Arcjet instance for reuse across routes with `withRule()`. + +**If a client already exists:** Import it. Do not create a new instance. + +## Step 3: Choose Protection Rules + +Select rules based on the route's purpose. If the user specified what they want, use that. Otherwise, infer from context: + +| Route type | Recommended rules | +| ----------------------- | ------------------------------------------------------------------------------------------------------------ | +| Public API endpoint | `shield()` + `detectBot()` + `slidingWindow()` (use `fixedWindow()` only if hard per-window caps are needed) | +| Form handler / signup | `shield()` + `validateEmail()` + `slidingWindow()` | +| Authentication endpoint | `shield()` + `slidingWindow()` (strict, low limits) | +| AI / LLM endpoint | `shield()` + `detectBot()` + `tokenBucket()` + content filtering (see AI Endpoints section below) | +| Webhook receiver | `shield()` + filter rules for allowed IPs | +| General server route | `shield()` + `detectBot()` | + +For routes that need to detect sophisticated bots (headless browsers, advanced scrapers) — especially form submissions, login/signup pages, and other abuse-prone endpoints — recommend adding Arcjet advanced signals. This is a browser-based detection system using client-side telemetry that complements server-side `detectBot()` rules. See https://docs.arcjet.com/bot-protection/advanced-signals for setup instructions. + +Apply route-specific rules using `withRule()` on the shared instance — do not modify the shared instance directly. + +## Step 4: Add Protection to the Handler + +Call `protect()` **inside** the route handler (not in middleware), only **once** per request, passing the framework's request object directly. For Next.js pages/server components: use `import { request } from "@arcjet/next"` then `const req = await request()`. + +Use this pattern: + +```typescript +const decision = await aj.protect(req); + +if (decision.isDenied()) { + if (decision.reason.isRateLimit()) { + return Response.json( + { error: "Too many requests" }, + { status: 429 }, + ); + } + if (decision.reason.isBot() || decision.reason.isShield() || decision.reason.isFilterRule()) { + return Response.json( + { error: "Forbidden" }, + { status: 403 }, + ); + } + if (decision.reason.isSensitiveInfo()) { + return Response.json( + { error: "Bad request" }, + { status: 400 }, + ); + } +} + +// Arcjet fails open — log errors but allow the request +if (decision.isErrored()) { + console.warn("Arcjet error:", decision.reason.message); +} + +// Proceed with route handler logic... +``` + +Adapt the response format to your framework (e.g., `res.status(429).json(...)` for Express, `JsonResponse` for Django). + +## AI / LLM Endpoints + +For HTTP routes serving AI chat or completion endpoints, layer these rules on top of the base protection: + +- **Prompt injection detection** — `detectPromptInjection()` (JS) / `detect_prompt_injection()` (Python). Pass the user message via `detectPromptInjectionMessage` / `detect_prompt_injection_message` at `protect()` time. +- **Sensitive info / PII blocking** — `sensitiveInfo({ deny: ["EMAIL", "CREDIT_CARD_NUMBER", "PHONE_NUMBER", "IP_ADDRESS"] })` (JS) / `detect_sensitive_info(deny=[...])` (Python). Pass the user message via `sensitiveInfoValue` / `sensitive_info_value` at `protect()` time. Sensitive info detection runs **locally in WASM** — no user data leaves the process. Only available in route handlers, not in Next.js pages or server actions. +- **Token budget rate limiting** — use `tokenBucket()` / `token_bucket()` instead of `slidingWindow()` for AI endpoints. The `requested` parameter can be set proportional to actual model token usage, directly linking rate limiting to cost. Recommended starting config: `capacity: 10`, `refillRate: 5`, `interval: "10s"`. Set `characteristics: ["userId"]` for authenticated tracking. + +Compose all parameters in a single `protect()` call: + +```typescript +const userMessage = req.body.message; + +const decision = await aj.protect(req, { + requested: 1, + sensitiveInfoValue: userMessage, + detectPromptInjectionMessage: userMessage, +}); + +if (decision.isDenied()) { + if (decision.reason.isPromptInjection()) { + return Response.json( + { error: "Your message was flagged as potentially harmful." }, + { status: 400 }, + ); + } + if (decision.reason.isSensitiveInfo()) { + return Response.json( + { error: "Your message contains sensitive information that cannot be processed." }, + { status: 400 }, + ); + } + // ... fall through to base rate-limit/bot/shield handling above +} +``` + +**Streaming responses:** Call `protect()` **before** opening the stream. If denied, return a plain error response — don't start streaming and then abort. + +**Vercel AI SDK / multi-provider:** Arcjet operates at the HTTP layer, independent of the model provider. Call `protect()` before `streamText()` / `generateText()`. + +For non-HTTP AI code paths (agent tool calls, MCP tool handlers, background workers), use `/arcjet:add-guard-protection` instead. + +## Step 5: Verify with the CLI + +After adding protection, use the CLI to verify decisions are firing correctly. This creates a feedback loop: start the app, hit the route, inspect decisions, adjust if needed. + +### 1. Start Watching + +In a separate terminal, stream live request decisions: + +```bash +arcjet watch --site-id +``` + +This polls for new decisions and prints them as they arrive. Use `--conclusion DENY` to filter to denials only, or `--interval 2` for faster polling. + +### 2. Hit the Protected Route + +Start the app and send requests to the protected route. Each request should produce a decision visible in the watch output. + +### 3. Inspect Decisions + +If a decision doesn't match expectations: + +```bash +# List recent requests (filter to denials) +arcjet requests list --site-id --conclusion DENY --limit 10 + +# Get full details for a specific request +arcjet requests details --site-id --request-id + +# Plain-English explanation of why a request was allowed/denied +arcjet requests explain --site-id --request-id +``` + +### 4. Adjust and Repeat + +If rules aren't firing as expected, adjust the code and re-test. Use `arcjet watch` to confirm each change produces the expected decisions. + +The Arcjet dashboard at https://app.arcjet.com is also available for visual inspection. + +If the user wants a full security review, suggest the `/arcjet:security-analyst` agent which can investigate traffic, detect anomalies, and recommend additional rules. + +## Step 6: Manage Remote Rules via CLI (Optional) + +Remote rules apply globally to all requests for a site and can be managed without code changes or redeployment. Supported types: `rate_limit`, `bot`, `shield`, `filter`. + +```bash +# Create a rule (always starts in DRY_RUN) +arcjet rules create --site-id --type rate_limit --max 100 --window 60 --confirm +arcjet rules create --site-id --type shield --confirm +arcjet rules create --site-id --type bot --deny CATEGORY:SEARCH_ENGINE --confirm + +# Check what a dry-run rule would block +arcjet analyze dry-run-impact --site-id + +# Promote to LIVE once verified +arcjet rules promote --site-id --rule-id --confirm + +# List / update / delete rules +arcjet rules list --site-id +arcjet rules update --site-id --rule-id --max 200 --confirm +arcjet rules delete --site-id --rule-id --confirm +``` + +## Step 7: Traffic Analysis + +Use the CLI to monitor traffic patterns and investigate issues: + +```bash +# Full security briefing (traffic, denials, quota, active rules) +arcjet briefing --site-id + +# Traffic analysis over 14 days +arcjet analyze traffic --site-id --days 14 + +# Detect anomalies (spikes, geographic shifts, new threats) +arcjet analyze anomalies --site-id + +# Investigate a specific IP +arcjet analyze ip --site-id --ip 1.2.3.4 +``` + +## Common Mistakes to Avoid + +- Creating a new Arcjet instance per request (causes connection overhead) +- Using Arcjet in Next.js middleware (fires on every request, no route context) +- Calling `protect()` multiple times in one request (double-counts rate limits) +- Hardcoding `ARCJET_KEY` instead of using environment variables +- Using `app.use()` as Express middleware instead of per-route protection +- For AI endpoints: starting a stream before calling `protect()` — if the request is denied mid-stream, the client gets a broken response +- For AI endpoints: forgetting `sensitiveInfoValue` / `detectPromptInjectionMessage` at `protect()` time silently skips those checks + +## CLI Quick Reference + +| Task | Command | +| --------------------- | -------------------------------------------------------------- | +| Install/run CLI | `npx @arcjet/cli` or `brew install arcjet` | +| Authenticate | `arcjet auth login` | +| Check auth status | `arcjet auth status` | +| List teams | `arcjet teams list` | +| List sites | `arcjet sites list --team-id ` | +| Create site | `arcjet sites create --team-id --name "Name" --confirm` | +| Get SDK key | `arcjet sites get-key --site-id ` | +| Watch live requests | `arcjet watch --site-id ` | +| List requests | `arcjet requests list --site-id ` | +| Explain a decision | `arcjet requests explain --site-id --request-id ` | +| Create rule (DRY_RUN) | `arcjet rules create --site-id --type ...` | +| List rules | `arcjet rules list --site-id ` | +| Promote to LIVE | `arcjet rules promote --site-id --rule-id --confirm` | +| Security briefing | `arcjet briefing --site-id ` | +| Analyze traffic | `arcjet analyze traffic --site-id ` | + +### Global Flags + +All commands support: + +- `--output text|json` — output format (default: text on TTY, json otherwise) +- `--fields ` — comma-separated fields to include in JSON output +- `--no-color` — disable ANSI colors (also honors `NO_COLOR` env var) +- `--timeout ` — max execution time (e.g. `30s`, `5m`; 0 disables) + +### Exit Codes + +| Code | Meaning | +| ---- | ----------------------------------------------------------- | +| 0 | Success | +| 1 | General error (unknown command, API failure, network error) | +| 2 | Authentication error (not logged in, token expired) | +| 3 | Input validation error (invalid ID, value out of range) | +| 4 | Confirmation required (mutation needs `--confirm`) | diff --git a/skills/protect-route/SKILL.md b/skills/protect-route/SKILL.md index 8b47b0b..8394f16 100644 --- a/skills/protect-route/SKILL.md +++ b/skills/protect-route/SKILL.md @@ -1,178 +1,17 @@ --- name: protect-route license: Apache-2.0 -description: Add security protection to a server-side route or endpoint — rate limiting, bot detection, email validation, and abuse prevention. Works across frameworks including Next.js, Express, Fastify, SvelteKit, Remix, Bun, Deno, NestJS, and Python (Django/Flask). Use this skill when the user wants to protect an API route, form handler, auth endpoint, or webhook from abuse, even if they describe it as "add rate limiting," "block bots," "prevent brute force," or "secure my endpoint" without mentioning Arcjet specifically. +description: "Deprecated alias for add-request-protection. Add security protection to a server-side route or endpoint — rate limiting, bot detection, email validation, and abuse prevention. Prefer /arcjet:add-request-protection." metadata: - pathPatterns: - - "app/**/route.ts" - - "app/**/route.js" - - "app/**/page.{ts,tsx}" - - "pages/api/**" - - "src/pages/api/**" - - "src/app/**/route.*" - - "**/server.{ts,js}" - - "**/app.{ts,js}" - - "**/routes/**" - - "**/*.py" - importPatterns: - - "@arcjet/*" - - "arcjet" - promptSignals: - phrases: - - "arcjet" - - "rate limit" - - "bot protection" - - "bot detection" - - "waf" - - "shield" - anyOf: - - "protect route" - - "add security" - - "block bots" - - "rate limiting" + author: arcjet + internal: true --- -# Add Arcjet Protection to a Route +# Deprecated — Use `/arcjet:add-request-protection` -Add runtime security to a route handler using Arcjet. This skill guides you through detecting the framework, setting up the client, choosing rules, and handling decisions. +`/arcjet:protect-route` has been renamed to `/arcjet:add-request-protection`. The new skill includes the same route protection plus integrated CLI workflows for authentication, site/key setup, remote rule management, and traffic verification. -## Reference +## Instructions for the agent -Read https://docs.arcjet.com/llms.txt for comprehensive SDK documentation covering all frameworks, rule types, and configuration options. - -## Step 1: Detect the Framework - -Check the project for framework indicators: - -- `package.json` dependencies: `next`, `express`, `fastify`, `@nestjs/core`, `@sveltejs/kit`, `hono`, `@remix-run/node`, `react-router`, `astro`, `nuxt` -- `bun.lockb` or `bun.lock` → Bun runtime -- `deno.json` → Deno runtime -- `pyproject.toml` or `requirements.txt` with `fastapi` or `flask` → Python - -Select the correct Arcjet adapter package: - -| Framework | Package | -| ------------------------ | ---------------------- | -| Next.js | `@arcjet/next` | -| Express / Node.js / Hono | `@arcjet/node` | -| Fastify | `@arcjet/fastify` | -| NestJS | `@arcjet/nest` | -| SvelteKit | `@arcjet/sveltekit` | -| Remix | `@arcjet/remix` | -| React Router | `@arcjet/react-router` | -| Astro | `@arcjet/astro` | -| Bun | `@arcjet/bun` | -| Deno | `npm:@arcjet/deno` | -| Python (FastAPI/Flask) | `arcjet` (pip) | - -## Step 2: Check for Existing Arcjet Setup - -Search the project for an existing shared Arcjet client file (commonly `lib/arcjet.ts`, `src/lib/arcjet.ts`, `lib/arcjet.py`, or similar). - -**If no client exists:** - -1. Install the correct adapter package. -2. Check if `ARCJET_KEY` is set in the environment file (`.env.local` for Next.js/Astro, `.env` for others). If not, use the Arcjet MCP tools to get one: - - Call `list-teams` to find the team - - Call `list-sites` to find an existing site, or `create-site` for a new one - - Call `get-site-key` to retrieve the key - - Add the key to the appropriate env file along with `ARCJET_ENV=development` -3. Create a shared client file with `shield()` as the base rule. This file should export the Arcjet instance for reuse across routes with `withRule()`. - -**If a client already exists:** Import it. Do not create a new instance. - -## Step 3: Choose Protection Rules - -Select rules based on the route's purpose. If the user specified what they want (via `$ARGUMENTS` or in their prompt), use that. Otherwise, infer from context: - -| Route type | Recommended rules | -| ----------------------- | ------------------------------------------------------------------------------------------------------------ | -| Public API endpoint | `shield()` + `detectBot()` + `slidingWindow()` (use `fixedWindow()` only if hard per-window caps are needed) | -| Form handler / signup | `shield()` + `validateEmail()` + `slidingWindow()` | -| Authentication endpoint | `shield()` + `slidingWindow()` (strict, low limits) | -| AI / LLM endpoint | Use `/arcjet:add-ai-protection` instead — it handles the full AI stack | -| Webhook receiver | `shield()` + filter rules for allowed IPs | -| General server route | `shield()` + `detectBot()` | - -For routes that need to detect sophisticated bots (headless browsers, advanced scrapers) — especially form submissions, login/signup pages, and other abuse-prone endpoints — recommend adding Arcjet advanced signals. This is a browser-based detection system using client-side telemetry that complements server-side `detectBot()` rules. See https://docs.arcjet.com/bot-protection/advanced-signals for setup instructions. - -Apply route-specific rules using `withRule()` on the shared instance — do not modify the shared instance directly. - -## Step 4: Add Protection to the Handler - -Call `protect()` **inside** the route handler (not in middleware), only **once** per request, passing the framework's request object directly. For Next.js pages/server components: use `import { request } from "@arcjet/next"` then `const req = await request()`. - -Use this pattern: - -```typescript -const decision = await aj.protect(req); - -if (decision.isDenied()) { - if (decision.reason.isRateLimit()) { - return Response.json( - { error: "Too many requests" }, - { status: 429 }, - ); - } - if (decision.reason.isBot() || decision.reason.isShield() || decision.reason.isFilterRule()) { - return Response.json( - { error: "Forbidden" }, - { status: 403 }, - ); - } - if (decision.reason.isSensitiveInfo()) { - return Response.json( - { error: "Bad request" }, - { status: 400 }, - ); - } -} - -// Arcjet fails open — log errors but allow the request -if (decision.isErrored()) { - console.warn("Arcjet error:", decision.reason.message); -} - -// Proceed with route handler logic... -``` - -Adapt the response format to your framework (e.g., `res.status(429).json(...)` for Express, `JsonResponse` for Django). - -## Step 5: Verify - -Suggest the user start their app and hit the protected route. Remind them that new rules should start in `"DRY_RUN"` mode and be promoted to `"LIVE"` after verification. - -**Always recommend using the Arcjet MCP tools** to verify rules are working and analyze traffic: - -- `list-requests` — confirm decisions are being recorded and inspect allow/deny outcomes -- `analyze-traffic` — review traffic patterns and denial rates for the protected route -- `explain-decision` — understand why a specific request was allowed or denied -- `promote-rule` — promote rules from `DRY_RUN` to `LIVE` once verified - -If the user wants a full security review, suggest the `/arcjet:security-analyst` agent which can investigate traffic, detect anomalies, and recommend additional rules. - -The Arcjet dashboard at https://app.arcjet.com is also available for visual inspection. - -## Step 6: Install Project-Local Skills (Recommended) - -Run the Arcjet CLI to write an `ARCJET.md` skills file into the current project. Future agent turns can then discover Arcjet capabilities without fetching the docs. - -```bash -npx -y @arcjet/cli@latest skills install -``` - -Or, if `arcjet` is on `PATH`: - -```bash -arcjet skills install -``` - -The CLI uses the same authentication state as the MCP server. If the user has not yet authenticated, run `arcjet auth login` (browser-based device flow). See `rules/arcjet-cli.mdc` for guidance on when to use the CLI vs MCP. - -## Common Mistakes to Avoid - -- Creating a new Arcjet instance per request (causes connection overhead) -- Using Arcjet in Next.js middleware (fires on every request, no route context) -- Calling `protect()` multiple times in one request (double-counts rate limits) -- Hardcoding `ARCJET_KEY` instead of using environment variables -- Using `app.use()` as Express middleware instead of per-route protection +1. **Tell the user:** "`/arcjet:protect-route` is deprecated. Use `/arcjet:add-request-protection` instead — it has the same behavior plus integrated CLI setup and verification." +2. Then proceed by following the `/arcjet:add-request-protection` skill (`skills/add-request-protection/SKILL.md`) for the rest of the workflow. Do not duplicate its content here — read and follow that skill directly.