arcjet/arcjet-js

GitHub: arcjet/arcjet-js

Arcjet JS SDK 提供运行时安全平台,用于拦截机器人与自动化攻击,保护 AI 应用预算与数据。

Stars: 658 | Forks: 29

Arcjet Logo # Arcjet - JS SDK

npm badge

[Arcjet][arcjet] 是随您的 AI 代码一起提供的运行时安全平台。阻止机器人和自动化攻击消耗您的 AI 预算、泄露数据或滥用工具。 这是一个包含多个 [Arcjet][arcjet] 开源包的 monorepo,专为 JS 设计。 ## 快速开始 1. **获取 API 密钥** — [在 `app.arcjet.com` 注册](https://app.arcjet.com)。 2. **安装对应框架的 SDK:** 每个功能都可在任意 JavaScript 应用中使用。 | 框架 | 包 | 安装命令 | | ------------ | --------------------------- | ----------------------------- | | Next.js | `@arcjet/next` | `npm i @arcjet/next` | | Node.js | `@arcjet/node` | `npm i @arcjet/node` | | Bun | `@arcjet/bun` | `bun add @arcjet/bun` | | Deno | `@arcjet/deno` | `deno add npm:@arcjet/deno` | | Express | `@arcjet/node` | `npm i @arcjet/node` | | Fastify | `@arcjet/fastify` | `npm i @arcjet/fastify` | | Hono | `@arcjet/node` 或 `@arcjet/bun` | `npm i @arcjet/node` | | NestJS | `@arcjet/nest` | `npm i @arcjet/nest` | | Nuxt | `@arcjet/nuxt` | `npm i @arcjet/nuxt` | | Remix | `@arcjet/remix` | `npm i @arcjet/remix` | | React Router | `@arcjet/react-router` | `npm i @arcjet/react-router` | | SvelteKit | `@arcjet/sveltekit` | `npm i @arcjet/sveltekit` | | Astro | `@arcjet/astro` | `npm i @arcjet/astro` | 3. **设置环境变量:** ``` # .env.local(或您的框架的 env 文件) ARCJET_KEY=ajkey_yourkey ``` 4. **保护路由** — 参考 [AI 保护示例](#vercel-ai-sdk-example) 或下方的 [功能示例](#features)。 ### 获取帮助 [加入我们的 Discord 服务器][discord-invite] 或 [联系支持][support]。 - [文档](https://docs.arcjet.com) — 完整参考与指南 - [示例应用](#example-apps) — 每个框架的启动项目 - [蓝图](#blueprints) — 常见安全模式的解决方案 ## 功能特性 - 🔒 [提示注入检测](#prompt-injection-detection) — 在到达 LLM 之前检测并阻止提示注入攻击。 - 🤖 [机器人防护](#bot-protection) — 阻止爬虫、凭证填充和 AI 爬虫滥用端点。 - 🛑 [速率限制](#rate-limiting) — 令牌桶、固定窗口和滑动窗口算法;按用户控制 AI 令牌预算。 - 🕵️ [敏感信息检测](#sensitive-information-detection) — 阻止 PII、信用卡号和自定义模式进入 AI 管道。 - 🛡️ [防护盾 WAF](#shield-waf) — 防御 SQL 注入、XSS 及其他常见 Web 攻击。 - 📧 [邮箱验证](#email-validation) — 在注册时阻止一次性、非法和无法投递的地址。 - 📝 [注册表单防护][feature-signup-protection] — 结合机器人防护、邮箱验证和速率限制保护注册表单。 - 🎯 [请求过滤](#request-filters) — 基于 IP、路径、头部和自定义字段的表达式规则。 - 🌐 [IP 分析](#ip-analysis) — 包含地理定位、ASN、VPN、代理、Tor 和主机检测。 - 🧩 [Arcjet Guard](#arcjet-guard) — 用于 AI 代理工具调用和后台任务的低级 API(无 HTTP 请求)。 ## 示例应用 - [Astro][github-arcjet-example-astro] - [Deno][github-arcjet-example-deno] - [Express][github-arcjet-example-express] - [FastAPI][github-arcjet-example-fastapi] - [Fastify][github-arcjet-example-fastify] - [NestJS][github-arcjet-example-nestjs] - [Next.js][github-arcjet-example-nextjs] ([在线体验][arcjet-example]) - [Nuxt][github-arcjet-example-nuxt] - [React Router][github-arcjet-example-react-router] - [Remix][github-arcjet-example-remix] - [SvelteKit][github-arcjet-example-sveltekit] - [Tanstack Start][github-arcjet-example-tanstack-start] ## 蓝图 - [AI 配额控制][blueprint-ai-quota-control] - [Cookie 横幅][blueprint-cookie-banner] - [自定义规则][blueprint-custom-rule] - [IP 地理定位][blueprint-ip-geolocation] - [反馈表单][blueprint-feedback-form] - [恶意流量][blueprint-malicious-traffic] - [支付表单][blueprint-payment-form] - [抽样流量][blueprint-sampling-traffic] - [VPN 与代理][blueprint-vpn-proxy] ## 用法 阅读 [`docs.arcjet.com`][arcjet-docs] 的文档。 ### Vercel AI SDK 示例 此示例使用 [Vercel AI SDK]([vercel-ai-sdk]) 保护 Next.js 的 AI 聊天路由,拦截自动客户端以膨胀成本、强制执行每用户令牌预算、检测消息中的敏感信息,并在到达模型前阻止提示注入攻击。 ``` // app/api/chat/route.ts import { openai } from "@ai-sdk/openai"; import arcjet, { detectBot, detectPromptInjection, sensitiveInfo, shield, tokenBucket, } from "@arcjet/next"; import type { UIMessage } from "ai"; import { convertToModelMessages, isTextUIPart, streamText } from "ai"; const aj = arcjet({ key: process.env.ARCJET_KEY!, // Get your site key from https://app.arcjet.com // Track budgets per user — replace "userId" with any stable identifier characteristics: ["userId"], rules: [ // Shield protects against common web attacks e.g. SQL injection shield({ mode: "LIVE" }), // Block all automated clients — bots inflate AI costs detectBot({ mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only allow: [], // Block all bots. See https://arcjet.com/bot-list }), // Enforce budgets to control AI costs. Adjust rates and limits as needed. tokenBucket({ mode: "LIVE", refillRate: 2_000, // Refill 2,000 tokens per hour interval: "1h", capacity: 5_000, // Maximum 5,000 tokens in the bucket }), // Block messages containing sensitive information to prevent data leaks sensitiveInfo({ mode: "LIVE", // Block PII types that should never appear in AI prompts. // Remove types your app legitimately handles (e.g. EMAIL for a support bot). deny: ["CREDIT_CARD_NUMBER", "EMAIL"], }), // Detect prompt injection attacks before they reach your AI model detectPromptInjection({ mode: "LIVE", }), ], }); export async function POST(req: Request) { const userId = "user-123"; // Replace with your session/auth lookup const { messages }: { messages: UIMessage[] } = await req.json(); const modelMessages = await convertToModelMessages(messages); // Estimate token cost: ~1 token per 4 characters of text (rough heuristic) const totalChars = modelMessages.reduce((sum, m) => { const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content); return sum + content.length; }, 0); const estimate = Math.ceil(totalChars / 4); // Extract the most recent user message to scan for injection and PII const lastMessage: string = (messages.at(-1)?.parts ?? []) .filter(isTextUIPart) .map((p) => p.text) .join(" "); const decision = await aj.protect(req, { userId, requested: estimate, sensitiveInfoValue: lastMessage, detectPromptInjectionMessage: lastMessage, }); if (decision.isDenied()) { if (decision.reason.isBot()) { return new Response("Automated clients are not permitted", { status: 403, }); } else if (decision.reason.isRateLimit()) { return new Response("AI usage limit exceeded", { status: 429 }); } else if (decision.reason.isSensitiveInfo()) { return new Response("Sensitive information detected", { status: 400 }); } else if (decision.reason.isPromptInjection()) { return new Response( "Prompt injection detected — please rephrase your message", { status: 400 }, ); } else { return new Response("Forbidden", { status: 403 }); } } const result = await streamText({ model: openai("gpt-4o"), messages: modelMessages, }); return result.toUIMessageStreamResponse(); } ``` ### 提示注入检测 检测并阻止提示注入攻击——在到达模型之前防止覆盖 AI 模型指令的尝试。在每次调用 `protect()` 时通过 `detectPromptInjectionMessage` 传递用户消息。 ``` import arcjet, { detectPromptInjection } from "@arcjet/next"; const aj = arcjet({ key: process.env.ARCJET_KEY!, rules: [ detectPromptInjection({ mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only }), ], }); export async function POST(request: Request) { const { message } = await request.json(); const decision = await aj.protect(request, { detectPromptInjectionMessage: message, }); if (decision.isDenied() && decision.reason.isPromptInjection()) { return new Response( "Prompt injection detected — please rephrase your message", { status: 400 }, ); } // Forward to your AI model... } ``` ### 机器人防护 Arcjet 允许您配置允许或拒绝的机器人列表。指定 `allow` 表示拒绝所有其他机器人。空允许列表会阻止所有机器人。 可用类别:`CATEGORY:ACADEMIC`、`CATEGORY:ADVERTISING`、`CATEGORY:AI`、`CATEGORY:AMAZON`、`CATEGORY:APPLE`、`CATEGORY:ARCHIVE`、`CATEGORY:BOTNET`、`CATEGORY:FEEDFETCHER`、`CATEGORY:GOOGLE`、`CATEGORY:META`、`CATEGORY:MICROSOFT`、`CATEGORY:MONITOR`、`CATEGORY:OPTIMIZER`、`CATEGORY:PREVIEW`、`CATEGORY:PROGRAMMATIC`、`CATEGORY:SEARCH_ENGINE`、`CATEGORY:SLACK`、`CATEGORY:SOCIAL`、`CATEGORY:TOOL`、`CATEGORY:UNKNOWN`、`CATEGORY:VERCEL`、`CATEGORY:WEBHOOK`、`CATEGORY:YAHOO`。您也可以允许或拒绝[特定机器人名称][bot-list]。 ``` import arcjet, { detectBot } from "@arcjet/next"; import { isSpoofedBot } from "@arcjet/inspect"; const aj = arcjet({ key: process.env.ARCJET_KEY!, rules: [ detectBot({ mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only allow: [ "CATEGORY:SEARCH_ENGINE", // Google, Bing, etc // Uncomment to allow these other common bot categories: // "CATEGORY:MONITOR", // Uptime monitoring services // "CATEGORY:PREVIEW", // Link previews e.g. Slack, Discord // See the full list at https://arcjet.com/bot-list ], }), ], }); export async function GET(request: Request) { const decision = await aj.protect(request); if (decision.isDenied() && decision.reason.isBot()) { return new Response("No bots allowed", { status: 403 }); } // Arcjet verifies the authenticity of common bots using IP data. // Verification isn't always possible, so check the results separately. // https://docs.arcjet.com/bot-protection/reference#bot-verification if (decision.results.some(isSpoofedBot)) { return new Response("Forbidden", { status: 403 }); } return new Response("Hello world"); } ``` 机器人可按[类别][feature-bot-protection]和/或[特定机器人名称][bot-list]进行配置。例如,允许搜索引擎和 OpenAI 爬虫,但拒绝所有其他机器人: ``` detectBot({ mode: "LIVE", allow: ["CATEGORY:SEARCH_ENGINE", "OPENAI_CRAWLER_SEARCH"], }); ``` ### 速率限制 Arcjet 支持令牌桶、固定窗口和滑动窗口算法。 令牌桶非常适合控制 AI 令牌预算——设置 `capacity` 为用户可花费的最大令牌数,`refillRate` 为每 `interval` 恢复的令牌数,并通过 `protect()` 中的 `requested` 扣除令牌。 `interval` 接受字符串(`"1s"`、`"1m"`、`"1h"`、`"1d"`)或秒数。使用 `characteristics` 以用户为单位而非 IP 地址跟踪限制。 ``` import arcjet, { tokenBucket } from "@arcjet/next"; const aj = arcjet({ key: process.env.ARCJET_KEY!, characteristics: ["userId"], // Track per user rules: [ tokenBucket({ mode: "LIVE", refillRate: 2_000, // Refill 2,000 tokens per hour interval: "1h", capacity: 5_000, // Maximum 5,000 tokens in the bucket }), ], }); const decision = await aj.protect(request, { userId: "user-123", requested: estimate, // Number of tokens to deduct }); if (decision.isDenied() && decision.reason.isRateLimit()) { return new Response("AI usage limit exceeded", { status: 429 }); } ``` ### 敏感信息检测 检测并请求内容中的 PII。通过 `sensitiveInfoValue` 在每次调用 `protect()` 时传递要扫描的内容。内置实体类型:`CREDIT_CARD_NUMBER`、`EMAIL`、`PHONE_NUMBER`、`IP_ADDRESS`。您也可以提供自定义 `detect` 回调以匹配其他模式。 ``` import arcjet, { sensitiveInfo } from "@arcjet/next"; const aj = arcjet({ key: process.env.ARCJET_KEY!, rules: [ sensitiveInfo({ mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only deny: ["CREDIT_CARD_NUMBER", "EMAIL", "PHONE_NUMBER"], }), ], }); const decision = await aj.protect(request, { sensitiveInfoValue: userMessage, }); if (decision.isDenied() && decision.reason.isSensitiveInfo()) { return new Response("Sensitive information detected", { status: 400 }); } ``` ### 防护盾 WAF 保护您的应用程序免受常见 Web 攻击,包括 OWASP Top 10。 ``` import arcjet, { shield } from "@arcjet/next"; const aj = arcjet({ key: process.env.ARCJET_KEY!, rules: [ shield({ mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only }), ], }); ``` ### 邮箱验证 验证并核实电子邮件地址。拒绝类型:`DISPOSABLE`、`FREE`、`NO_MX_RECORDS`、`NO_GRAVATAR`、`INVALID`。 ``` import arcjet, { validateEmail } from "@arcjet/next"; const aj = arcjet({ key: process.env.ARCJET_KEY!, rules: [ validateEmail({ mode: "LIVE", deny: ["DISPOSABLE", "INVALID", "NO_MX_RECORDS"], }), ], }); const decision = await aj.protect(request, { email: "user@example.com", }); if (decision.isDenied() && decision.reason.isEmail()) { return new Response("Invalid email address", { status: 400 }); } ``` ### 请求过滤 使用基于表达式的规则过滤请求属性(IP、头部、路径、方法等)。 #### 按国家/地区阻止 限制对特定国家/地区的访问——适用于许可、合规或区域发布。`allow` 列表会拒绝未列出的所有国家/地区: ``` filter({ mode: "LIVE", // Allow only US traffic — all other countries are denied allow: ['ip.src.country == "US"'], }); ``` #### 阻止 VPN 和代理流量 阻止匿名流量访问敏感端点——适用于欺诈预防、强制执行地理限制和减少滥用: ``` filter({ mode: "LIVE", deny: [ "ip.src.vpn", // VPN services "ip.src.proxy", // Open proxies "ip.src.tor", // Tor exit nodes ], }); ``` 对于更细粒度的处理,在调用 `protect()` 后使用 `decision.ip` 辅助函数: ``` const decision = await aj.protect(request); if (decision.ip.isVpn() || decision.ip.isTor()) { return new Response("VPN traffic not allowed", { status: 403 }); } ``` 请参考[请求过滤文档][feature-filters]、[IP 地理定位蓝图][blueprint-ip-geolocation] 和 [VPN/代理检测蓝图][blueprint-vpn-proxy] 获取更多细节。 ### IP 分析 Arcjet 为每个请求丰富 IP 元数据。使用这些辅助函数根据网络信号制定策略决策: ``` const decision = await aj.protect(request); if (decision.ip.isHosting()) { // Requests from cloud/hosting providers are often automated. // https://docs.arcjet.com/blueprints/vpn-proxy-detection return new Response("Forbidden", { status: 403 }); } if (decision.ip.isVpn() || decision.ip.isProxy() || decision.ip.isTor()) { // Handle VPN/proxy traffic according to your policy } // Access geolocation and network details console.log(decision.ip.country, decision.ip.city, decision.ip.asn); ``` ### 自定义特征 使用任意稳定标识符(用户 ID、API 密钥、会话等)而非仅 IP 地址来跟踪和限制请求。 ``` const aj = arcjet({ key: process.env.ARCJET_KEY!, characteristics: ["userId"], // Declare at the SDK level rules: [ tokenBucket({ mode: "LIVE", refillRate: 2_000, interval: "1h", capacity: 5_000, }), ], }); // Pass the characteristic value at request time const decision = await aj.protect(request, { userId: "user-123", requested: estimate, }); ``` ## Arcjet Guard ### 与框架 SDK 的区别 | 维度 | 框架 SDK(`@arcjet/next` 等) | `@arcjet/guard` | |------|------------------------------|-----------------| | **设计用途** | HTTP 请求保护 | AI 代理工具调用、后台任务 | | **请求对象** | 必需(`protect(request, ...)`) | 不需要 | | **规则绑定** | 规则配置一次,通过 `protect()` 选项输入 | 规则配置为函数,每次调用时传入输入 | | **速率限制键** | IP 或 `characteristics` 字典 | 显式 `key` 字符串(SHA-256 哈希后发送) | | **自定义规则** | 不支持 | `defineCustomRule`,支持类型化的配置/输入/数据 | ### 安装 ``` npm i @arcjet/guard ``` ### 快速开始 ``` import { launchArcjet, tokenBucket, detectPromptInjection, } from "@arcjet/guard"; // Create the Arcjet client once at module scope const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! }); // Configure reusable rules const limitRule = tokenBucket({ refillRate: 10, intervalSeconds: 60, maxTokens: 100, }); const piRule = detectPromptInjection(); // Per request — create rule inputs each time async function handleToolCall( userId: string, userMessage: string, tokenCount: number, ) { const rl = limitRule({ key: userId, requested: tokenCount }); const decision = await arcjet.guard({ label: "tools.weather", rules: [rl, piRule(userMessage)], }); if (decision.conclusion === "DENY") { throw new Error(`Blocked: ${decision.reason}`); } // safe to proceed } ``` ### 速率限制(Guard) 令牌桶、固定窗口和滑动窗口算法均可用。 配置规则一次,然后针对每次调用传入 `key`(以及可选的 `requested` 令牌数)。 #### 令牌桶(Guard) ``` import { launchArcjet, tokenBucket } from "@arcjet/guard"; const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! }); const limitRule = tokenBucket({ refillRate: 2_000, // tokens added per interval intervalSeconds: 3600, // seconds between refills maxTokens: 5_000, // maximum bucket capacity }); const decision = await arcjet.guard({ label: "tools.chat", rules: [limitRule({ key: userId, requested: tokenEstimate })], }); if (decision.conclusion === "DENY" && decision.reason === "RATE_LIMIT") { throw new Error("Rate limit exceeded"); } ``` #### 固定窗口(Guard) ``` import { launchArcjet, fixedWindow } from "@arcjet/guard"; const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! }); const limitRule = fixedWindow({ maxRequests: 1000, // maximum requests per window windowSeconds: 3600, // 1-hour window }); const decision = await arcjet.guard({ label: "api.search", rules: [limitRule({ key: teamId })], }); ``` #### 滑动窗口(Guard) ``` import { launchArcjet, slidingWindow } from "@arcjet/guard"; const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! }); const limitRule = slidingWindow({ maxRequests: 500, // maximum requests per interval intervalSeconds: 60, // 1-minute rolling window }); const decision = await arcjet.guard({ label: "api.events", rules: [limitRule({ key: userId })], }); ``` ### 提示注入检测(Guard) ``` import { launchArcjet, detectPromptInjection } from "@arcjet/guard"; const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! }); const piRule = detectPromptInjection(); const decision = await arcjet.guard({ label: "tools.chat", rules: [piRule(userMessage)], }); if (decision.conclusion === "DENY" && decision.reason === "PROMPT_INJECTION") { throw new Error("Prompt injection detected — please rephrase your message"); } ``` ### 敏感信息检测(Guard) 在本地检测 PII —— 原始文本不会离开 SDK。内置实体类型:`EMAIL`、`PHONE_NUMBER`、`IP_ADDRESS`、`CREDIT_CARD_NUMBER`。 ``` import { launchArcjet, localDetectSensitiveInfo } from "@arcjet/guard"; const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! }); const si = localDetectSensitiveInfo({ deny: ["CREDIT_CARD_NUMBER", "PHONE_NUMBER"], }); const decision = await arcjet.guard({ label: "tools.summary", rules: [si(userMessage)], }); if (decision.conclusion === "DENY" && decision.reason === "SENSITIVE_INFO") { throw new Error("Sensitive information detected"); } ``` ### 自定义规则(Guard) 使用任意键值数据定义本地评估逻辑。当提供 `evaluate` 时,SDK 在本地调用它,然后再发送请求。 函数接收 `(config, input, { signal })` 并必须返回 `{ conclusion: "ALLOW" | "DENY" }`。 ``` import { launchArcjet, defineCustomRule } from "@arcjet/guard"; const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! }); const topicBlock = defineCustomRule< { blockedTopic: string }, { topic: string }, { matched: string } >({ evaluate: (config, input) => { if (input.topic === config.blockedTopic) { return { conclusion: "DENY", data: { matched: input.topic } }; } return { conclusion: "ALLOW" }; }, }); const rule = topicBlock({ data: { blockedTopic: "politics" } }); const decision = await arcjet.guard({ label: "tools.chat", rules: [rule({ data: { topic: userTopic } })], }); ``` ### 每个规则的结果(Guard) 对已配置的规则和绑定的输入均可访问类型化的结果访问器: ``` const limitRule = tokenBucket({ refillRate: 10, intervalSeconds: 60, maxTokens: 100, }); const rl = limitRule({ key: userId, requested: 5 }); const decision = await arcjet.guard({ label: "tools.weather", rules: [rl] }); // From the bound input (matches exact invocation) const r = rl.result(decision); if (r) { console.log(r.remainingTokens, r.maxTokens); } // From the configured rule (matches all invocations of this rule) const ruleResult = limitRule.result(decision); // Check only denied results const denied = rl.deniedResult(decision); if (denied) { console.log(`Rate limited — resets at ${denied.resetAtUnixSeconds}`); } ``` `RuleWithConfig` 和 `RuleWithInput` 上的方法: | 方法 | `RuleWithConfig`(例如 `limitRule`) | `RuleWithInput`(例如 `rl`) | |------|-------------------------------------|------------------------------| | `results(decision)` | 该配置的所有结果 | 单元素或空数组 | | `result(decision)` | 第一个结果(任意结论) | 本次提交的结果 | | `deniedResult(decision)` | 第一个被拒绝的结果 | 被拒绝时的结果 | ### 决策 API(Guard) ``` const decision = await arcjet.guard({ label: "tools.weather", rules: [...] }); // Layer 1: conclusion and reason decision.conclusion; // "ALLOW" or "DENY" decision.reason; // "RATE_LIMIT", "PROMPT_INJECTION", "SENSITIVE_INFO", "CUSTOM", "ERROR", etc. // Layer 2: error detection decision.hasError(); // true if any rule errored or the server reported diagnostics // Layer 3: per-rule results (see "Per-rule results" above) for (const result of decision.results) { console.log(result.type, result.conclusion); } ``` ### `guard()` 参数参考 | 参数 | 类型 | 描述 | |------|------|------| | `rules` | `RuleWithInput[]` | 绑定的规则输入(必需) | | `label` | `string` | 标识本次 Guard 调用的标签(必需) | | `metadata` | `Record \| undefined` | 可选键值元数据 | ### DRY_RUN 模式 所有 Guard 规则均接受 `mode` 参数。使用 `"DRY_RUN"` 可在不阻断的情况下评估规则: ``` const limitRule = tokenBucket({ mode: "DRY_RUN", refillRate: 10, intervalSeconds: 60, maxTokens: 100, }); ``` ## 最佳实践 请参考[Arcjet 最佳实践][best-practices]获取详细说明。关键建议: **创建单个客户端实例** 并在整个应用中使用 `withRule()` 附加路由特定规则。SDK 会缓存决策和配置,因此为每个请求创建新实例会浪费计算资源。 ``` // lib/arcjet.ts — create once, import everywhere import arcjet, { shield } from "@arcjet/next"; // Replace @arcjet/next with @arcjet/node, @arcjet/bun, etc. for your runtime export default arcjet({ key: process.env.ARCJET_KEY!, rules: [ shield({ mode: "LIVE" }), // base rules applied to every request ], }); ``` ``` // app/api/chat/route.ts — extend per-route with withRule() import aj from "@/lib/arcjet"; import { detectBot, tokenBucket } from "@arcjet/next"; const routeAj = aj.withRule(detectBot({ mode: "LIVE", allow: [] })).withRule( tokenBucket({ mode: "LIVE", refillRate: 2_000, interval: "1h", capacity: 5_000, }), ); export async function POST(req: Request) { const decision = await routeAj.protect(req, { requested: 500 }); // ... } ``` **其他建议:** - **在路由处理程序中调用 `protect()`,而非中间件。** 中间件缺乏路由上下文,难以应用路由特定规则或自定义响应。 - **对每个请求只调用 `protect()` 一次。** 在中间件和处理程序中都调用会加倍计算并可能导致意外结果。 - **以 `DRY_RUN` 模式启动规则**,以便在切换至 `LIVE` 模式前观察行为。这允许你在不影响真实流量的情况下调整阈值。 - **如果应用运行在负载均衡器或反向代理后方,请配置代理**,使 Arcjet 能解析真实的客户端 IP: arcjet({ key: process.env.ARCJET_KEY!, rules: [], proxies: ["100.100.100.100"], }); - **显式处理错误。** `protect()` 永不抛出错误;出错时返回 `ERROR` 结果。故障开放策略:记录并允许请求继续: if (decision.isErrored()) { console.error("Arcjet error", decision.reason.message); // 允许请求继续 } ## 软件包 我们在此仓库中提供多个软件包的源代码,可通过以下分类和描述查找特定软件包。 ### SDK - [`@arcjet/astro`](./arcjet-astro/README.md): Astro 的 SDK。 - [`@arcjet/bun`](./arcjet-bun/README.md): Bun 的 SDK。 - [`@arcjet/deno`](./arcjet-deno/README.md): Deno 的 SDK。 - [`@arcjet/fastify`](./arcjet-fastify/README.md): Fastify 的 SDK。 - [`@arcjet/guard`](./arcjet-guard/README.md): 用于 AI 代理工具调用和后台任务的 Guard SDK。 - [`@arcjet/nest`](./arcjet-nest/README.md): NestJS 的 SDK。 - [`@arcjet/next`](./arcjet-next/README.md): Next.js 的 SDK。 - [`@arcjet/node`](./arcjet-node/README.md): Node.js 的 SDK。 - [`@arcjet/nuxt`](./arcjet-nuxt/README.md): Nuxt 的 SDK。 - [`@arcjet/react-router`](./arcjet-react-router/README.md): React Router 的 SDK。 - [`@arcjet/remix`](./arcjet-remix/README.md): Remix 的 SDK。 - [`@arcjet/sveltekit`](./arcjet-sveltekit/README.md): SvelteKit 的 SDK。 ### Nosecone 详见[文档][nosecone-docs]。 - [`@nosecone/next`](./nosecone-next/README.md): 使用安全标头保护 Next.js 应用。 - [`@nosecone/sveltekit`](./nosecone-sveltekit/README.md): 使用标头保护 SvelteKit 应用。 - [`nosecone`](./nosecone/README.md): 使用安全标头保护 `Response`。 ### 工具 - [`@arcjet/analyze`](./analyze/README.md): 本地分析引擎。 - [`@arcjet/body`](./body/README.md): 从流中提取请求体。 - [`@arcjet/cache`](./cache/README.md): 基础缓存接口及实现。 - [`@arcjet/decorate`](./decorate/README.md): 为响应添加信息。 - [`@arcjet/duration`](./duration/README.md): 解析持续时间字符串。 - [`@arcjet/env`](./env/README.md): 环境检测。 - [`@arcjet/headers`](./headers/README.md): Headers 类的扩展。 - [`@arcjet/inspect`](./inspect/README.md): 检查 SDK 做出的决策。 - [`@arcjet/ip`](./ip/README.md): 查找请求的源 IP。 - [`@arcjet/logger`](./logger/README.md): 模拟 Pino 结构化日志接口的轻量级日志记录器。 - [`@arcjet/protocol`](./protocol/README.md): Arcjet 协议到 JS 的接口。 - [`@arcjet/redact`](./redact/README.md): 对字符串脱敏和去脱敏敏感信息。 - [`@arcjet/runtime`](./runtime/README.md): 运行时检测。 - [`@arcjet/sprintf`](./sprintf/README.md): 与平台无关的 `util.format` 替代实现。 - [`@arcjet/stable-hash`](./stable-hash/README.md): 稳定哈希。 - [`@arcjet/transport`](./transport/README.md): Arcjet 协议的数据传输机制。 - [`arcjet`](./arcjet/README.md): JS SDK 核心。 ### 内部开发 - [`@arcjet/eslint-config`](./eslint-config/README.md): 我们项目的自定义 ESLint 配置。 - [`@arcjet/rollup-config`](./rollup-config/README.md): 我们项目的自定义 Rollup 配置。 ## 支持 本仓库遵循 [Arcjet 支持策略][arcjet-support]。 ## 安全性 本仓库遵循 [Arcjet 安全策略][arcjet-security]。 ## 开发 这是一个由 [npm workspaces](https://npm.workspaces) 和 [Turborepo](https.turborepo.org) 管理的 monorepo。每个软件包都位于其自己的目录中(例如 `arcjet-next/`、`analyze/`)。 如果您想使用 Arcjet,则应安装适用于特定运行时的软件包(例如 `@arcjet/next` 适用于 Next.js)。如果您想为 SDK 开发做出贡献,请参阅 [CONTRIBUTING.md](./CONTRIBUTING.md)。 ## 兼容性 本仓库维护的软件包与 LTS 版本的 Node.js 和 TypeScript 的当前次要版本兼容。 ## 许可证 根据 [Apache License, Version 2.0][apache-license] 授权。
标签:AI安全, AI预算保护, Bun, Chat Copilot, CMS安全, Deno, Express, Fastify, GNU通用公共许可证, Hono, JavaScript, LLM应用, MITM代理, NestJS, Node.js, npm包, SEO, TypeScript, 前端安全, 后端安全, 威胁防护, 安全插件, 工具误用防护, 开源, 攻击防护, 机器人防护, 流量清洗, 自动化攻击, 自动化攻击防护, 零日漏洞检测