arcjet

package module
v0.0.0-...-2b6c4ac Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 2, 2026 License: Apache-2.0 Imports: 31 Imported by: 0

README

Arcjet Logo

Arcjet - Go SDK

[!IMPORTANT] The Go SDK is pre-release and unstable.

Arcjet is the runtime security platform that ships with your AI code. Stop bots and automated attacks from burning your AI budget, leaking data, or misusing tools with Arcjet's AI security building blocks.

This is the Go SDK for Arcjet — use arcjet.NewClient for request protection in net/http handlers (and any router that exposes *http.Request) and arcjet.NewGuardClient for guard protection (AI agent tool calls, MCP servers, background jobs, queue workers).

Getting started

Install the Arcjet CLI

The CLI is used to log in, manage site keys, and install protection skills.

Homebrew (macOS and Linux):

brew install arcjet/tap/arcjet

npx (Node.js) — run any command without installing:

npx @arcjet/cli <command>

Or download a binary for macOS (Apple Silicon, Intel), Linux (x86_64, arm64), and Windows (x86_64, arm64).

Examples below use the arcjet binary. If you installed via npx, replace arcjet with npx @arcjet/cli.

Quick setup with an AI agent
  1. Log in with the CLI:
    arcjet auth login
    
  2. Install the protection skill:
    npx skills add arcjet/skills
    
  3. Tell your agent what to protect — it handles the rest.
Manual setup
  1. Log in with the CLI (or at app.arcjet.com):
    arcjet auth login
    
  2. go get github.com/arcjet/arcjet-go
  3. Get your site key:
    arcjet sites get-key
    
    Or copy it from the Arcjet dashboard.
  4. Set ARCJET_KEY=ajkey_yourkey in your environment.
  5. Protect a handler — see the AI protection example or individual feature examples below.
Get help

Join our Discord server or reach out for support.

Quick start

Note: Create the client once at package scope and reuse it across handlers. For larger projects, move it into its own package (e.g. internal/security/arcjet.go) so handlers can import a single shared instance.

Protect an AI chat endpoint with prompt injection detection, token budget rate limiting, and bot protection:

// main.go
package main

import (
	"encoding/json"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/arcjet/arcjet-go"
)

var arcjetKey = func() string {
	key := os.Getenv("ARCJET_KEY")
	if key == "" {
		log.Fatal("ARCJET_KEY is required. Get one with: arcjet sites get-key" +
			" or from https://app.arcjet.com")
	}
	return key
}()

// Create a single Arcjet client and reuse it across requests.
var aj = must(arcjet.NewClient(arcjet.Config{
	Key: arcjetKey,
	Rules: []arcjet.Rule{
		// Detect and block prompt injection attacks in user messages.
		arcjet.DetectPromptInjection(arcjet.PromptInjectionOptions{
			Mode: arcjet.ModeLive,
		}),
		// Rate limit by token budget — refill 100 tokens every 60 seconds.
		arcjet.TokenBucket(arcjet.TokenBucketOptions{
			Mode:            arcjet.ModeLive,
			Characteristics: []string{"userId"},
			RefillRate:      100,
			Interval:        time.Minute,
			Capacity:        1000,
		}),
		// Block automated clients and scrapers from your AI endpoints.
		arcjet.DetectBot(arcjet.BotOptions{
			Mode:  arcjet.ModeLive,
			Allow: []string{}, // empty = block all bots
		}),
		// Protect against common web attacks (SQLi, XSS, etc.).
		arcjet.Shield(arcjet.ShieldOptions{Mode: arcjet.ModeLive}),
	},
}))

type chatRequest struct {
	Message string `json:"message"`
}

func chat(w http.ResponseWriter, r *http.Request) {
	var body chatRequest
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		http.Error(w, "bad request", http.StatusBadRequest)
		return
	}

	userID := "user_123" // replace with real user ID from session

	decision, err := aj.Protect(
		r.Context(),
		r,
		arcjet.WithRequested(5), // tokens consumed per request
		arcjet.WithCharacteristics(map[string]string{"userId": userID}),
		arcjet.WithDetectPromptInjectionMessage(body.Message), // scan for prompt injection
	)
	if err != nil {
		// Arcjet fails open — log and continue serving.
		log.Printf("arcjet: %v", err)
	} else if decision.IsDenied() {
		status := http.StatusForbidden
		if decision.Reason.IsRateLimit() {
			status = http.StatusTooManyRequests
		}
		http.Error(w, "denied", status)
		return
	}

	// Safe to pass body.Message to your LLM.
	_ = json.NewEncoder(w).Encode(map[string]string{"reply": "..."})
}

func main() {
	http.HandleFunc("/chat", chat)
	log.Fatal(http.ListenAndServe(":3000", nil))
}

func must[T any](v T, err error) T {
	if err != nil {
		log.Fatal(err)
	}
	return v
}

Pass r.Context() to Protect (as the example does) so the call honors client disconnects.

Call aj.Protect inside each handler — once per request. Avoid wrapping it in generic net/http middleware that runs on every path (including static assets); you lose the ability to apply per-route rules and risk double-counting traffic.

Features

Feature Request (NewClient) Guard (NewGuardClient)
Rate Limiting
Prompt Injection Detection
Sensitive Information Detection
Bot Protection
Shield WAF
Email Validation
Request Filters
IP Analysis
Custom Rules
  • 🔒 Prompt Injection Detection — detect and block prompt injection attacks before they reach your LLM.
  • 🤖 Bot Protection — stop scrapers, credential stuffers, and AI crawlers from abusing your endpoints.
  • 🛑 Rate Limiting — token bucket, fixed window, and sliding window algorithms; model AI token budgets per user.
  • 🛡️ Shield WAF — protect against SQL injection, XSS, and other common web attacks.
  • 📧 Email Validation — block disposable, invalid, and undeliverable addresses at signup.
  • 🎯 Request Filters — expression-based rules on IP, path, headers, and custom fields.
  • 🌐 IP Analysis — geolocation, ASN, VPN, proxy, Tor, and hosting detection included with every request.
  • 🧩 Arcjet Guard — lower-level API for AI agent tool calls and background tasks where there is no HTTP request.
Which features do I need?
If your app has... Recommended features
LLM / AI chat endpoints Prompt injection + token bucket rate limit + bot protection + shield
AI agent tool calls Arcjet Guard — rate limiting + prompt injection + custom rules
MCP servers Arcjet Guard — tool calls run over stdio/SSE, not HTTP, so use guard rules at each tool call site
Background jobs/workers Arcjet Guard — no HTTP request at the protection site
Public API Rate limiting + bot protection + shield
Signup / login forms Email validation + bot protection + rate limiting (or signup protection)
Internal / admin routes Shield + request filters (country, VPN/proxy blocking)
Any web application Shield + bot protection (good baseline for all apps)

All features can be combined in a single Arcjet client. Rules are evaluated together — if any rule denies the request, decision.IsDenied() returns true. Use arcjet.ModeDryRun on individual rules to test them before enforcing.

Installation

go get github.com/arcjet/arcjet-go

The SDK requires Go 1.25 or later.

Prompt injection detection

Detect and block prompt injection attacks — attempts by users to hijack your LLM's behavior through crafted input — before they reach your model.

aj, err := arcjet.NewClient(arcjet.Config{
	Key: arcjetKey,
	Rules: []arcjet.Rule{
		arcjet.DetectPromptInjection(arcjet.PromptInjectionOptions{
			Mode: arcjet.ModeLive,
		}),
	},
})
if err != nil {
	return err
}

decision, err := aj.Protect(
	r.Context(),
	r,
	arcjet.WithDetectPromptInjectionMessage(body.Message),
)
if err != nil {
	// Fails open — log and continue.
	return err
}

if decision.IsDenied() {
	http.Error(w, "Prompt injection detected", http.StatusBadRequest)
	return
}

// Safe to pass body.Message to your LLM.

See the Prompt Injection docs for more details.

Bot protection

Manage traffic from automated clients. Block scrapers, credential stuffers, and AI crawlers, while allowing legitimate bots like search engines and monitors.

aj, err := arcjet.NewClient(arcjet.Config{
	Key: arcjetKey,
	Rules: []arcjet.Rule{
		arcjet.DetectBot(arcjet.BotOptions{
			Mode: arcjet.ModeLive,
			Allow: []string{
				arcjet.BotCategorySearchEngine, // Google, Bing, etc.
				// arcjet.BotCategoryMonitor,    // Uptime monitoring
				// arcjet.BotCategoryPreview,    // Link previews (Slack, Discord)
				// "OPENAI_CRAWLER_SEARCH",      // Allow a specific bot by name
			},
		}),
	},
})
if err != nil {
	return err
}

decision, err := aj.Protect(r.Context(), r)
if err != nil {
	return err
}

if decision.IsDenied() {
	http.Error(w, "Bot detected", http.StatusForbidden)
	return
}

if decision.IsSpoofedBot() {
	http.Error(w, "Spoofed bot", http.StatusForbidden)
	return
}
Bot categories

Configure rules using categories or specific bot identifiers:

arcjet.DetectBot(arcjet.BotOptions{
	Mode: arcjet.ModeLive,
	Allow: []string{
		arcjet.BotCategorySearchEngine,
		"OPENAI_CRAWLER_SEARCH",
	},
})

Exported constants cover all built-in categories: arcjet.BotCategoryAcademic, BotCategoryAdvertising, BotCategoryAI, BotCategoryAmazon, BotCategoryArchive, BotCategoryBotnet, BotCategoryFeedFetcher, BotCategoryGoogle, BotCategoryMeta, BotCategoryMicrosoft, BotCategoryMonitor, BotCategoryOptimizer, BotCategoryPreview, BotCategoryProgrammatic, BotCategorySearchEngine, BotCategorySlack, BotCategorySocial, BotCategoryTool, BotCategoryUnknown, BotCategoryVercel, BotCategoryYahoo. Plain strings still work (e.g. "CATEGORY:AI" or "OPENAI_CRAWLER_SEARCH" for specific bots by name) — the constants exist for autocomplete and to catch typos at compile time.

If you specify an allow list, all other bots are denied. An empty allow list blocks all bots. The reverse applies for deny lists.

Verified vs. spoofed bots

Bots claiming to be well-known crawlers (e.g. Googlebot) are verified against their known IP ranges. Use decision.IsSpoofedBot() to check:

if decision.IsSpoofedBot() {
	http.Error(w, "Spoofed bot", http.StatusForbidden)
	return
}

decision.IsVerifiedBot() reports the opposite — a crawler whose IP matched its published ranges — which you may want to allow even when other signals would deny. decision.IsMissingUserAgent() reports a request a bot rule denied for having no User-Agent header, a common sign of an automated client.

See the Bot Protection docs for more details.

Rate limiting

Limit request rates per IP, user, or any custom characteristic. Arcjet supports token bucket, fixed window, and sliding window algorithms. Token buckets are ideal for controlling AI token budgets — set Capacity to the max tokens a user can spend, RefillRate to how many tokens are restored per Interval, and deduct tokens per request via arcjet.WithRequested(n). The Interval accepts a time.Duration. Use Characteristics to track limits per user instead of per IP.

Rate limits track by IP address by default. To track per user, declare the key name in Characteristics on the rule, then pass the actual value via arcjet.WithCharacteristics at call time:

