Skip to content
IQ Routing

Security

Last reviewed: July 3, 2026.

This page covers the per-org security controls an admin tunes from the Settings → Security pane: the PII redactor inline path that strips sensitive content out of the prompt before it leaves the gateway, the anomaly rate-limit that caps how loudly the detector can fire, the access scoping that gates every admin surface, and the multi-region data-residency control that pins the org's region.

The audience is an org admin tuning security plus data-residency posture. Each section names the control the admin flips, the gateway behaviour the flip changes, and the roadmap work that will close the section out.

PII redactor inline path

Status: Roadmap. The gateway PII redactor described in this section is on the roadmap; it is not live at the request boundary in production today. The section documents the per-org control surface as it is designed.

The PII redactor is designed to run inline on the request path so an org with the inline flag enabled redacts the prompt before it leaves the gateway, then re-injects the original spans on the response stream so the model's reply still references the user's actual data.

The two flags

The redactor reads two per-org settings:

  • A redaction mode: one of off, redact_in_transit, or redact_and_log. off skips the redactor entirely.
  • An inline-enabled gate. The redactor only runs when the mode is not off AND inline is enabled.

The two-flag design lets an org stage the rollout: set the mode to redact_and_log first to confirm the redactor finds the right spans without changing the prompt that goes upstream, then enable inline once the audit trail looks correct.

The two modes

redact_in_transit rewrites the prompt to replace each PII span with a placeholder token (<PII:EMAIL_0>, <PII:PHONE_1>, <PII:NAME_2>), stashes the original spans server-side with a 60-second TTL, and re-injects the original spans into the streaming response so the user sees their actual data in the model's reply. The model never sees the PII; the user never sees the placeholders.

redact_and_log rewrites the prompt the same way but skips the re-injection. The user sees the placeholders in the response. This mode is for high-sensitivity workflows where the PII must not round-trip even into the response stream.

The 60-second TTL

The span map lives for exactly 60 seconds. A redactor run that takes longer than 60 seconds loses the spans and the response surfaces the placeholder tokens instead of the original data. The 60s budget is generous for the typical model latency (single-digit seconds) but tight enough that a stalled response cannot leak spans into a future request's window.

The request id that keys the span map is server-generated and is never read from a client-controlled header, so an attacker cannot forge one to read another user's span map.

The latency-budget gate

The redactor wraps a transformer-backed entity recogniser, which has a warm-path latency around 50ms but can spike on long prompts. A per-request wall-clock budget (default 300ms) caps the redactor's time. On timeout, the redactor fails open (the request continues with the unredacted prompt) and writes an audit row carrying the entity count and types only (never the unredacted content).

The fail-open default is the safer of the two failure modes for an org that depends on the gateway for chat latency. An org that prefers fail-closed semantics can set the mode to off until the latency stabilises.

On the roadmap

  • A per-org fail-mode control so an org can opt into fail-closed semantics without turning the mode entirely off.
  • A per-team override on the redaction mode so a team can be more aggressive than the org default without forcing every team to match.
  • A live latency p95 plus p99 readout on the dashboard tile so an admin can spot a slow cold start before it triggers the budget gate.

Anomaly rate-limit

Status: Live today. The per-org anomaly rate-limit and its anomaly_rate_threshold alert-rule are in force in production.

The anomaly detector emits one event for every detection. A misconfigured detector or a real spike can flood the store with thousands of events per minute, which both costs storage and drowns the alert-rule evaluator. The per-org rate-limit caps the emission rate.

The per-org cap

A per-org rate limit (default 60 per minute) caps how many anomaly events the detector can write per minute. The value cannot be set below 1, so a misconfigured 0 cannot silence the detector entirely.

When the cap is hit, the detector still writes the event but flips a rate_limited flag to true so the dashboard can render a "Rate limited" badge on the row. The flip preserves the audit trail (the detector did fire) without fanning the alert evaluator out to every downstream rule.

The alert-rule type

An anomaly_rate_threshold alert-rule fires when the count of anomaly events in a rolling window exceeds a configured threshold. The rule config carries two keys:

{
  "window_hours": 1,
  "threshold":   50
}

window_hours defaults to 1 and threshold defaults to 50. The dashboard form constrains the config shape to exactly those two keys; an attempt to inject any other key is rejected at the submit handler.

The cooldown

