AI data loss prevention for Go
People often paste personal data into AI prompts – card numbers, phone numbers, home addresses, and whole résumés. Once that data reaches your AI provider, the provider’s logging and retention apply to it, not yours.
Arcjet sensitive info detection scans prompt content inside your application, before it reaches the AI provider. Detection runs locally in your own environment, so the raw text doesn’t leave your app. The SDK reports the decision to Arcjet with the type and position of each finding, but not the matched text. When Arcjet finds something, you decide what happens next: block the request, remove the data, or ask the user to edit their prompt.
Get started
Section titled “Get started”Install the SDK and the Rampart backend:
go get github.com/arcjet/arcjet-go github.com/arcjet/arcjet-go/sensitiveinfo/rampartRampart runs on-device and detects email addresses, phone numbers, IP addresses, card numbers, names, addresses, and government identifiers. For the full list, see On-device detection with Rampart.
package main
import ( "encoding/json" "log" "net/http" "os"
"github.com/arcjet/arcjet-go" "github.com/arcjet/arcjet-go/sensitiveinfo/rampart")
var backend = must(rampart.New(rampart.Options{}))
var aj = must(arcjet.NewClient(arcjet.Config{ Key: os.Getenv("ARCJET_KEY"), Rules: []arcjet.Rule{ arcjet.SensitiveInfo(arcjet.SensitiveInfoOptions{ Mode: arcjet.ModeLive, Deny: []arcjet.EntityType{ arcjet.SensitiveInfoCreditCardNumber, arcjet.SensitiveInfoEmail, }, Backend: backend, }), },}))
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 }
decision, err := aj.Protect( r.Context(), r, arcjet.WithSensitiveInfoValue(body.Message), ) // Fail closed when Arcjet can't be reached. if err != nil { log.Printf("arcjet: %v", err) http.Error(w, "Service unavailable", http.StatusServiceUnavailable) return } if decision.IsDenied() { if decision.Reason.IsSensitiveInfo() { http.Error(w, "Sensitive information detected – remove it from your prompt", http.StatusBadRequest) return } // Block other denials too, such as one from a remote rule. http.Error(w, "Forbidden", http.StatusForbidden) return } // Fail closed when Arcjet couldn't complete the check. if decision.IsErrored() { log.Printf("arcjet: %v", decision.Err()) http.Error(w, "Service unavailable", http.StatusServiceUnavailable) return }
_ = json.NewEncoder(w).Encode(map[string]string{"reply": "..."})}
func main() { http.HandleFunc("/chat", chat) log.Fatal(http.ListenAndServe(":8000", nil))}
func must[T any](v T, err error) T { if err != nil { log.Fatal(err) } return v}How detection works
Section titled “How detection works”Sensitive info detection runs on-device with Rampart, a named-entity-recognition (NER) model paired with deterministic recognizers:
- Deterministic recognizers match structured types with validated patterns – card numbers, email addresses, IP addresses, URLs, and Social Security numbers.
- The NER model detects the free-form PII that people paste into prompts – names, street addresses, phone numbers, and government or financial identifiers. These don’t follow a fixed pattern, so a model finds them where pattern matching can’t.
The SDK rule runs detection on your own infrastructure, so the text isn’t sent to Arcjet or a third party for analysis. That makes it a good fit in front of an AI provider. The server-side sensitive information detector in an agent guard policy and coding agent hooks screen text on Arcjet’s side instead. Use the server-side detector for text longer than the SDK scans, and to detect protected health information (PHI) and secrets such as API keys, which on-device detection doesn’t cover.
Configure detection
Section titled “Configure detection”Choose which PII to block
Section titled “Choose which PII to block”Use deny to list the entity types to block, or allow to block everything
except the types you list (the two are mutually exclusive). Tune the list to
your app – for a support bot that legitimately collects phone numbers, leave
PHONE_NUMBER out of deny:
arcjet.SensitiveInfo(arcjet.SensitiveInfoOptions{ Mode: arcjet.ModeLive, // Blocks requests. Use ModeDryRun to log only. Deny: []arcjet.EntityType{ arcjet.SensitiveInfoCreditCardNumber, arcjet.SensitiveInfoEmail, }, Backend: backend, // Create once with rampart.New})For every entity type and where Arcjet detects it, see Entity types.
Detect names, addresses, and IDs
Section titled “Detect names, addresses, and IDs”The
Rampart
NER model detects names, addresses, and government or financial identifiers.
Add them to Deny. Create the backend once at startup with rampart.New:
package main
import ( "log" "net/http" "os"
"github.com/arcjet/arcjet-go" "github.com/arcjet/arcjet-go/sensitiveinfo/rampart")
var backend = must(rampart.New(rampart.Options{}))
var aj = must(arcjet.NewClient(arcjet.Config{ Key: os.Getenv("ARCJET_KEY"), Rules: []arcjet.Rule{ arcjet.SensitiveInfo(arcjet.SensitiveInfoOptions{ Mode: arcjet.ModeLive, Deny: []arcjet.EntityType{ arcjet.SensitiveInfoGivenName, arcjet.SensitiveInfoSurname, arcjet.SensitiveInfoEmail, arcjet.SensitiveInfoSSN, }, Backend: backend, }), },}))
func handler(w http.ResponseWriter, r *http.Request) { message := r.FormValue("message") decision, err := aj.Protect(r.Context(), r, arcjet.WithSensitiveInfoValue(message)) // Fail closed when Arcjet can't be reached. if err != nil { log.Printf("arcjet: %v", err) http.Error(w, "Service unavailable", http.StatusServiceUnavailable) return } if decision.IsDenied() { if decision.Reason.IsSensitiveInfo() { http.Error(w, "Please remove personal information", http.StatusBadRequest) return } // Block other denials too, such as one from a remote rule. http.Error(w, "Forbidden", http.StatusForbidden) return } // Fail closed when Arcjet couldn't complete the check. if decision.IsErrored() { log.Printf("arcjet: %v", decision.Err()) http.Error(w, "Service unavailable", http.StatusServiceUnavailable) return } w.WriteHeader(http.StatusNoContent)}
func must[T any](v T, err error) T { if err != nil { log.Fatal(err) } return v}The model weights ship with the package (about 15 MB), so nothing is
fetched at runtime. Inference is pure Go and runs on the request path. Rampart
scans up to MaxInputChars characters, 4,096 by default, and the rule denies a
longer message without scanning it. Phone numbers are left to the model,
because their digit shape overlaps with financial and government identifiers.
For options and the full entity list, see the
Rampart reference.
Choose what text to scan
Section titled “Choose what text to scan”Pass the text to scan as sensitiveInfoValue in JavaScript,
sensitive_info_value in Python, or WithSensitiveInfoValue in Go. For a chat
endpoint, this is usually the user’s most recent message. To scan every message,
pass the full conversation history instead – PII can appear earlier in the
exchange.
Test before you block
Section titled “Test before you block”Set mode to "DRY_RUN" in JavaScript, Mode.DRY_RUN in Python, or
ModeDryRun in Go to log detections without blocking requests. Run it in
production to see which PII appears in your prompts, then switch to live mode
when the entity list matches what you want to block.
Combine with other protections
Section titled “Combine with other protections”Sensitive info detection controls what data reaches your AI provider. Pair it with the other AI protection layers, each of which covers a different risk:
- Prompt injection detection blocks hostile instructions and jailbreak attempts.
- AI abuse protection and AI budget control block automated clients and enforce per-user token budgets.
- Agent guards apply PII detection directly inside agent tool handlers and pipelines that don’t route through HTTP. A local policy input keeps the raw string in the SDK while reporting policy evidence to Arcjet.
- Protected health information (PHI) detection finds medical record numbers, health plan member IDs, and vehicle and device identifiers with the server-side detector in an agent guard policy.