aj, err := arcjet.NewClient(arcjet.Config{
	Key: arcjetKey,
	Rules: []arcjet.Rule{
		arcjet.TokenBucket(arcjet.TokenBucketOptions{
			Mode:            arcjet.ModeLive,
			Characteristics: []string{"userId"}, // or omit for IP-based
			RefillRate:      100,                // tokens added per interval
			Interval:        time.Minute,        // interval duration
			Capacity:        1000,               // maximum tokens per bucket
		}),
	},
})

decision, err := aj.Protect(
	r.Context(),
	r,
	arcjet.WithRequested(5), // tokens consumed by this request
	arcjet.WithCharacteristics(map[string]string{"userId": "user_123"}),
)

if decision.IsDenied() {
	http.Error(w, "Rate limited", http.StatusTooManyRequests)
	return
}

Put Characteristics on the specific rate-limit rule that needs it, not on the global client — that way different rules can key by different things.

Fixed window
arcjet.FixedWindow(arcjet.FixedWindowOptions{
	Mode:        arcjet.ModeLive,
	Window:      time.Minute,
	MaxRequests: 100,
})
Sliding window
arcjet.SlidingWindow(arcjet.SlidingWindowOptions{
	Mode:        arcjet.ModeLive,
	Interval:    time.Minute,
	MaxRequests: 100,
})

See the Rate Limiting docs for more details.

Rate limit response headers

Call arcjet.SetRateLimitHeaders(w, decision) to advertise the limit to clients using the IETF RateLimit header fields for HTTP (RateLimit and RateLimit-Policy). It is a no-op when the decision carries no rate limit, so you can call it unconditionally:

decision, _ := aj.Protect(r.Context(), r)
arcjet.SetRateLimitHeaders(w, decision)
if decision.IsDenied() {
	http.Error(w, "Rate limited", http.StatusTooManyRequests)
	return
}

Protecting a signup form

arcjet.ProtectSignup bundles the rules commonly used on a signup form — a sliding-window rate limit, bot detection, and email validation — into one slice you can hand to Config.Rules:

aj, err := arcjet.NewClient(arcjet.Config{
	Key: arcjetKey,
	Rules: arcjet.ProtectSignup(arcjet.ProtectSignupOptions{
		RateLimit: arcjet.SlidingWindowOptions{Mode: arcjet.ModeLive, Interval: time.Hour, MaxRequests: 5},
		Bots:      arcjet.BotOptions{Mode: arcjet.ModeLive, Allow: nil}, // block all bots
		Email:     arcjet.EmailOptions{Mode: arcjet.ModeLive, Deny: []arcjet.EmailType{arcjet.EmailTypeDisposable, arcjet.EmailTypeInvalid, arcjet.EmailTypeNoMXRecords}},
	}),
})

The returned rules can be combined with others using append.

Sensitive information detection

Detect and block personally identifiable information — emails, phone numbers, IP addresses, and credit card numbers — in text you pass to Protect. Detection runs locally via the bundled WebAssembly analyzer (the same arcjet_analyze_js_req component used by the JavaScript and Python SDKs), so the scanned text never leaves the SDK. Pass the text to scan with arcjet.WithSensitiveInfoValue:

aj, err := arcjet.NewClient(arcjet.Config{
	Key: arcjetKey,
	Rules: []arcjet.Rule{
		arcjet.SensitiveInfo(arcjet.SensitiveInfoOptions{
			Mode: arcjet.ModeLive,
			Deny: []arcjet.EntityType{
				arcjet.SensitiveInfoEmail,
				arcjet.SensitiveInfoCreditCardNumber,
			},
		}),
	},
})

decision, err := aj.Protect(
	r.Context(),
	r,
	arcjet.WithSensitiveInfoValue("User input to scan"),
)
if decision.Reason.IsSensitiveInfo() {
	http.Error(w, "Sensitive information detected", http.StatusBadRequest)
	return
}

Allow and Deny are mutually exclusive: Deny blocks the listed entity types, while Allow blocks every type except those listed. The built-in entity types are SensitiveInfoEmail, SensitiveInfoPhoneNumber, SensitiveInfoIPAddress, and SensitiveInfoCreditCardNumber.

To detect custom entities, set Config.SensitiveInfoDetect. It receives the tokenized text and returns one EntityType per token (empty leaves a token unclassified); the returned label can be any custom string you then list in Allow/Deny:

aj, err := arcjet.NewClient(arcjet.Config{
	Key: arcjetKey,
	SensitiveInfoDetect: func(ctx context.Context, tokens []string) []arcjet.EntityType {
		out := make([]arcjet.EntityType, len(tokens))
		for i, tok := range tokens {
			if strings.HasPrefix(tok, "sk-") {
				out[i] = "API_KEY"
			}
		}
		return out
	},
	Rules: []arcjet.Rule{
		arcjet.SensitiveInfo(arcjet.SensitiveInfoOptions{
			Mode: arcjet.ModeLive,
			Deny: []arcjet.EntityType{"API_KEY"},
		}),
	},
})

To redact rather than block sensitive information, see Redacting sensitive information below.

See the Sensitive Information docs for more details.

Redacting sensitive information

The github.com/arcjet/arcjet-go/redact package detects and redacts sensitive information — emails, phone numbers, IP addresses, credit card numbers, and custom entities — entirely in-process. The text is never sent to Arcjet, which makes it suitable for scrubbing prompts before they reach a third-party LLM and restoring the values in the response. It runs the same WebAssembly component as @arcjet/redact and arcjet.redact, so all three SDKs redact identically.

import "github.com/arcjet/arcjet-go/redact"

r, err := redact.New(ctx, redact.Options{})
if err != nil {
	log.Fatal(err)
}
defer r.Close(ctx)

redacted, unredact, err := r.Redact(ctx, "Contact me at test@example.com")
// redacted == "Contact me at <Redacted email #0>"

// ...send `redacted` to the model, then restore the originals in the reply:
answer := unredact(modelResponse)

redact.New compiles the component once; reuse the Redactor across calls. Use Options.Entities to limit which entity types are redacted, and Options.Detect / Options.Replace to plug in custom detection and replacement logic. See the Redaction docs for more details.

Shield WAF

Protect against common web attacks including SQL injection, XSS, path traversal, and other OWASP Top 10 threats. No additional configuration needed — Shield analyzes request patterns automatically.

arcjet.Shield(arcjet.ShieldOptions{Mode: arcjet.ModeLive})

Always include Shield on the shared client as a base rule — it costs nothing to add and protects every route.

See the Shield docs for more details.

Email validation

Prevent users from signing up with disposable, invalid, or undeliverable email addresses. Deny types: DISPOSABLE, FREE, INVALID, NO_MX_RECORDS, NO_GRAVATAR.

aj, err := arcjet.NewClient(arcjet.Config{
	Key: arcjetKey,
	Rules: []arcjet.Rule{
		arcjet.ValidateEmail(arcjet.EmailOptions{
			Mode: arcjet.ModeLive,
			Deny: []arcjet.EmailType{
				arcjet.EmailTypeDisposable,
				arcjet.EmailTypeInvalid,
				arcjet.EmailTypeNoMXRecords,
			},
		}),
	},
})

// Pass the email with each Protect call.
decision, err := aj.Protect(
	r.Context(),
	r,
	arcjet.WithEmail("user@example.com"),
)

See the Email Validation docs for more details.

Request filters

Filter requests using expression-based rules against request properties (IP address, headers, path, HTTP method, and custom local fields).

Block by country

Restrict access to specific countries — useful for licensing, compliance, or regional rollouts. The Allow list denies all countries not listed:

aj, err := arcjet.NewClient(arcjet.Config{
	Key: arcjetKey,
	Rules: []arcjet.Rule{
		// Allow only US traffic — all other countries are denied.
		arcjet.Filter(arcjet.FilterOptions{
			Mode:  arcjet.ModeLive,
			Allow: []string{`ip.src.country == "US"`},
		}),
	},
})

decision, err := aj.Protect(r.Context(), r)
if decision.IsDenied() {
	http.Error(w, "Access restricted in your region", http.StatusForbidden)
	return
}

To restrict to a specific state or province, combine country and region:

arcjet.Filter(arcjet.FilterOptions{
	Mode: arcjet.ModeLive,
	// Allow only California — useful for state-level compliance e.g. CCPA testing.
	Allow: []string{`ip.src.country == "US" && ip.src.region == "California"`},
})
Block VPN and proxy traffic

Prevent anonymized traffic from accessing sensitive endpoints — useful for fraud prevention, enforcing geo-restrictions, and reducing abuse:

arcjet.Filter(arcjet.FilterOptions{
	Mode: arcjet.ModeLive,
	Deny: []string{
		"ip.src.vpn",   // VPN services
		"ip.src.proxy", // Open proxies
		"ip.src.tor",   // Tor exit nodes
	},
})

For cases where you want to allow some anonymized traffic (e.g. Apple Private Relay) but still log or handle it differently, use decision.IP helpers after calling Protect:

decision, err := aj.Protect(r.Context(), r)
if err != nil {
	return err
}

if decision.IP.IsVPN || decision.IP.IsTor {
	http.Error(w, "VPN traffic not allowed", http.StatusForbidden)
	return
}
if decision.IP.IsRelay {
	// Privacy relay (e.g. Apple Private Relay) — lower risk than a VPN.
	// Allow through with custom handling.
}
Custom local fields

Pass arbitrary values from your application for use in filter expressions:

decision, err := aj.Protect(
	r.Context(),
	r,
	arcjet.WithFilterLocal(map[string]string{
		"userId": currentUser.ID,
		"plan":   currentUser.Plan,
	}),
)

These are then available as local["userId"] and local["plan"] in expressions:

arcjet.Filter(arcjet.FilterOptions{
	Mode: arcjet.ModeLive,
	Deny: []string{`local["plan"] == "free" && ip.src.country != "US"`},
})

WithFilterLocal values stay local — they are evaluated by the embedded WebAssembly runtime and are never sent to Arcjet Cloud.

See the Request Filters docs, IP Geolocation blueprint, and VPN/Proxy Detection blueprint for more details.

IP analysis

Arcjet returns IP metadata with every decision — no extra API calls needed.

if decision.IP.IsHosting {
	// Likely a cloud/hosting provider — often suspicious for bots.
	http.Error(w, "Hosting IP blocked", http.StatusForbidden)
	return
}

if decision.IP.IsVPN || decision.IP.IsProxy || decision.IP.IsTor {
	// Apply your policy for anonymized traffic.
}

ip := decision.IP
log.Println(ip.City, ip.CountryName) // geolocation
log.Println(ip.ASN, ip.ASNName)      // ASN / network
log.Println(ip.IsVPN, ip.IsHosting)  // reputation

Available fields include geolocation (Latitude, Longitude, City, Region, Country, Continent), network (ASN, ASNName, ASNDomain, ASNType, ASNCountry), and reputation (IsVPN, IsProxy, IsTor, IsHosting, IsRelay).

Arcjet Guard

arcjet.NewGuardClient is a lower-level API designed for AI agent tool calls and background tasks where there is no HTTP request object. It gives you fine-grained, per-call control over rate limiting, prompt injection detection, sensitive information detection (via GuardSensitiveInfo), and custom rules.

How it differs from NewClient
NewClient (request protection) NewGuardClient (guard)
Designed for HTTP request protection AI agent tool calls, background jobs
Request object Required (Protect(ctx, r, ...)) Not needed
Rule binding Rules configured once, input via Protect options Rules configured once, bound with input per invocation
Rate limit key IP or WithCharacteristics Explicit key string (SHA-256 hashed before sending)
Custom rules Not supported GuardCustom
Installation