Every alert-rule carries a cooldown that prevents the same rule from firing twice in quick succession. The anomaly_rate_threshold rule defaults to a 15-minute cooldown. The cooldown is enforced server-side; even if the dashboard form submits a zero cooldown, the validator rejects it so a misconfigured rule cannot ladder.

On the roadmap

  • A per-rule plus per-recipient cooldown so the same rule can fire immediately to the on-call SIEM but every 60 minutes to the Slack channel.
  • A per-detector type rate-limit (separate budgets for the cost spike, the latency spike, and the error-rate spike detectors) so one noisy detector cannot exhaust the org-wide cap.
  • An "anomaly digest" alert-rule type that batches the rate-limited rows into a single notification at the cooldown boundary so the alert recipient sees one rolled-up message instead of N rate-limited badges in the dashboard.

Access scoping

Status: Live today. Org-scoped enumeration and operator gating are in force in production. The granular per-role permission catalogue described under "On the roadmap" below is not yet in force.

Every admin surface enforces two access controls that are live today: org-scoped enumeration and operator gating. The granular per-role permission catalogue (a Member role that cannot reveal a key even when it reaches the route) is on the roadmap, not in force yet.

Org-scoped enumeration

Every admin route resolves the caller's active org and masks the result set to that org. An admin with credentials for org A cannot enumerate, read, or mutate org B's keys, audit rows, billing, or settings; the query is scoped to the caller's org before it runs, so a cross-org probe returns the caller's own data or a 404, never another org's. This is the load-bearing tenancy boundary and it is enforced on every admin handler.

Operator gating

A small set of platform-operator surfaces (the cross-org and fleet-wide views) gate on an operator flag carried on the session. A non-operator session that scripts the endpoint directly hits a 403 before the handler runs, so the gate is enforced server-side, not just hidden in the UI.

Role assignments

The Settings → Members pane shows each member's role for display. Role assignment is not yet an enforcement input: the pane renders roles read-only and surfaces a "Role assignments unavailable" notice. Today the real boundary is the org scope plus the operator gate above, not the displayed role.

On the roadmap

  • A per-role permission catalogue so a Member role cannot reveal an API key even when the route handler is reached, with a per-route guard backing every admin surface.
  • A role-assignment audit row on every grant and revocation so the Members pane changes flow into the compliance evidence trail.
  • Editable role assignment from the Members pane once the catalogue and the per-route guard land.

Multi-region scaffolding

Status: Roadmap. The data-residency control surface records an org's intended region today, but provider-pinned data-residency (routing an eu org only to eu-region providers) is on the roadmap and not in force yet.

The current build lands the data-residency control surface plus the read-side resolution so a later release can wire region-pinned providers without re-architecting. The region currently records the org's intended residency; provider pinning is on the roadmap.

The region values

An org's data-residency region is one of three values: us, eu, or apac. The default is us, which keeps every existing org on the US region until an admin sets the value via the Settings → Security pane. A fourth value is rejected, so a typo cannot land a rogue region.

Defaulting

The region resolution defaults to us whenever the stored value is missing or empty, so the read path is safe to call across a deploy window before every org has a region set.

On the roadmap

  • Region-pinned routing so an eu org only routes to providers whose region matches.
  • A per-team override on the region so a team in a US-default org can opt into EU residency without forcing every team to match.
  • A region-aware webhook deliverer so a US-default org with a Frankfurt receiver routes the delivery through the EU egress pool rather than the US pool.
  • A region-pinned audit-log composer so an evidence pack only carries rows from the region the auditor is scoped to.

PII customer-scoped enablement

Status: Roadmap. These controls are part of the gateway PII redactor, which is on the roadmap and not live at the request boundary today.

The redactor's latency budget, the deep-PII inspection (DPI) toggle, and the re-injection policy are per-org controls so an org can opt into a wider budget plus the DPI transformer plus a tighter re-injection policy without a process restart. The inline path reads the current values on every request, so a settings change lands on the very next request.

The three controls

  • A per-org latency budget (default 300ms): the wall-clock bound the inline path hands the redactor.
  • A per-org DPI opt-in (default off): enables the transformer-model recogniser for deeper detection.
  • A per-org re-injection policy (default always): controls how the response wrapper re-injects the original spans.

The latency budget override

An org that pegs the budget at 100ms gets fail-open behaviour on every prompt that exceeds 100ms; an org that widens to 3000ms gets the full transformer-model headroom. The dashboard clamps the input between 50ms and 3000ms to protect the operator UI from typos and runaway values. A submission outside the accepted range is rejected at the validator boundary.

