Sensitive information reference
Arcjet Sensitive Information Detection protects against clients sending you sensitive information such as PII that you do not wish to handle. All detection runs locally in your own environment through a pluggable detection backend: a built-in WebAssembly engine (the default) or the on-device Rampart NER model, which detects names, addresses, and government or financial identifiers in addition to the structured types.
Configuration
Section titled “Configuration”Sensitive information detection is configured by allowing or denying a subset of
sensitive information. The allow and deny lists are mutually exclusive. With
allow, Arcjet denies any detected sensitive information that the list does not
name. With deny, Arcjet allows any detected sensitive information that the
list does not name.
The arcjet client can be configured with one or more Sensitive information
rules. In JavaScript / TypeScript these are constructed with
sensitiveInfo(options: SensitiveInfoOptionsAllow | SensitiveInfoOptionsDeny).
In Python they are constructed with the detect_sensitive_info(...) factory.
allow and deny are mutually exclusive in both languages.
The optional backend selects which detection backend
runs – omit it to use the built-in engine, or pass rampart() for on-device NER
detection.
type SensitiveInfoOptionsAllow = { mode?: "LIVE" | "DRY_RUN"; allow?: Array<ArcjetSensitiveInfoType>; contextWindowSize?: number; // You can also provide a custom detection function to detect other types // of sensitive information (see below). detect?: (tokens: string[]) -> Array<SensitiveInfoType | undefined>; // Optionally swap the default WebAssembly detection engine for a custom // backend, such as the on-device Rampart NER model (see below). backend?: SensitiveInfoBackend;};type SensitiveInfoOptionsDeny = { mode?: "LIVE" | "DRY_RUN"; deny?: Array<ArcjetSensitiveInfoType>; contextWindowSize?: number; // You can also provide a custom detection function to detect other types // of sensitive information (see below). detect?: (tokens: string[]) -> Array<SensitiveInfoType | undefined>; // Optionally swap the default WebAssembly detection engine for a custom // backend, such as the on-device Rampart NER model (see below). backend?: SensitiveInfoBackend;};# Signature for arcjet.detect_sensitive_infodef detect_sensitive_info( *, # Required. Mode.LIVE blocks requests; Mode.DRY_RUN logs only. mode: Mode, # Entity types to allow (report only). Mutually exclusive with `deny`. allow: Sequence[str | SensitiveInfoEntityType] = (), # Entity types to deny. Mutually exclusive with `allow`. deny: Sequence[str | SensitiveInfoEntityType] = (), # Optional context window size for detection context_window_size: int | None = None, # You can also provide a custom detection function to detect other types # of sensitive information (see below). detect: Callable[[list[str]], Sequence[str | None]] | None = None, # Optionally swap the default WebAssembly detection engine for a custom # backend, such as the on-device Rampart NER model (see below). backend: SensitiveInfoBackend | None = None, # Rate-limit characteristics (see https://docs.arcjet.com/fingerprints) characteristics: Sequence[str] = (),) -> SensitiveInfoDetection: ...
# Entity types the default (WASM) backend detects natively# (SensitiveInfoEntityType enum members):# - SensitiveInfoEntityType.EMAIL# - SensitiveInfoEntityType.PHONE_NUMBER# - SensitiveInfoEntityType.IP_ADDRESS# - SensitiveInfoEntityType.CREDIT_CARD_NUMBER## The enum also defines backend-only types (GIVEN_NAME, SURNAME, SSN, URL,# TAX_ID, and more — see the entity detection table below). Listing one without# a `backend` that supports it — such as `rampart()` — raises an error.type ArcjetSensitiveInfoType = | "EMAIL" | "PHONE_NUMBER" | "IP_ADDRESS" | "CREDIT_CARD_NUMBER" | "GIVEN_NAME" | "SURNAME" | "SSN" | "URL" | "TAX_ID" | "BANK_ACCOUNT" | "ROUTING_NUMBER" | "GOVERNMENT_ID" | "PASSPORT" | "DRIVERS_LICENSE" | "BUILDING_NUMBER" | "STREET_NAME" | "SECONDARY_ADDRESS" | "CITY" | "STATE" | "ZIP_CODE";Detection backends
Section titled “Detection backends”Sensitive information detection runs through a detection backend – the
engine that scans the request body and identifies entities. The rest of the rule
(mode, allow/deny, and the result shape) is the same whichever backend you
choose. For exactly what each backend detects, see Entity
detection.
Built-in engine
Section titled “Built-in engine”By default – when you don’t set a backend – the rule runs a WebAssembly engine
that detects four structured types: email addresses, phone numbers, IP
addresses, and credit card numbers. It runs anywhere the SDK runs, including edge
runtimes, needs no extra dependencies, and adds negligible latency, so it’s the
right choice for most applications. Extend it with a
custom detect function when you need to match your
own patterns.
On-device detection with Rampart
Section titled “On-device detection with Rampart”For broader coverage, an optional Rampart package provides a backend powered by the on-device Rampart named-entity-recognition (NER) model. It runs a ~15 MB quantized ONNX model locally so the rule can detect names, addresses, and government or financial identifiers in addition to the built-in structured types. Everything runs in your own environment – the model weights are bundled with the package, so nothing is fetched at runtime and no data leaves your infrastructure.
Install the package alongside your Arcjet SDK. In Python it ships as an optional
extra of the arcjet package:
npm install @arcjet/sensitive-info-rampartpip install "arcjet[sensitive-info-rampart]"Then import rampart and pass it as the rule’s backend. Every entity the
model detects is a built-in sensitive info type (ArcjetSensitiveInfoType in
JS, SensitiveInfoEntityType in Python), so you can list the types directly in
allow or deny:
Without a backend, the rule continues to use the built-in engine – this
package is entirely opt-in.
The full set of types the backend supports is exported as rampartEntities
(JS) / rampart_entities (Python), which is convenient when you want to deny
everything it can detect:
import { rampart, rampartEntities } from "@arcjet/sensitive-info-rampart";
sensitiveInfo({ mode: "LIVE", deny: rampartEntities, backend: rampart() });from arcjet import Mode, detect_sensitive_infofrom arcjet_sensitive_info_rampart import rampart, rampart_entities
detect_sensitive_info(mode=Mode.LIVE, deny=rampart_entities, backend=rampart())Model accuracy and performance
Section titled “Model accuracy and performance”Rampart is a compact model – a ~14.7 MB artifact (~18.5M parameters,
quantized to 4-bit weights) with a 512-token context window – so it is fast
enough to run inline on each request. On the Node.js CPU runtime, median (p50)
inference is ~6.6 ms, dropping to ~3.9 ms p50 on WebGPU (set
device: "webgpu" in the JS SDK). The Python backend runs on ONNX
Runtime and defaults to CPU execution; select a
different accelerator with the providers option (described later on this
page).
On the model author’s held-out benchmark it recalls ~98% of private terms across the seven Latin-script languages it supports (English, Spanish, French, German, Italian, Portuguese, and Dutch).
Accuracy varies by input, so it’s worth knowing the trade-offs:
- Non-Latin scripts (for example Chinese, Japanese, Korean, Arabic, Hindi, or Cyrillic) have much lower recall – the model is trained on Latin-script text.
- Government or financial identifiers without a checksum (passports, licenses, case numbers) are recognized less reliably than the structured types the deterministic recognizers validate, such as emails and card numbers.
- Inference runs synchronously in the request path, so this latency is added to each request the rule scans.
These figures come from the Rampart model card, which has the full per-language breakdown, calibration data, and limitations.
Rampart options
Section titled “Rampart options”The rampart() factory accepts an optional configuration object:
import { defaultRecognizers, rampart } from "@arcjet/sensitive-info-rampart";
rampart({ // Execution device (default: "cpu"). Set to "webgpu" to use a GPU when the // runtime supports it. device: "webgpu", // Minimum confidence score for a model token to count (default: 0.5). threshold: 0.6, // Deterministic recognizers to run alongside the model (default: // `defaultRecognizers`). This is the extension point for custom detection // with this backend — add a recognizer that returns matched spans. Pass `[]` // to rely on the model alone. recognizers: [ ...defaultRecognizers, (value) => { // Return an array of `{ start, end, type }` spans. return []; }, ],});The rampart() factory takes an optional RampartOptions:
from arcjet_sensitive_info_rampart import ( RampartOptions, default_recognizers, rampart,)
rampart( RampartOptions( # Minimum confidence score for a model token to count (default: 0.5). threshold=0.6, # ONNX Runtime execution providers (default: ("CPUExecutionProvider",)). # Set this to use a GPU or other accelerator supported by your ONNX # Runtime install. providers=("CPUExecutionProvider",), # Maximum number of characters scanned per request (default: 100,000). # Inference runs synchronously on the request path, so input longer than # this is truncated before detection to bound worst-case CPU cost. max_input_chars=100_000, # Deterministic recognizers to run alongside the model (default: # `default_recognizers`). This is the extension point for custom # detection with this backend — add a recognizer that returns matched # spans. Pass () to rely on the model alone. recognizers=( *default_recognizers, lambda value: [], # Return a list of DetectedSpan(start, end, type). ), # Directory containing the model files (default: the bundled models). model_path=None, ))The model loads once on first use and is reused for every subsequent request.
Bundlers and runtimes
Section titled “Bundlers and runtimes”The Rampart backend loads a native ONNX runtime and reads its bundled model weights from disk at runtime. As a result:
- It requires a server runtime with filesystem and native-addon access. In JS that means Node.js, Bun, or Deno – it does not run on edge runtimes. In Python it runs anywhere the SDK does (Python 3.10+).
- A server build must not bundle it, or the runtime fails to locate the model weights.
In Python, the backend depends on
ONNX Runtime (onnxruntime), which is installed for
you by the arcjet[sensitive-info-rampart] extra. Model inference is
synchronous and blocking, so its latency is added directly to each request the
rule scans – the same is true whether you use the arcjet() or arcjet_sync()
client.
For Next.js, mark the package and its native dependencies as
server external packages
so that Next.js loads them from node_modules at runtime instead of bundling
them. Also make sure any route handler that uses the backend runs on the Node.js
runtime (the default) rather than the Edge runtime:
/** @type {import('next').NextConfig} */const nextConfig = { serverExternalPackages: [ "@arcjet/sensitive-info-rampart", "@huggingface/transformers", "onnxruntime-node", ],};
module.exports = nextConfig;export const runtime = "nodejs";Decision
Section titled “Decision”Arcjet provides a single protect function that is used to execute your
protection rules.
This function returns a Promise that resolves to an
ArcjetDecision object. This contains the following properties:
id(string) – The unique ID for the request. This can be used to look up the request in the Arcjet dashboard. It is prefixed withreq_for decisions involving the Arcjet cloud API. For decisions taken locally, the prefix islreq_.conclusion(ArcjetConclusion) – The final conclusion based on evaluating each of the configured rules. If you wish to accept Arcjet’s recommended action based on the configured rules then you can use this property.reason(ArcjetReason) – An object containing more detailed information about the conclusion.results(ArcjetRuleResult[]) – An array ofArcjetRuleResultobjects containing the results of each rule that was executed.ip(ArcjetIpDetails) – An object containing Arcjet’s analysis of the client IP address. For more information, see the SDK reference.
To check whether a sensitive info rule returned a deny conclusion, use
decision.isDenied() and decision.reason.isSensitiveInfo() (JS) /
decision.is_denied() and decision.reason_v2.type == "SENSITIVE_INFO"
(Python).
You can iterate through the results and check whether a sensitive info rule was applied:
for (const result of decision.results) { console.log("Rule Result", result);}for result in decision.results: print("Rule Result", result)This example logs the full result as well as the sensitive info rule:
Entity detection
Section titled “Entity detection”The entity types available to allow/deny depend on the active detection
backend. Every type in the following list is a built-in
ArcjetSensitiveInfoType, so you can list any of them regardless of backend – a
type that the active backend doesn’t detect never matches. You can also detect
your own types with custom entity detection.
| Entity type | Built-in engine (default) | Rampart backend |
|---|---|---|
EMAIL | ✅ | ✅ Model + recognizer |
PHONE_NUMBER | ✅ | ✅ Model + recognizer |
IP_ADDRESS | ✅ | ✅ Recognizer |
CREDIT_CARD_NUMBER | ✅ | ✅ Recognizer |
URL | – | ✅ Model + recognizer |
SSN | – | ✅ Recognizer |
GIVEN_NAME | – | ✅ Model |
SURNAME | – | ✅ Model |
TAX_ID | – | ✅ Model |
BANK_ACCOUNT | – | ✅ Model |
ROUTING_NUMBER | – | ✅ Model |
GOVERNMENT_ID | – | ✅ Model |
PASSPORT | – | ✅ Model |
DRIVERS_LICENSE | – | ✅ Model |
BUILDING_NUMBER | – | ✅ Model |
STREET_NAME | – | ✅ Model |
SECONDARY_ADDRESS | – | ✅ Model |
CITY | – | ✅ Model |
STATE | – | ✅ Model |
ZIP_CODE | – | ✅ Model |
| Custom | ✅ detect callback | ✅ recognizers option |
For the Rampart backend, Model means the type is detected by the on-device NER model, and Recognizer means it is detected by a deterministic, validated pattern (mirroring Rampart’s deterministic redaction layer). Where the model and a recognizer overlap on the same text, the recognizer wins.
Structured type matching
Section titled “Structured type matching”The structured types are matched the same way by the built-in engine and by the Rampart recognizers.
Card numbers
Section titled “Card numbers”Card numbers can be detected.
The values 4242424242424242, 4000 0566 5566 5556, and 3782-8224-6310005
match but 4242424242424241 does not.
Whether something looks like a card number is based on its initial and final
digits.
The initial digits determine the expected length (min, max) of the total
number.
The final digit is a Luhn
check digit.
Spaces and dashes are ignored because users often group digits.
Email addresses
Section titled “Email addresses”Email addresses can be detected.
The values alice@example.com and bob.smith@subdomain.example.com match but
alice.example.com does not.
A dotless domain (user@localhost) and a domain literal (user@[127.0.0.1],
if it contains a valid IP) also match.
IP addresses
Section titled “IP addresses”IP addresses can be detected.
The values 127.0.0.1 and ::1 match but 012.004.002.000 does not.
IP v4 and v6 addresses are supported.
Whether something looks like an IP address is based on whether it parses
as IpAddr from std::net.
Phone numbers
Section titled “Phone numbers”Phone numbers can be detected.
The values +1 (555) 555-5555 and (020) 334 4522 match but 555-1234 does not.
As phone numbers have a wide variety of sizes and formats,
special short numbers such as 911 or 14 020 are not detected.
Several characters such as dots (.), dashes (-), and spaces are allowed
to separate digit groups.
The country code can be prefixed with plus (+) or omitted,
the next region group can be in parentheses.
Custom entity detection
Section titled “Custom entity detection”When using the built-in engine you can provide a custom
detect function, which lets you detect entities that Arcjet doesn’t support
out of the box using custom logic.
The function takes a list of tokens and must return a list of the same length.
Each element is undefined if the corresponding token is not sensitive, or the
name of the entity if it matches. The number of tokens that are provided to the
function is controlled by the contextWindowSize option, which defaults to 1.
If you need additional context to perform detections then you can increase this
value.
Error handling
Section titled “Error handling”Arcjet is designed to fail open so that a service issue or misconfiguration does not block all requests. The SDK also times out and fails open after 2000 ms by default. However, in most cases, the response time is less than 20 ms to 30 ms.
If there is an error condition when processing the rule, Arcjet returns an
ERROR result for that rule and you can check the message property on the
rule’s error result for more information.
If all other rules that were run returned an ALLOW result, then the final
Arcjet conclusion is ERROR.
Testing
Section titled “Testing”Arcjet runs the same in any environment, including locally and in CI. You can
use the mode set to DRY_RUN to log the results of rule execution without
blocking any requests.
We have an example test framework you can use to automatically test your rules. Arcjet can also be triggered based using a sample of your traffic.
For details, see the Testing section of the docs.