Guard is part of the same github.com/arcjet/arcjet-go module — no extra install required.

Quick start

Declare the guard client and each rule once at package scope. Call guard.Guard at each specific operation with a hardcoded Label so the dashboard groups calls by what they actually are.

package agent

import (
	"context"
	"errors"
	"fmt"
	"os"
	"time"

	"github.com/arcjet/arcjet-go"
)

// Single guard client — reused across calls.
var guard = must(arcjet.NewGuardClient(arcjet.GuardConfig{
	Key: os.Getenv("ARCJET_KEY"),
}))

// Rules configured once. Each rule's Bucket groups its counters server-side.
var (
	userLimit = must(arcjet.GuardTokenBucket(arcjet.GuardTokenBucketOptions{
		Mode:       arcjet.ModeLive,
		RefillRate: 100,
		Interval:   time.Minute,
		Capacity:   1000,
		Bucket:     "agent.user-tokens", // distinct from any Label
	}))
	promptScan = must(arcjet.GuardPromptInjection(arcjet.GuardPromptInjectionOptions{Mode: arcjet.ModeLive}))
)

// GetWeather is an agent tool. Guard at the tool function (or at the
// dispatch arm right before calling it) — never in a generic
// handleToolCall(name, args) wrapper with an interpolated label.
func GetWeather(ctx context.Context, userID, message string) error {
	decision, err := guard.Guard(ctx, arcjet.GuardRequest{
		Label: "tools.get_weather", // hardcoded string, not fmt.Sprintf
		Metadata: map[string]string{
			"userId": userID,
		},
		Rules: []arcjet.GuardRuleInput{
			userLimit.Key(userID, 1),
			promptScan.Text(message),
		},
	})
	if err != nil {
		// Guard fails open — log and continue.
		return nil
	}
	if decision.IsDenied() {
		// Use the per-rule accessor to recover details — rate limit reset
		// time, prompt injection signal — so the caller knows WHY.
		if r := userLimit.DeniedResult(decision); r != nil {
			return fmt.Errorf("rate limited — resets at unix %d", r.ResetAtUnixSeconds)
		}
		if promptScan.DeniedResult(decision) != nil {
			return errors.New("input flagged as prompt injection")
		}
		return errors.New("blocked")
	}

	// ... do the work.
	return nil
}

Note: The Label argument should be a hardcoded string like "tools.get_weather", not fmt.Sprintf("tools.%s", name). Hardcoded labels stay greppable, and the dashboard groups by them. Interpolation produces a sea of distinct-looking entries instead of one bucket per operation.

Rate limiting

Token bucket, fixed window, and sliding window algorithms are available. Configure the rule once, then call .Key(key, requested) with a key and optional requested count for each invocation.

Every guard rate-limit rule requires an explicit Key at call time. There is no IP fallback — guard runs where there is no HTTP context. When there is no per-user context (e.g. a stdio MCP server or a single-tenant worker), pick a stable identifier such as the deployment name or "default" and add a comment explaining why.

Token bucket
userLimit, err := arcjet.GuardTokenBucket(arcjet.GuardTokenBucketOptions{
	Mode:       arcjet.ModeLive,
	RefillRate: 100,         // tokens added per interval
	Interval:   time.Minute, // refill interval
	Capacity:   1000,        // maximum bucket capacity
	Bucket:     "agent.user-tokens",
})

// At call time:
decision, err := guard.Guard(ctx, arcjet.GuardRequest{
	Label: "tools.get_weather",
	Rules: []arcjet.GuardRuleInput{userLimit.Key(userID, 5)},
})
Fixed window
teamLimit, err := arcjet.GuardFixedWindow(arcjet.GuardFixedWindowOptions{
	Mode:        arcjet.ModeLive,
	Window:      time.Hour,
	MaxRequests: 1000,
	Bucket:      "api.search",
})

decision, err := guard.Guard(ctx, arcjet.GuardRequest{
	Label: "api.search",
	Rules: []arcjet.GuardRuleInput{teamLimit.Key(teamID, 1)},
})
Sliding window
apiLimit, err := arcjet.GuardSlidingWindow(arcjet.GuardSlidingWindowOptions{
	Mode:        arcjet.ModeLive,
	Interval:    time.Minute,
	MaxRequests: 500,
	Bucket:      "api.query",
})

decision, err := guard.Guard(ctx, arcjet.GuardRequest{
	Label: "api.query",
	Rules: []arcjet.GuardRuleInput{apiLimit.Key(userID, 1)},
})
Prompt injection detection

Use on any untrusted text before it reaches a model or tool argument — and on tool call results when the tool fetches content from untrusted sources.

promptScan, err := arcjet.GuardPromptInjection(arcjet.GuardPromptInjectionOptions{Mode: arcjet.ModeLive})

decision, err := guard.Guard(ctx, arcjet.GuardRequest{
	Label: "tools.get_weather",
	Rules: []arcjet.GuardRuleInput{promptScan.Text(userMessage)},
})

if decision.IsDenied() && decision.Reason == arcjet.ReasonPromptInjection {
	return errors.New("prompt injection detected")
}
Custom rules

Define custom local rules with GuardCustom. The evaluation function runs in your process — Arcjet never executes it. The Input map you pass at call time is submitted to Arcjet alongside the function's Conclusion and any opaque Data, so the dashboard can show what the rule saw. Do not pass raw secrets or unhashed PII through Input; hash or redact first if the inputs are sensitive.

topicRule, err := arcjet.GuardCustom(arcjet.GuardCustomOptions{
	Mode:   arcjet.ModeLive,
	Config: map[string]string{"blocked_topic": "weapons"},
	Func: func(ctx context.Context, input map[string]string) (arcjet.GuardCustomResult, error) {
		if input["topic"] == "weapons" {
			return arcjet.GuardCustomResult{
				Conclusion: arcjet.ConclusionDeny,
				Data:       map[string]string{"matched": input["topic"]},
			}, nil
		}
		return arcjet.GuardCustomResult{Conclusion: arcjet.ConclusionAllow}, nil
	},
})

decision, err := guard.Guard(ctx, arcjet.GuardRequest{
	Label: "content",
	Rules: []arcjet.GuardRuleInput{
		topicRule.Input(map[string]string{"topic": userTopic}),
	},
})
Per-rule results

decision.Results contains one entry per rule invocation, with typed result details for inspection:

for _, result := range decision.Results {
	switch {
	case result.TokenBucket != nil:
		log.Printf("rate limit: %d / %d remaining; resets at %d",
			result.TokenBucket.RemainingTokens,
			result.TokenBucket.MaxTokens,
			result.TokenBucket.ResetAtUnixSeconds)
	case result.PromptInjection != nil:
		log.Printf("prompt injection detected=%v", result.PromptInjection.Detected)
	case result.LocalSensitiveInfo != nil:
		log.Printf("sensitive info detected=%v types=%v",
			result.LocalSensitiveInfo.Detected,
			result.LocalSensitiveInfo.DetectedEntityTypes)
	}
	if result.IsDenied() {
		log.Printf("denied by %s", result.Reason)
	}
}
Decision API
decision, err := guard.Guard(ctx, arcjet.GuardRequest{
	Label: "tools.get_weather",
	Rules: []arcjet.GuardRuleInput{userLimit.Key(userID, 5)},
})

// Layer 1: conclusion and reason.
decision.Conclusion // arcjet.ConclusionAllow or arcjet.ConclusionDeny
decision.Reason     // arcjet.ReasonRateLimit, ReasonPromptInjection, etc.

// Layer 2: error detection.
decision.IsErrored() // true if any rule errored or the server reported diagnostics

// Layer 3: per-rule results (see "Per-rule results" above).
for _, result := range decision.Results {
	log.Println(result.Type, result.Conclusion)
}
Guard parameter reference
Field Type Description
Rules []arcjet.GuardRuleInput Bound rule inputs (required)
Label string Hardcoded label identifying this guard call (required)
Metadata map[string]string Optional key-value metadata recorded in the dashboard
DRY_RUN mode

All guard rules accept a Mode parameter. Use arcjet.ModeDryRun to evaluate rules without blocking:

userLimit, err := arcjet.GuardTokenBucket(arcjet.GuardTokenBucketOptions{
	Mode:       arcjet.ModeDryRun,
	RefillRate: 10,
	Interval:   time.Minute,
	Capacity:   100,
	Bucket:     "agent.user-tokens",
})

Best practices

Single-instance pattern

Create one Arcjet client at startup and reuse it across all handlers:

// Good — one instance, created once at package scope.
var aj = must(arcjet.NewClient(arcjet.Config{Key: arcjetKey, Rules: []arcjet.Rule{...}}))

// Bad — new client per request wastes resources and creates a new HTTP/2
// connection each time.
func handler(w http.ResponseWriter, r *http.Request) {
	aj, _ := arcjet.NewClient(arcjet.Config{...}) // don't do this
}
Shared client in its own package

For larger projects, put the client in its own package (e.g. internal/security/arcjet.go) and import it from handlers. Always include Shield as a base rule, then layer route-specific rules with WithRule:

// internal/security/arcjet.go
package security

import (
	"os"

	"github.com/arcjet/arcjet-go"
)

var Client = must(arcjet.NewClient(arcjet.Config{
	Key: os.Getenv("ARCJET_KEY"),
	Rules: []arcjet.Rule{
		arcjet.Shield(arcjet.ShieldOptions{Mode: arcjet.ModeLive}),
		arcjet.DetectBot(arcjet.BotOptions{Mode: arcjet.ModeLive, Allow: []string{}}),
	},
}))
// internal/http/chat.go
package http

import (
	"log"
	"net/http"
	"time"

	"github.com/arcjet/arcjet-go"
	"example.com/app/internal/security"
)

// Layer per-route rules without mutating the shared client. WithRule
// validates and pre-builds the rule's wire form, so it returns an error
// for misconfigured rules — keep the call at package scope so a bad rule
// fails at startup instead of on the first request.
var chatClient = must(security.Client.WithRule(arcjet.TokenBucket(arcjet.TokenBucketOptions{
	Mode:            arcjet.ModeLive,
	Characteristics: []string{"userId"},
	RefillRate:      100,
	Interval:        time.Minute,
	Capacity:        1000,
})))

func must[T any](v T, err error) T {
	if err != nil {
		log.Fatal(err)
	}
	return v
}

WithRule returns a copy — the original client is left unchanged, so the same base instance can be specialised for many routes.

DRY_RUN mode for testing

Use arcjet.ModeDryRun to test rules without blocking traffic. Decisions are logged but requests are allowed through:

arcjet.DetectBot(arcjet.BotOptions{Mode: arcjet.ModeDryRun, Allow: []string{}})
arcjet.TokenBucket(arcjet.TokenBucketOptions{
	Mode:       arcjet.ModeDryRun,
	RefillRate: 5,
	Interval:   10 * time.Second,
	Capacity:   10,
})
Proxy configuration

When running behind a load balancer or reverse proxy, configure trusted IPs so Arcjet resolves the real client IP from X-Forwarded-For:

aj, err := arcjet.NewClient(arcjet.Config{
	Key:     arcjetKey,
	Rules:   []arcjet.Rule{...},
	Proxies: []string{"10.0.0.0/8", "192.168.0.1"},
})
Hosting platform