The DPI lazy load

The DPI toggle gates the transformer-model recogniser. When on, the redactor loads the analyzer on the first request for the org and caches it for the lifetime of the process; the cold-start adds 200-500ms on the very first call but every subsequent request reads the cached analyzer.

The default (off) means an org that never opts in never pays the cold-start cost. A process that boots with zero DPI-enabled orgs never instantiates the transformer at all.

The re-injection policy

The re-injection policy takes one of three values: always, trusted_only, or never. Any other value is rejected so a misconfigured admin endpoint cannot land a rogue policy.

always is the default and matches the inline contract: the response wrapper re-injects every per-request PII span so the user sees their original data in the model's reply. trusted_only re-injects only when the response originated from a trusted endpoint; otherwise the placeholders stay in the response. never skips the re-injection entirely so the response stream carries the placeholders.

The Settings → Security pane exposes the three values and carries an inline caption naming the default-on contract plus the latency trade-off for the DPI toggle.

Anomaly detector extended heuristics

Status: Live today. The base heuristic, the five extended heuristics, and the per-type rate-limit windows are in force in production.

The anomaly detector fires one base heuristic (thinking_budget_excess) plus emits one event per detection. The detector is extended with five new heuristics covering request-rate spikes, cost-rate spikes, failed-auth bursts, key-vault-reveal bursts, plus webhook failure streaks. Each event carries a discrete anomaly type so the dashboard can index-seek per type instead of scanning a free-form reason field.

The five new types

The five new heuristics each emit an event tagged with a discrete anomaly type:

  • request_rate_spike: fires when the per-org request count over a rolling 5-minute window exceeds 5x the 7-day p95 baseline.
  • cost_rate_spike: fires when the per-org cost rate over a rolling 5-minute window exceeds 5x the 7-day p95 baseline.
  • failed_auth_burst: fires when the per-org failed-auth count over a rolling 1-minute window crosses 10 (a fixed threshold, no per-org baseline because failed auth is rare enough that 10 in one minute is anomalous for every org).
  • key_vault_reveal_burst: fires when the per-org key-reveal count over a rolling 1-minute window crosses 5. The threshold is tighter than failed_auth_burst because a legitimate operator rarely reveals more than one or two keys in a minute.
  • webhook_failure_streak: fires when the per-org webhook delivery log shows 10 consecutive failures with no successes interleaved, walking back from the most recent delivery.

Per-type rate-limit windows

The per-org rate-limit caps the org-wide emission rate. The cap extends into per-type windows so one noisy heuristic cannot exhaust the org-wide budget. The detector maintains a per-type bucket alongside the org-wide bucket; a detection that would exhaust the per-type bucket flips the rate_limited flag on the emitted event but writes the event anyway so the audit trail stays complete.

The per-type windows are tuned to the heuristic's natural cadence:

  • request_rate_spike and cost_rate_spike cap at 12 per hour because a 5x-over-p95 condition rarely sustains beyond the rolling window.
  • failed_auth_burst and key_vault_reveal_burst cap at 60 per hour because a sustained attack can burst-fire across multiple minutes.
  • webhook_failure_streak caps at 12 per hour because a streak resolves to one detection until the streak clears.

Per-type alert-rule integration

The anomaly_rate_threshold alert-rule fires when the count of anomaly events in a rolling window exceeds a configured threshold. The rule config carries an optional anomaly_type filter so an operator can wire one alert rule per heuristic (or one rule covering every type):

{
  "window_hours":  1,
  "threshold":     5,
  "anomaly_type":  "failed_auth_burst"
}

The dashboard form exposes the anomaly_type field as an optional dropdown; an operator who leaves the field empty gets the org-wide rule shape the base surface already supports. An operator who pins a value gets a per-heuristic rule that only fires on events whose type matches.

The 15-minute cooldown carries forward; the per-type rule inherits it unless the operator overrides.

On the roadmap

  • A per-detector exponential backoff so a heuristic that fires plus rate-limits in close succession backs off the bucket refill rate for the next window. The current buckets refill linearly; a malicious actor who learns the window can pace the attack to the cap.
  • A per-heuristic alert digest that batches the rate-limited events into a single notification at the cooldown boundary.
  • Full audit-chain coverage on the anomaly events store so the verifier no longer skips any rows (the flip is currently parked).