When deployed on a managed hosting platform, Arcjet reads the client IP from that platform's signed headers. Fly.io, Vercel, Render, Firebase, Railway, and Cloudflare Pages are auto-detected from their environment variables. If your service runs behind a platform that isn't auto-detected — most importantly a Go origin behind the Cloudflare CDN, which doesn't set CF_PAGES — set Config.Platform explicitly so Arcjet trusts the platform header (e.g. CF-Connecting-IP) instead of guessing:

aj, err := arcjet.NewClient(arcjet.Config{
	Key:      arcjetKey,
	Rules:    []arcjet.Rule{...},
	Platform: arcjet.PlatformCloudflare,
})

NewClient returns ErrInvalidPlatform for an unrecognized value.

Protect parameter reference

All options are optional and passed alongside the *http.Request:

Option Used by
WithRequested(int) Token bucket rate limit
WithCharacteristic(key, value string) Rate limiting — single key/value
WithCharacteristics(map[string]string) Rate limiting (values for keys declared in rules)
WithDetectPromptInjectionMessage(string) Prompt injection detection
WithSensitiveInfoValue(string) Sensitive information detection (text scanned locally)
WithEmail(string) Email validation
WithFilterLocal(map[string]string) Request filters using local["field"] expressions
WithIPSrc(string) Manual IP override (advanced)
WithBody([]byte) Request body override
WithExtra(map[string]string) Additional fields sent to Arcjet
Decision response
decision, err := aj.Protect(r.Context(), r)

// Top-level checks.
decision.IsDenied()    // true if any LIVE rule denied the request
decision.IsAllowed()   // true if all rules allowed the request
decision.IsErrored()   // true if Arcjet encountered an error (fails open)

// Branch on reason for actionable error responses. Only branch on reasons that
// produce a different response — a SHIELD arm returning 403 when the default
// already returns 403 is dead code.
switch {
case decision.Reason.IsRateLimit():
	log.Println(decision.Reason.RateLimit.Remaining)
case decision.Reason.IsBot():
	log.Println(decision.Reason.Bot.Denied)
}

// Per-rule results.
for _, result := range decision.Results {
	log.Println(result.Reason.Type, result.Conclusion)
}
Error handling

Arcjet is designed to fail open — if the service is unavailable, requests are allowed through. Check for errors explicitly if your use case requires it:

decision, err := aj.Protect(r.Context(), r)
if err != nil {
	// Arcjet service error — fail open or apply fallback policy.
	log.Printf("arcjet: %v", err)
} else if decision.IsDenied() {
	http.Error(w, "Denied", http.StatusForbidden)
	return
}

Configuration and validation errors wrap exported sentinels so they can be detected with errors.Is:

aj, err := arcjet.NewClient(arcjet.Config{Key: ""})
if errors.Is(err, arcjet.ErrMissingKey) {
	log.Fatal("set ARCJET_KEY in your environment")
}

Available sentinels: ErrMissingKey, ErrNilClient, ErrNilRequest, ErrNilRule, ErrInvalidMode, ErrAllowDenyConflict, ErrInvalidProxy, ErrInvalidLabel, ErrInvalidRateLimit, ErrEmptyKey, ErrMissingFunc, ErrInvalidWasm, ErrWasmClosed, ErrWasmExportNotFound, ErrEmptyResponse.

Remote and per-rule errors are surfaced as ArcjetError values with a Code field. Match a specific server error code with errors.Is:

if errors.Is(err, arcjet.ArcjetError{Code: "AJ1100"}) {
	// handle a specific Arcjet error code
}

Decision.Err() and GuardDecision.Err() return the underlying ArcjetError (or nil) when the decision errored — useful for bubbling Arcjet errors out of helpers.

Verify decisions

After wiring up protection, confirm it is actually firing. There is no shortcut to seeing a real decision — trigger one, then check the platform.

  1. Build and start the app (go build ./... && ./your-app).
  2. Trigger a real request, e.g. curl http://localhost:3000/chat. To trip a rate limit, loop the call:
    for i in {1..50}; do curl -s -o /dev/null -w "%{http_code}\n" http://localhost:3000/chat; done
    
    For guard calls, invoke the protected function directly (go run ./cmd/agent-smoketest) rather than trying to curl something — there is no HTTP surface.
  3. Confirm the decision via the Arcjet CLI:
    arcjet requests list --site-id <id>   # request protection
    arcjet guards list --site-id <id>     # guard
    arcjet requests explain --site-id <id> --request-id <id>
    

The dashboard at app.arcjet.com shows the same data with filtering and history.

Gotchas

  • Wrong client: NewClient is for HTTP routes; NewGuardClient is for non-HTTP code (tool calls, MCP servers, queue workers, background jobs). Using the wrong one is the most common mistake — MCP "servers" don't receive HTTP requests, so they use NewGuardClient.
  • Wrong placement: Protect belongs inside each handler, not in generic middleware that wraps every request including static assets.
  • Wrong layer for Guard: don't put guard.Guard in a handleToolCall(name, args) dispatcher. Put it inside each specific tool function (or the dispatch arm right before the call) so the Label and Metadata can be hardcoded.
  • Interpolated labels: Label: fmt.Sprintf("tools.%s", name) defeats the dashboard grouping. Use a hardcoded string per tool.
  • Double-counting: calling Protect or Guard multiple times for the same operation counts against rate limits multiple times.
  • Hardcoded keys: never hardcode ARCJET_KEY — read it from the environment. Don't commit it to source.

Support

This repository follows the Arcjet Support Policy.

Security

This repository follows the Arcjet Security Policy.

License

Licensed under the Apache License, Version 2.0.

Documentation

Overview

Package arcjet provides the Go SDK for Arcjet, the runtime security platform for AI code.

Use NewClient for request protection in net/http handlers and any router that exposes *http.Request. Always include Shield as a base rule, then layer route-specific rules with Client.WithRule, which returns a copy of the client without mutating the base. WithRule validates and pre-builds the rule's wire form, so it returns an error if the rule is misconfigured; keep the call near startup rather than on the hot path. Call Protect inside each handler — once per request — not in generic middleware that runs on every path.

Use NewGuardClient for non-HTTP entry points: AI agent tool calls, MCP servers, queue consumers, and background jobs. Create the GuardClient and each rule once at package scope so per-rule result accessors have a stable reference. Call GuardClient.Guard at the specific operation with a hardcoded Label such as "tools.get_weather" — never an interpolated string like fmt.Sprintf("tools.%s", name), which defeats dashboard grouping. Each rate-limit rule needs an explicit Key at call time; when there is no user context (e.g. a stdio MCP server), pick a stable identifier such as the deployment name rather than an empty string.

Arcjet is designed to fail open: if the service is unavailable, Protect and Guard return an error and the caller should continue serving.

Index

Constants

View Source
const (
	BotCategoryAcademic     = "CATEGORY:ACADEMIC"
	BotCategoryAdvertising  = "CATEGORY:ADVERTISING"
	BotCategoryAI           = "CATEGORY:AI"
	BotCategoryAmazon       = "CATEGORY:AMAZON"
	BotCategoryArchive      = "CATEGORY:ARCHIVE"
	BotCategoryBotnet       = "CATEGORY:BOTNET"
	BotCategoryFeedFetcher  = "CATEGORY:FEEDFETCHER"
	BotCategoryGoogle       = "CATEGORY:GOOGLE"
	BotCategoryMeta         = "CATEGORY:META"
	BotCategoryMicrosoft    = "CATEGORY:MICROSOFT"
	BotCategoryMonitor      = "CATEGORY:MONITOR"
	BotCategoryOptimizer    = "CATEGORY:OPTIMIZER"
	BotCategoryPreview      = "CATEGORY:PREVIEW"
	BotCategoryProgrammatic = "CATEGORY:PROGRAMMATIC"
	BotCategorySearchEngine = "CATEGORY:SEARCH_ENGINE"
	BotCategorySlack        = "CATEGORY:SLACK"
	BotCategorySocial       = "CATEGORY:SOCIAL"
	BotCategoryTool         = "CATEGORY:TOOL"
	BotCategoryUnknown      = "CATEGORY:UNKNOWN"
	BotCategoryVercel       = "CATEGORY:VERCEL"
	BotCategoryYahoo        = "CATEGORY:YAHOO"
)

Bot category identifiers for use with BotOptions.Allow and BotOptions.Deny.

Categories group well-known bots so a single entry covers many user agents. Pass these alongside any specific bot identifiers from https://arcjet.com/bot-list. Strings are still accepted; these constants exist for autocomplete and to catch typos at compile time.

View Source
const Version = "0.1.0"

Version is the Arcjet Go SDK version sent with Decide and Guard requests.

Variables

View Source
var (
	// ErrMissingKey is returned when no Arcjet site key is configured.
	ErrMissingKey = errors.New("site key required (set Config.Key or ARCJET_KEY)")
	// ErrNilClient is returned when a method is called on a nil Client or
	// GuardClient.
	ErrNilClient = errors.New("client is nil")
	// ErrNilRequest is returned when Client.Protect is called with a nil
	// *http.Request.
	ErrNilRequest = errors.New("request is nil")
	// ErrNilRule is returned when a rule input is nil.
	ErrNilRule = errors.New("rule is nil")
	// ErrInvalidMode is returned when a Mode value is unrecognized.
	ErrInvalidMode = errors.New("invalid mode")
	// ErrAllowDenyConflict is returned when a rule sets both Allow and Deny.
	ErrAllowDenyConflict = errors.New("allow and deny are mutually exclusive")
	// ErrInvalidProxy is returned when a trusted proxy IP or CIDR is invalid.
	ErrInvalidProxy = errors.New("invalid trusted proxy")
	// ErrInvalidPlatform is returned when Config.Platform is not a recognized
	// Platform value.
	ErrInvalidPlatform = errors.New("invalid platform")
	// ErrInvalidLabel is returned when a Guard label fails validation.
	ErrInvalidLabel = errors.New("invalid guard label")
	// ErrInvalidRateLimit is returned when rate-limit options are invalid.
	ErrInvalidRateLimit = errors.New("invalid rate limit configuration")
	// ErrEmptyKey is returned when a Guard rate-limit key is empty.
	ErrEmptyKey = errors.New("rate limit key required")
	// ErrMissingFunc is returned when a custom rule has no evaluation
	// function.
	ErrMissingFunc = errors.New("custom rule function required")
	// ErrInvalidWasm is returned when a Wasm module is empty or invalid.
	ErrInvalidWasm = errors.New("invalid wasm module")
	// ErrWasmClosed is returned when a Wasm module method is called after
	// Close.
	ErrWasmClosed = errors.New("wasm module is closed")
	// ErrWasmExportNotFound is returned when a Wasm function export is
	// missing.
	ErrWasmExportNotFound = errors.New("wasm export not found")
	// ErrEmptyResponse is returned when Arcjet returns an empty decision
	// response.
	ErrEmptyResponse = errors.New("empty response")
)

Sentinel errors returned by configuration and validation paths. Wrap them with fmt.Errorf("...: %w", Err...) when adding context so callers can detect the underlying cause via errors.Is.

Remote errors and per-rule errors are surfaced as ArcjetError values.

Functions

func SetRateLimitHeaders

func SetRateLimitHeaders(w http.ResponseWriter, d Decision)

SetRateLimitHeaders writes rate limit headers describing the decision onto w, following the IETF "RateLimit header fields for HTTP" draft. It sets:

  • RateLimit: limit=<max>, remaining=<remaining>, reset=<seconds>
  • RateLimit-Policy: <max>;w=<window>[, <max>;w=<window>...]

When the decision ran multiple rate limit rules, RateLimit reports the limit nearest to being exhausted (lowest remaining, then soonest reset, then smallest max) while RateLimit-Policy lists every policy. SetRateLimitHeaders is a no-op when the decision carries no rate limit reason, so it is safe to call unconditionally. Mirrors setRateLimitHeaders from @arcjet/decorate in arcjet-js.

Types

type ArcjetError

type ArcjetError struct {
	Code    string `json:"code,omitempty"`
	Message string `json:"message,omitempty"`
}

ArcjetError describes an error returned by Arcjet or a local guard rule.

func (ArcjetError) Error

func (e ArcjetError) Error() string

Error formats an Arcjet error as a Go error string.

func (ArcjetError) Is

func (e ArcjetError) Is(target error) bool

Is reports whether target is an ArcjetError with the same, non-empty Code. Use it with errors.Is to detect specific Arcjet error codes:

if errors.Is(err, ArcjetError{Code: "AJ1100"}) { ... }

A target with an empty Code matches nothing, so ArcjetError{} is not a wildcard for "any Arcjet error" — use errors.As for that. This matters because Decision.Err returns a code-less ArcjetError.

type BotOptions

type BotOptions struct {
	// Mode controls whether the rule enforces denials or only reports them.
	Mode Mode
	// Allow lists allowed bot categories or identifiers.
	Allow []string
	// Deny lists denied bot categories or identifiers.
	Deny []string
}

BotOptions configures bot detection.

Allow and Deny are mutually exclusive. An empty Allow list blocks all detected bots.

type BotReason

type BotReason struct {
	Allowed  []string `json:"allowed,omitempty"`
	Denied   []string `json:"denied,omitempty"`
	Verified bool     `json:"verified,omitempty"`
	Spoofed  bool     `json:"spoofed,omitempty"`
}

BotReason contains details for a bot detection decision.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client evaluates HTTP requests with Arcjet request protection rules.

A Client is safe for concurrent use and should be created once at startup and reused across handlers.

func NewClient

func NewClient(cfg Config) (*Client, error)

NewClient creates a reusable request protection client.

If Config.Key is empty, NewClient reads ARCJET_KEY from the environment.

func (*Client) Close

func (c *Client) Close(ctx context.Context) error

Close releases local WebAssembly resources held by the client.

func (*Client) Protect

func (c *Client) Protect(ctx context.Context, r *http.Request, opts ...ProtectOption) (Decision, error)

Protect evaluates an HTTP request with the client's configured rules.

func (*Client) ProtectDetails

func (c *Client) ProtectDetails(ctx context.Context, details ProtectDetails, opts ...ProtectOption) (Decision, error)

ProtectDetails evaluates explicit request details with the client's rules.

func (*Client) WithRule

func (c *Client) WithRule(rule Rule) (*Client, error)

WithRule returns a copy of the client with an additional route-specific rule.

The new rule is validated and converted to its wire form once; subsequent Protect calls reuse the cached representation.

type Conclusion

type Conclusion string

Conclusion is the top-level Arcjet decision outcome.

Conclusion values are normalized when JSON-decoded: both the bare wire strings ("DENY", "ALLOW", "CHALLENGE", "ERROR") and the prefixed forms ("CONCLUSION_DENY", "GUARD_CONCLUSION_DENY", etc.) are mapped to the canonical constants below.

const (
	// ConclusionAllow means Arcjet allowed the request or guard call.
	ConclusionAllow Conclusion = "ALLOW"
	// ConclusionDeny means Arcjet denied the request or guard call.
	ConclusionDeny Conclusion = "DENY"
	// ConclusionChallenge means Arcjet returned a challenge decision.
	ConclusionChallenge Conclusion = "CHALLENGE"
	// ConclusionError means Arcjet or a local rule produced an error result.
	ConclusionError Conclusion = "ERROR"
)

func (Conclusion) LogValue

func (c Conclusion) LogValue() slog.Value

LogValue implements slog.LogValuer so Conclusion logs as its string form.

func (*Conclusion) UnmarshalJSON

func (c *Conclusion) UnmarshalJSON(data []byte) error

UnmarshalJSON normalizes wire-format conclusion strings to canonical Conclusion constants. Single source of truth is parseConclusion.

type Config

type Config struct {
	// Key is the Arcjet site key. If empty, ARCJET_KEY is used.
	Key string
	// Rules are the request protection rules evaluated for each request.
	Rules []Rule
	// Characteristics are global rate-limit characteristic keys.
	Characteristics []string
	// HTTPClient is the client used for Arcjet RPCs. If nil, http.DefaultClient is used.
	HTTPClient *http.Client
	// BaseURL overrides the Arcjet Decide API base URL.
	BaseURL string
	// SDKVersion overrides the version reported to Arcjet.
	SDKVersion string
	// Proxies are trusted proxy IPs or CIDRs used to trust X-Forwarded-For.
	Proxies []string
	// Platform selects a managed hosting platform explicitly, overriding the
	// environment auto-detection. Set it when running behind a platform whose
	// environment variables aren't present — most importantly a Go service
	// behind the Cloudflare CDN. Leave empty to auto-detect.
	Platform Platform
	// SensitiveInfoDetect, if set, classifies tokens the bundled analyzer
	// didn't recognise. Shared across every SensitiveInfo rule on this
	// Client — the same callback model as arcjet-py's
	// `ImportCallbacks.sensitive_info_detect` and arcjet-js's analyzer
	// `detect` hook.
	SensitiveInfoDetect SensitiveInfoDetect
}

Config configures a request protection Client.

type Decision

type Decision struct {
	ID         string     `json:"id,omitempty"`
	Conclusion Conclusion `json:"conclusion,omitempty"`
	Reason     Reason     `json:"reason"`
	Results    []RuleResult
	// TTL is the number of seconds the decision can be cached.
	TTL int
	IP  IPDetails
	Raw json.RawMessage
}

Decision is the result of evaluating request protection rules.

func (Decision) Err

func (d Decision) Err() error

Err returns the decision's terminal error, or nil if the decision did not error. The returned error is an ArcjetError carrying the Reason message when available.

func (Decision) IsAllowed

func (d Decision) IsAllowed() bool

IsAllowed reports whether Arcjet allowed the request.

func (Decision) IsChallenged

func (d Decision) IsChallenged() bool

IsChallenged reports whether Arcjet returned a challenge decision.

func (Decision) IsDenied

func (d Decision) IsDenied() bool

IsDenied reports whether Arcjet denied the request.

func (Decision) IsErrored

func (d Decision) IsErrored() bool

IsErrored reports whether Arcjet returned or locally computed an error.

func (Decision) IsMissingUserAgent

func (d Decision) IsMissingUserAgent() bool

IsMissingUserAgent reports whether a bot rule denied the request because it had no User-Agent header. A missing User-Agent is a common indicator of an automated client, since IETF HTTP Semantics (RFC 9110) recommends sending one. Mirrors @arcjet/inspect's isMissingUserAgent in arcjet-js.

func (Decision) IsSpoofedBot

func (d Decision) IsSpoofedBot() bool

IsSpoofedBot reports whether a bot rule detected a spoofed verified bot — one claiming to be a well-known crawler but originating from an IP outside that crawler's published ranges.

func (Decision) IsVerifiedBot

func (d Decision) IsVerifiedBot() bool

IsVerifiedBot reports whether a bot rule confirmed the request came from a verified bot (for example a search engine crawler whose IP matches its published ranges). You may want to allow such requests even when other signals would otherwise deny them.

type EmailOptions

type EmailOptions struct {
	// Mode controls whether the rule enforces denials or only reports them.
	Mode Mode
	// Allow lists allowed email types.
	Allow []EmailType
	// Deny lists denied email types.
	Deny []EmailType
	// RequireTopLevelDomain requires a top-level domain in the address when true.
	RequireTopLevelDomain *bool
	// AllowDomainLiteral allows domain literals such as user@[192.0.2.1] when true.
	AllowDomainLiteral *bool
}

EmailOptions configures email validation.

type EmailReason

type EmailReason struct {
	Types []EmailType `json:"types,omitempty"`
}

EmailReason contains details for an email validation decision.

type EmailType

type EmailType string

EmailType classifies an email address for ValidateEmail rules.

Values are stored in their canonical form (without the "EMAIL_TYPE_" wire prefix). UnmarshalJSON strips the prefix when decoding responses. See constants.go for the supported constants.

const (
	EmailTypeDisposable  EmailType = "DISPOSABLE"
	EmailTypeFree        EmailType = "FREE"
	EmailTypeInvalid     EmailType = "INVALID"
	EmailTypeNoMXRecords EmailType = "NO_MX_RECORDS"
	EmailTypeNoGravatar  EmailType = "NO_GRAVATAR"
)

Email type identifiers for use with EmailOptions.Allow and EmailOptions.Deny.

func (EmailType) LogValue

func (e EmailType) LogValue() slog.Value

LogValue implements slog.LogValuer so EmailType logs as its string form.

func (*EmailType) UnmarshalJSON

func (e *EmailType) UnmarshalJSON(data []byte) error

UnmarshalJSON strips the wire "EMAIL_TYPE_" prefix when present so values match the EmailType constants.

type EntityType

type EntityType string

EntityType classifies a sensitive-information entity. See constants.go for the supported constants.

const (
	SensitiveInfoEmail            EntityType = "EMAIL"
	SensitiveInfoPhoneNumber      EntityType = "PHONE_NUMBER"
	SensitiveInfoIPAddress        EntityType = "IP_ADDRESS"
	SensitiveInfoCreditCardNumber EntityType = "CREDIT_CARD_NUMBER"
)

Sensitive information entity type identifiers for use with SensitiveInfoOptions.Allow, SensitiveInfoOptions.Deny, GuardSensitiveInfoOptions.Allow, and GuardSensitiveInfoOptions.Deny.

func (EntityType) LogValue

func (e EntityType) LogValue() slog.Value

LogValue implements slog.LogValuer so EntityType logs as its string form.

type FilterOptions

type FilterOptions struct {
	// Mode controls whether the rule enforces denials or only reports them.
	Mode Mode
	// Allow expressions allow matching requests and deny non-matching requests.
	Allow []string
	// Deny expressions deny matching requests.
	Deny []string
}

FilterOptions configures request filters.

Allow and Deny expressions are mutually exclusive.

type FilterReason

type FilterReason struct {
	MatchedExpressions      []string `json:"matchedExpressions,omitempty"`
	UndeterminedExpressions []string `json:"undeterminedExpressions,omitempty"`
}

FilterReason contains request filter match results.

type FixedWindowOptions

type FixedWindowOptions struct {
	// Mode controls whether the rule enforces denials or only reports them.
	Mode Mode
	// Characteristics are rate-limit keys such as "userId".
	Characteristics []string
	// Window is the fixed window duration.
	Window time.Duration
	// MaxRequests is the maximum number of requests per window.
	MaxRequests int
}

FixedWindowOptions configures a fixed window rate limit rule.

type GuardClient

type GuardClient struct {
	// contains filtered or unexported fields
}

GuardClient evaluates non-HTTP inputs such as tool calls, jobs, and queues.

A GuardClient is safe for concurrent use and should be created once at startup.

func NewGuardClient

func NewGuardClient(cfg GuardConfig) (*GuardClient, error)

NewGuardClient creates a reusable Guard client.

If GuardConfig.Key is empty, NewGuardClient reads ARCJET_KEY from the environment.

func (*GuardClient) Close

func (c *GuardClient) Close(ctx context.Context) error

Close releases the locally-compiled wasm factory, if any. Safe to call even if no local Guard rule was ever used.

func (*GuardClient) Guard

Guard evaluates bound guard rule inputs.

type GuardConfig

type GuardConfig struct {
	// Key is the Arcjet site key. If empty, ARCJET_KEY is used.
	Key string
	// HTTPClient is the client used for Arcjet RPCs. If nil, http.DefaultClient is used.
	HTTPClient *http.Client
	// BaseURL overrides the Arcjet Guard API base URL.
	BaseURL string
	// SDKVersion overrides the version reported to Arcjet.
	SDKVersion string
	// SensitiveInfoDetect, if set, classifies tokens the bundled analyzer
	// didn't recognise. Shared across every GuardSensitiveInfo rule on
	// this client.
	SensitiveInfoDetect SensitiveInfoDetect
}

GuardConfig configures a GuardClient.

type GuardCustomFunc

type GuardCustomFunc func(context.Context, map[string]string) (GuardCustomResult, error)

GuardCustomFunc evaluates one custom local Guard rule input.

type GuardCustomOptions

type GuardCustomOptions struct {
	// Mode controls whether the rule enforces denials or only reports them.
	Mode Mode
	// Config is the rule configuration recorded with each invocation.
	Config map[string]string
	// Func is the local evaluation function. Required.
	Func GuardCustomFunc
	// Label identifies this rule in the Arcjet dashboard.
	Label string
	// Metadata is recorded with every invocation of this rule.
	Metadata map[string]string
}

GuardCustomOptions configures a custom local Guard rule.

type GuardCustomResult

type GuardCustomResult struct {
	// Conclusion is the custom rule conclusion.
	Conclusion Conclusion
	// Data is optional result data recorded with the custom rule result.
	Data map[string]string
}

GuardCustomResult is the result returned by a custom local Guard rule.

type GuardCustomRule

type GuardCustomRule struct {
	// contains filtered or unexported fields
}

GuardCustomRule is a configured custom local Guard rule.

func GuardCustom

func GuardCustom(opts GuardCustomOptions) (*GuardCustomRule, error)

GuardCustom creates a custom local Guard rule.

func (*GuardCustomRule) DeniedResult

DeniedResult returns this rule's custom result if it denied the Guard call, or nil otherwise.

func (*GuardCustomRule) Input

func (r *GuardCustomRule) Input(data map[string]string) GuardRuleInput

Input binds custom rule input data for one Guard call.

func (*GuardCustomRule) Result

Result returns this rule's custom result from the given Guard decision, or nil if the rule did not produce one.

type GuardDecision

type GuardDecision struct {
	ID         string
	Conclusion Conclusion
	Reason     ReasonType
	Results    []GuardRuleResult
	Errors     []ArcjetError
}

GuardDecision is the result of a Guard evaluation.

func (GuardDecision) Err

func (d GuardDecision) Err() error

Err returns the first ArcjetError carried by this decision (top-level or per-rule) or nil if the decision did not error. Useful with errors.Is / errors.As when bubbling up Arcjet errors to handlers.

func (GuardDecision) IsAllowed

func (d GuardDecision) IsAllowed() bool

IsAllowed reports whether Arcjet allowed the Guard call.

func (GuardDecision) IsDenied

func (d GuardDecision) IsDenied() bool

IsDenied reports whether Arcjet denied the Guard call.

func (GuardDecision) IsErrored

func (d GuardDecision) IsErrored() bool

IsErrored reports whether any Guard rule or the Guard response has an error. Arcjet fails open — when this is true the call was allowed to proceed but rule evaluation was incomplete.

type GuardFixedWindowOptions

type GuardFixedWindowOptions struct {
	// Mode controls whether the rule enforces denials or only reports them.
	Mode Mode
	// Window is the fixed window duration.
	Window time.Duration
	// MaxRequests is the maximum number of requests per window.
	MaxRequests int
	// Bucket groups counters for this rule.
	Bucket string
	// Label identifies this rule in the Arcjet dashboard.
	Label string
	// Metadata is recorded with every invocation of this rule.
	Metadata map[string]string
}

GuardFixedWindowOptions configures a Guard fixed window rule.

type GuardFixedWindowResult

type GuardFixedWindowResult struct {
	Conclusion         Conclusion `json:"conclusion"`
	RemainingRequests  int        `json:"remainingRequests"`
	MaxRequests        int        `json:"maxRequests"`
	ResetAtUnixSeconds int64      `json:"resetAtUnixSeconds"`
	WindowSeconds      int        `json:"windowSeconds"`
}

GuardFixedWindowResult contains Guard fixed window result details.

type GuardFixedWindowRule

type GuardFixedWindowRule struct {
	// contains filtered or unexported fields
}

GuardFixedWindowRule is a configured Guard fixed window rule.

func GuardFixedWindow

func GuardFixedWindow(opts GuardFixedWindowOptions) (*GuardFixedWindowRule, error)

GuardFixedWindow creates a Guard fixed window rule.

func (*GuardFixedWindowRule) DeniedResult

DeniedResult returns this rule's fixed window result if it denied the Guard call, or nil otherwise.

func (*GuardFixedWindowRule) Key

func (r *GuardFixedWindowRule) Key(key string, requested int) GuardRuleInput

Key binds a fixed window key and requested count for one Guard call.

func (*GuardFixedWindowRule) Result

Result returns this rule's fixed window result from the given Guard decision, or nil if the rule did not produce one.

type GuardLocalCustomResult

type GuardLocalCustomResult struct {
	Conclusion Conclusion        `json:"conclusion"`
	Data       map[string]string `json:"data,omitempty"`
}

GuardLocalCustomResult contains custom local Guard result details.

type GuardPromptInjectionOptions

type GuardPromptInjectionOptions struct {
	// Mode controls whether the rule enforces denials or only reports them.
	Mode Mode
	// Label identifies this rule in the Arcjet dashboard.
	Label string
	// Metadata is recorded with every invocation of this rule.
	Metadata map[string]string
}

GuardPromptInjectionOptions configures a Guard prompt injection rule.

type GuardPromptInjectionRule

type GuardPromptInjectionRule struct {
	// contains filtered or unexported fields
}

GuardPromptInjectionRule is a configured Guard prompt injection rule.

func GuardPromptInjection

func GuardPromptInjection(opts GuardPromptInjectionOptions) (*GuardPromptInjectionRule, error)

GuardPromptInjection creates a Guard prompt injection rule.

func (*GuardPromptInjectionRule) DeniedResult

DeniedResult returns this rule's prompt injection result if it denied the Guard call, or nil otherwise.

func (*GuardPromptInjectionRule) Result

Result returns this rule's prompt injection result from the given Guard decision, or nil if the rule did not produce one.

func (*GuardPromptInjectionRule) Text

Text binds text to scan for one Guard call.

type GuardPromptResult

type GuardPromptResult struct {
	Conclusion Conclusion `json:"conclusion"`
	Detected   bool       `json:"detected"`
}

GuardPromptResult contains Guard prompt injection result details.

type GuardRequest

type GuardRequest struct {
	// Label identifies this Guard call.
	Label string
	// Metadata is optional key-value metadata for this Guard call.
	Metadata map[string]string
	// Rules are bound rule inputs evaluated by Guard.
	Rules []GuardRuleInput
}

GuardRequest is a single Guard evaluation request.

type GuardRuleInput

type GuardRuleInput interface {
	// contains filtered or unexported methods
}

GuardRuleInput is a rule bound to runtime input for a Guard call.

The unexported `guardSubmission` method seals the interface so external types can't implement it; SDK-provided rules use it to build the wire submission, optionally running locally via the shared evaluator.

type GuardRuleResult

type GuardRuleResult struct {
	ResultID           string
	ConfigID           string
	InputID            string
	Type               GuardRuleType
	Conclusion         Conclusion
	Reason             ReasonType
	TokenBucket        *GuardTokenBucketResult
	FixedWindow        *GuardFixedWindowResult
	SlidingWindow      *GuardSlidingWindowResult
	PromptInjection    *GuardPromptResult
	LocalSensitiveInfo *GuardSensitiveInfoResult
	LocalCustom        *GuardLocalCustomResult
	Error              *ArcjetError
	NotRun             bool
}

GuardRuleResult is the per-rule result included in a Guard decision.

func (GuardRuleResult) IsDenied

func (r GuardRuleResult) IsDenied() bool

IsDenied reports whether this Guard rule result denied the Guard call.

func (GuardRuleResult) IsErrored

func (r GuardRuleResult) IsErrored() bool

IsErrored reports whether this Guard rule result contains an error.

type GuardRuleType

type GuardRuleType string

GuardRuleType identifies a Guard rule family in a GuardRuleResult.

const (
	// GuardRuleTypeUnknown is used when a Guard rule type is unrecognized.
	GuardRuleTypeUnknown GuardRuleType = ""
	// GuardRuleTypeTokenBucket identifies a Guard token bucket rule.
	GuardRuleTypeTokenBucket GuardRuleType = "TOKEN_BUCKET"
	// GuardRuleTypeFixedWindow identifies a Guard fixed window rule.
	GuardRuleTypeFixedWindow GuardRuleType = "FIXED_WINDOW"
	// GuardRuleTypeSlidingWindow identifies a Guard sliding window rule.
	GuardRuleTypeSlidingWindow GuardRuleType = "SLIDING_WINDOW"
	// GuardRuleTypePromptInjection identifies a Guard prompt injection rule.
	GuardRuleTypePromptInjection GuardRuleType = "PROMPT_INJECTION"
	// GuardRuleTypeLocalSensitiveInfo identifies a local sensitive info Guard rule.
	GuardRuleTypeLocalSensitiveInfo GuardRuleType = "LOCAL_SENSITIVE_INFO"
	// GuardRuleTypeLocalCustom identifies a custom local Guard rule.
	GuardRuleTypeLocalCustom GuardRuleType = "LOCAL_CUSTOM"
)

func (GuardRuleType) LogValue

func (g GuardRuleType) LogValue() slog.Value

LogValue implements slog.LogValuer so GuardRuleType logs as its string form.

type GuardSensitiveInfoOptions

type GuardSensitiveInfoOptions struct {
	// Mode controls whether the rule enforces denials or only reports them.
	Mode Mode
	// Allow lists entity types allowed in scanned text.
	Allow []EntityType
	// Deny lists entity types denied in scanned text.
	Deny []EntityType
	// Label identifies this rule in the Arcjet dashboard.
	Label string
	// Metadata is recorded with every invocation of this rule.
	Metadata map[string]string
}

GuardSensitiveInfoOptions configures local Guard sensitive information detection.

type GuardSensitiveInfoResult

type GuardSensitiveInfoResult struct {
	Conclusion          Conclusion   `json:"conclusion"`
	Detected            bool         `json:"detected"`
	DetectedEntityTypes []EntityType `json:"detectedEntityTypes"`
}

GuardSensitiveInfoResult contains Guard sensitive information result details.

type GuardSensitiveInfoRule

type GuardSensitiveInfoRule struct {
	// contains filtered or unexported fields
}

GuardSensitiveInfoRule is a configured local Guard sensitive information rule.

func GuardSensitiveInfo

func GuardSensitiveInfo(opts GuardSensitiveInfoOptions) (*GuardSensitiveInfoRule, error)

GuardSensitiveInfo creates a local Guard sensitive information rule.

func (*GuardSensitiveInfoRule) DeniedResult

DeniedResult returns this rule's sensitive information result if it denied the Guard call, or nil otherwise.

func (*GuardSensitiveInfoRule) Result

Result returns this rule's sensitive information result from the given Guard decision, or nil if the rule did not produce one.

func (*GuardSensitiveInfoRule) Text

Text binds text to scan for one Guard call.

Detection runs locally via the bundled WebAssembly analyzer (the same `arcjet_analyze_js_req` component used by arcjet-js and arcjet-py); the text never leaves the SDK. The submission carries a SHA-256 hash of the text alongside the locally-computed result so the server can correlate inputs without seeing the raw value.

type GuardSlidingWindowOptions

type GuardSlidingWindowOptions struct {
	// Mode controls whether the rule enforces denials or only reports them.
	Mode Mode
	// Interval is the sliding window interval.
	Interval time.Duration
	// MaxRequests is the maximum number of requests per interval.
	MaxRequests int
	// Bucket groups counters for this rule.
	Bucket string
	// Label identifies this rule in the Arcjet dashboard.
	Label string
	// Metadata is recorded with every invocation of this rule.
	Metadata map[string]string
}

GuardSlidingWindowOptions configures a Guard sliding window rule.

type GuardSlidingWindowResult

type GuardSlidingWindowResult struct {
	Conclusion         Conclusion `json:"conclusion"`
	RemainingRequests  int        `json:"remainingRequests"`
	MaxRequests        int        `json:"maxRequests"`
	ResetAtUnixSeconds int64      `json:"resetAtUnixSeconds"`
	IntervalSeconds    int        `json:"intervalSeconds"`
}

GuardSlidingWindowResult contains Guard sliding window result details.

type GuardSlidingWindowRule

type GuardSlidingWindowRule struct {
	// contains filtered or unexported fields
}

GuardSlidingWindowRule is a configured Guard sliding window rule.

func GuardSlidingWindow

func GuardSlidingWindow(opts GuardSlidingWindowOptions) (*GuardSlidingWindowRule, error)

GuardSlidingWindow creates a Guard sliding window rule.

func (*GuardSlidingWindowRule) DeniedResult

DeniedResult returns this rule's sliding window result if it denied the Guard call, or nil otherwise.

func (*GuardSlidingWindowRule) Key

func (r *GuardSlidingWindowRule) Key(key string, requested int) GuardRuleInput

Key binds a sliding window key and requested count for one Guard call.

func (*GuardSlidingWindowRule) Result

Result returns this rule's sliding window result from the given Guard decision, or nil if the rule did not produce one.

type GuardTokenBucketOptions

type GuardTokenBucketOptions struct {
	// Mode controls whether the rule enforces denials or only reports them.
	Mode Mode
	// RefillRate is the number of tokens added per interval.
	RefillRate int
	// Interval is the token refill interval.
	Interval time.Duration
	// Capacity is the maximum bucket size.
	Capacity int
	// Bucket groups counters for this rule.
	Bucket string
	// Label identifies this rule in the Arcjet dashboard.
	Label string
	// Metadata is recorded with every invocation of this rule.
	Metadata map[string]string
}

GuardTokenBucketOptions configures a Guard token bucket rule.

type GuardTokenBucketResult

type GuardTokenBucketResult struct {
	Conclusion            Conclusion `json:"conclusion"`
	RemainingTokens       int        `json:"remainingTokens"`
	MaxTokens             int        `json:"maxTokens"`
	ResetAtUnixSeconds    int64      `json:"resetAtUnixSeconds"`
	RefillRate            int        `json:"refillRate"`
	RefillIntervalSeconds int        `json:"refillIntervalSeconds"`
}

GuardTokenBucketResult contains Guard token bucket result details.

type GuardTokenBucketRule

type GuardTokenBucketRule struct {
	// contains filtered or unexported fields
}

GuardTokenBucketRule is a configured Guard token bucket rule.

func GuardTokenBucket

func GuardTokenBucket(opts GuardTokenBucketOptions) (*GuardTokenBucketRule, error)

GuardTokenBucket creates a Guard token bucket rule.

func (*GuardTokenBucketRule) DeniedResult

DeniedResult returns this rule's token bucket result if it denied the Guard call, or nil otherwise. Useful for reading reset and remaining-token information when returning a "rate limited" response to the caller.

func (*GuardTokenBucketRule) Key

func (r *GuardTokenBucketRule) Key(key string, requested int) GuardRuleInput

Key binds a token bucket key and requested token count for one Guard call.

func (*GuardTokenBucketRule) Result

Result returns this rule's token bucket result from the given Guard decision, or nil if the rule did not produce one.

type IPDetails

type IPDetails struct {
	Latitude       float64           `json:"latitude,omitempty"`
	Longitude      float64           `json:"longitude,omitempty"`
	AccuracyRadius int32             `json:"accuracyRadius,omitempty"`
	Timezone       string            `json:"timezone,omitempty"`
	PostalCode     string            `json:"postalCode,omitempty"`
	City           string            `json:"city,omitempty"`
	Region         string            `json:"region,omitempty"`
	Country        string            `json:"country,omitempty"`
	CountryName    string            `json:"countryName,omitempty"`
	Continent      string            `json:"continent,omitempty"`
	ContinentName  string            `json:"continentName,omitempty"`
	ASN            string            `json:"asn,omitempty"`
	ASNName        string            `json:"asnName,omitempty"`
	ASNDomain      string            `json:"asnDomain,omitempty"`
	ASNType        string            `json:"asnType,omitempty"`
	ASNCountry     string            `json:"asnCountry,omitempty"`
	Service        string            `json:"service,omitempty"`
	IsHosting      bool              `json:"isHosting,omitempty"`
	IsVPN          bool              `json:"isVpn,omitempty"`
	IsProxy        bool              `json:"isProxy,omitempty"`
	IsTor          bool              `json:"isTor,omitempty"`
	IsRelay        bool              `json:"isRelay,omitempty"`
	IsAbuser       bool              `json:"isAbuser,omitempty"`
	Bots           map[string]string `json:"bots,omitempty"`
}

IPDetails contains geolocation, network, and reputation details for a request IP.

type IdentifiedEntity

type IdentifiedEntity struct {
	Type  EntityType `json:"identifiedType,omitempty"`
	Start int        `json:"start,omitempty"`
	End   int        `json:"end,omitempty"`
}

IdentifiedEntity describes a sensitive information entity found in text.

type Mode

type Mode string

Mode controls whether a rule enforces decisions or only reports them.

const (
	// ModeDryRun evaluates a rule without blocking the request or guard call.
	ModeDryRun Mode = "DRY_RUN"
	// ModeLive evaluates a rule and enforces denial decisions.
	ModeLive Mode = "LIVE"
)

func (Mode) LogValue

func (m Mode) LogValue() slog.Value

LogValue implements slog.LogValuer so Mode logs as its string form.

type Platform

type Platform string

Platform names a managed hosting platform whose proxy headers Arcjet can trust to determine the client IP. Set Config.Platform to one of these to select a platform explicitly when its environment isn't auto-detected — most importantly a Go service behind the Cloudflare CDN, which does not set the CF_PAGES variable detectPlatform looks for. The names mirror the platform values accepted by arcjet-js's @arcjet/ip.

const (
	PlatformFirebase   Platform = "firebase"
	PlatformFlyIo      Platform = "fly-io"
	PlatformVercel     Platform = "vercel"
	PlatformRender     Platform = "render"
	PlatformCloudflare Platform = "cloudflare"
	PlatformRailway    Platform = "railway"
)

type PromptInjectionOptions

type PromptInjectionOptions struct {
	// Mode controls whether the rule enforces denials or only reports them.
	Mode Mode
}

PromptInjectionOptions configures prompt injection detection.

Arcjet no longer exposes a prompt injection threshold; use Mode to enforce or dry-run the rule.

type PromptInjectionReason

type PromptInjectionReason struct {
	Detected    bool `json:"injectionDetected,omitempty"`
	TotalTokens int  `json:"totalTokens,omitempty"`
}

PromptInjectionReason contains prompt injection detection results.

type ProtectDetails

type ProtectDetails struct {
	// IP is the request source IP address.
	IP string
	// Method is the HTTP method.
	Method string
	// Protocol is the HTTP protocol string.
	Protocol string
	// Host is the request host.
	Host string
	// Path is the URL path.
	Path string
	// Headers are request headers keyed by lowercase header name.
	Headers map[string]string
	// Body is an optional request body override.
	Body []byte
	// Email is the email address used by ValidateEmail.
	Email string
	// Cookies is the raw Cookie header.
	Cookies string
	// Query is the raw URL query, with or without a leading question mark.
	Query string
	// Extra contains additional string fields sent to Arcjet.
	Extra map[string]string
}

ProtectDetails is the request data Arcjet evaluates.

Use DetailsFromRequest or Client.Protect for ordinary HTTP handlers. Construct ProtectDetails directly when protecting a non-standard request source.

func DetailsFromRequest

func DetailsFromRequest(r *http.Request) ProtectDetails

DetailsFromRequest extracts Arcjet request details from an HTTP request.

It uses Request.RemoteAddr for the source IP. Configure Config.Proxies and use Client.Protect when Arcjet should trust X-Forwarded-For from known proxies, or when running on a supported hosting platform (Fly.io, Vercel, Render, Firebase, Railway) where Client.Protect reads the platform's signed headers.

type ProtectOption

type ProtectOption func(*ProtectOptions)

ProtectOption configures a single Client.Protect or Client.ProtectDetails call.

func WithBody

func WithBody(body []byte) ProtectOption

WithBody overrides the request body sent to Arcjet.

func WithCharacteristic

func WithCharacteristic(key, value string) ProtectOption

WithCharacteristic sets a single rate-limit characteristic value. It merges with any prior WithCharacteristic or WithCharacteristics call.

func WithCharacteristics

func WithCharacteristics(values map[string]string) ProtectOption

WithCharacteristics sets values for rate-limit characteristics declared by rules.

func WithDetectPromptInjectionMessage

func WithDetectPromptInjectionMessage(s string) ProtectOption

WithDetectPromptInjectionMessage sets the text scanned by prompt injection detection.

func WithEmail

func WithEmail(email string) ProtectOption

WithEmail sets the email address scanned by email validation.

func WithExtra

func WithExtra(extra map[string]string) ProtectOption

WithExtra sets additional string fields sent to Arcjet with the request.

func WithFilterLocal

func WithFilterLocal(fields map[string]string) ProtectOption

WithFilterLocal sets local-only values available to Filter expressions.

Values are evaluated by local WebAssembly and are not sent to Arcjet Cloud.

func WithIPSrc

func WithIPSrc(ip string) ProtectOption

WithIPSrc overrides the request source IP sent to Arcjet.

func WithRequested

func WithRequested(n int) ProtectOption

WithRequested sets the token or request cost consumed by this request.

func WithSensitiveInfoValue

func WithSensitiveInfoValue(s string) ProtectOption

WithSensitiveInfoValue sets the text scanned by sensitive information detection. Pair with SensitiveInfo; the value is evaluated locally and never leaves the SDK.

type ProtectOptions

type ProtectOptions struct {
	// Requested is the token or request cost consumed by this request.
	Requested int
	// Characteristics are per-request rate-limit characteristic values.
	Characteristics map[string]string
	// DetectPromptInjectionMessage is text scanned by prompt injection detection.
	DetectPromptInjectionMessage string
	// SensitiveInfoValue is text scanned by sensitive information detection.
	SensitiveInfoValue string
	// Email is the email address scanned by ValidateEmail.
	Email string
	// IPSrc overrides the request source IP.
	IPSrc string
	// FilterLocal contains local-only fields for Filter expressions.
	FilterLocal map[string]string
	// Extra contains additional string fields sent to Arcjet.
	Extra map[string]string
	// Body overrides the request body sent to Arcjet.
	Body []byte
}

ProtectOptions contains per-request inputs used by specific rules.

Most callers set these with ProtectOption helpers such as WithRequested and WithEmail.

type ProtectSignupOptions

type ProtectSignupOptions struct {
	// RateLimit configures the sliding-window rate limit applied to the signup
	// form, typically keyed on the source IP.
	RateLimit SlidingWindowOptions
	// Bots configures bot detection for the signup form.
	Bots BotOptions
	// Email configures validation of the submitted email address.
	Email EmailOptions
}

ProtectSignupOptions configures the ProtectSignup composite rule.

type RateLimitReason

type RateLimitReason struct {
	Max             int `json:"max,omitempty"`
	Remaining       int `json:"remaining,omitempty"`
	ResetInSeconds  int `json:"resetInSeconds,omitempty"`
	WindowInSeconds int `json:"windowInSeconds,omitempty"`
}

RateLimitReason contains details for a rate limit decision.

type Reason

type Reason struct {
	Type    ReasonType
	Message string

	RateLimit       *RateLimitReason
	Bot             *BotReason
	Shield          *ShieldReason
	Email           *EmailReason
	SensitiveInfo   *SensitiveInfoReason
	PromptInjection *PromptInjectionReason
	Filter          *FilterReason
}

Reason contains typed details about why Arcjet reached a decision.

func (Reason) IsBot

func (r Reason) IsBot() bool

IsBot reports whether a bot detection rule drove this reason.

func (Reason) IsEmail

func (r Reason) IsEmail() bool

IsEmail reports whether email validation drove this reason.

func (Reason) IsError

func (r Reason) IsError() bool

IsError reports whether an error drove this reason.

func (Reason) IsFilter

func (r Reason) IsFilter() bool

IsFilter reports whether a request filter drove this reason.

func (Reason) IsPromptInjection

func (r Reason) IsPromptInjection() bool

IsPromptInjection reports whether prompt injection detection drove this reason.

func (Reason) IsRateLimit

func (r Reason) IsRateLimit() bool

IsRateLimit reports whether a rate limit rule drove this reason.

func (Reason) IsSensitiveInfo

func (r Reason) IsSensitiveInfo() bool

IsSensitiveInfo reports whether sensitive info detection drove this reason.

func (Reason) IsShield

func (r Reason) IsShield() bool

IsShield reports whether Shield drove this reason.

type ReasonType

type ReasonType string

ReasonType identifies the rule family or condition behind a decision.

const (
	// ReasonUnknown is used when a response does not include a known reason.
	ReasonUnknown ReasonType = ""
	// ReasonRateLimit means a rate limit rule determined the result.
	ReasonRateLimit ReasonType = "RATE_LIMIT"
	// ReasonBot means a bot detection rule determined the result.
	ReasonBot ReasonType = "BOT"
	// ReasonShield means Shield determined the result.
	ReasonShield ReasonType = "SHIELD"
	// ReasonEmail means an email validation rule determined the result.
	ReasonEmail ReasonType = "EMAIL"
	// ReasonSensitiveInfo means a sensitive information rule determined the result.
	ReasonSensitiveInfo ReasonType = "SENSITIVE_INFO"
	// ReasonPromptInjection means a prompt injection rule determined the result.
	ReasonPromptInjection ReasonType = "PROMPT_INJECTION"
	// ReasonFilter means a request filter rule determined the result.
	ReasonFilter ReasonType = "FILTER"
	// ReasonError means the decision contains an error.
	ReasonError ReasonType = "ERROR"
	// ReasonNotRun means a guard rule did not run.
	ReasonNotRun ReasonType = "NOT_RUN"
	// ReasonCustom means a custom guard rule determined the result.
	ReasonCustom ReasonType = "CUSTOM"
)

func (ReasonType) LogValue

func (r ReasonType) LogValue() slog.Value

LogValue implements slog.LogValuer so ReasonType logs as its string form.

type Rule

type Rule interface {
	// contains filtered or unexported methods
}

Rule is a request protection rule evaluated by Client.Protect.

func DetectBot

func DetectBot(opts BotOptions) Rule

DetectBot creates a bot detection rule.

func DetectPromptInjection

func DetectPromptInjection(opts PromptInjectionOptions) Rule

DetectPromptInjection creates a prompt injection detection rule.

func Filter

func Filter(opts FilterOptions) Rule

Filter creates a request filter rule.

Local filter values passed with WithFilterLocal are available to expressions as local["name"].

func FixedWindow

func FixedWindow(opts FixedWindowOptions) Rule

FixedWindow creates a fixed window rate limit rule.

func ProtectSignup

func ProtectSignup(opts ProtectSignupOptions) []Rule

ProtectSignup bundles the rules commonly used to protect a signup form: a sliding-window rate limit, bot detection, and email validation. It is sugar over SlidingWindow, DetectBot, and ValidateEmail; the returned rules can be passed directly to Client.Protect alongside any others. Mirrors protectSignup in arcjet-js.

func SensitiveInfo

func SensitiveInfo(opts SensitiveInfoOptions) Rule

SensitiveInfo creates a sensitive information detection rule. The text to scan comes from WithSensitiveInfoValue on each Protect call.

Detection runs locally via the bundled WebAssembly analyzer (the same `arcjet_analyze_js_req` component used by the JavaScript and Python SDKs) — the text never leaves the SDK.

func Shield

func Shield(opts ShieldOptions) Rule

Shield creates a rule that protects against common web attacks.

func SlidingWindow

func SlidingWindow(opts SlidingWindowOptions) Rule

SlidingWindow creates a sliding window rate limit rule.

func TokenBucket

func TokenBucket(opts TokenBucketOptions) Rule

TokenBucket creates a token bucket rate limit rule.

Token buckets are useful for AI token budgets because callers can pass the consumed token count with WithRequested.

func ValidateEmail

func ValidateEmail(opts EmailOptions) Rule

ValidateEmail creates an email validation rule.

type RuleResult

type RuleResult struct {
	RuleID     string
	State      RuleState
	Conclusion Conclusion
	Reason     Reason
	// TTL is the number of seconds the per-rule result can be cached.
	TTL         int
	Fingerprint string
}

RuleResult is the per-rule result included in a request decision.

type RuleState

type RuleState string

RuleState is the lifecycle state of a per-rule evaluation in a Decision.

const (
	// RuleStateUnspecified means no rule state was provided.
	RuleStateUnspecified RuleState = ""
	// RuleStateRun means the rule was evaluated this request.
	RuleStateRun RuleState = "RULE_STATE_RUN"
	// RuleStateDryRun means the rule was evaluated but not enforced.
	RuleStateDryRun RuleState = "RULE_STATE_DRY_RUN"
	// RuleStateNotRun means the rule did not run.
	RuleStateNotRun RuleState = "RULE_STATE_NOT_RUN"
	// RuleStateCached means the rule result was served from cache.
	RuleStateCached RuleState = "RULE_STATE_CACHED"
)

func (RuleState) LogValue

func (s RuleState) LogValue() slog.Value

LogValue implements slog.LogValuer so RuleState logs as its string form.

type SensitiveInfoDetect

type SensitiveInfoDetect func(ctx context.Context, tokens []string) []EntityType

SensitiveInfoDetect classifies tokens that the bundled wasm analyzer didn't recognise. The returned slice must have one entry per input token; an empty EntityType leaves the token unclassified, otherwise the value is recorded — either a built-in constant (SensitiveInfoEmail, SensitiveInfoPhoneNumber, …) or any custom label.

type SensitiveInfoOptions

type SensitiveInfoOptions struct {
	// Mode controls whether the rule enforces denials or only reports them.
	Mode Mode
	// Allow lists entity types allowed in scanned text.
	Allow []EntityType
	// Deny lists entity types denied in scanned text.
	Deny []EntityType
}

SensitiveInfoOptions configures request sensitive information detection.

Allow and Deny are mutually exclusive. Pass text for each request with WithSensitiveInfoValue.

type SensitiveInfoReason

type SensitiveInfoReason struct {
	Allowed []IdentifiedEntity `json:"allowed,omitempty"`
	Denied  []IdentifiedEntity `json:"denied,omitempty"`
}

SensitiveInfoReason contains sensitive information detection results.

type ShieldOptions

type ShieldOptions struct {
	// Mode controls whether the rule enforces denials or only reports them.
	Mode Mode
	// Characteristics are optional keys associated with Shield evaluation.
	Characteristics []string
}

ShieldOptions configures Arcjet Shield.

type ShieldReason

type ShieldReason struct {
	Triggered  bool `json:"shieldTriggered,omitempty"`
	Suspicious bool `json:"suspicious,omitempty"`
}

ShieldReason contains details for a Shield decision.

type SlidingWindowOptions

type SlidingWindowOptions struct {
	// Mode controls whether the rule enforces denials or only reports them.
	Mode Mode
	// Characteristics are rate-limit keys such as "userId".
	Characteristics []string
	// Interval is the sliding window interval.
	Interval time.Duration
	// MaxRequests is the maximum number of requests per interval.
	MaxRequests int
}

SlidingWindowOptions configures a sliding window rate limit rule.

type TokenBucketOptions

type TokenBucketOptions struct {
	// Mode controls whether the rule enforces denials or only reports them.
	Mode Mode
	// Characteristics are rate-limit keys such as "userId".
	Characteristics []string
	// RefillRate is the number of tokens added per interval.
	RefillRate int
	// Interval is the token refill interval.
	Interval time.Duration
	// Capacity is the maximum bucket size.
	Capacity int
}

TokenBucketOptions configures a token bucket rate limit rule.

type WasmModule

type WasmModule struct {
	// contains filtered or unexported fields
}

WasmModule is a small helper for executing raw WebAssembly modules with wazero.

Most applications do not need this type; request rules use the SDK's bundled analyzers directly.

func NewWasmModule

func NewWasmModule(ctx context.Context, wasm []byte) (*WasmModule, error)

NewWasmModule instantiates a raw WebAssembly module.

func (*WasmModule) Call

func (m *WasmModule) Call(ctx context.Context, name string, params ...uint64) ([]uint64, error)

Call invokes an exported WebAssembly function by name.

func (*WasmModule) Close

func (m *WasmModule) Close(ctx context.Context) error

Close releases the WebAssembly runtime and module.

Directories

Path Synopsis
internal
Package redact detects and redacts sensitive information — emails, phone numbers, IP addresses, credit card numbers, and custom entities — in arbitrary text.
Package redact detects and redacts sensitive information — emails, phone numbers, IP addresses, credit card numbers, and custom entities — in arbitrary text.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL