Latch / Docs
filters

Filter reference

Every filter type, its configuration, and what it does when it denies.

A pipeline is an ordered list of filters. Every request through /proxy is evaluated against it, in order, and the first filter to deny short-circuits: the remaining filters do not run, the upstream is never called, and the credential is never used. A denial returns 403 with the filter that denied and its reason.

There are 13 filter types. They fall into three groups by when they run.

Group Filters When
Request endpoint, method, rate_limit, payload, body_size, time_window, ip_allowlist, header_requirement, identity, custom_code, cedar Before the upstream is called. A denial means the request never leaves Latch.
Response response After the upstream replies, before you see it. Can block or redact.
Metered spend_limit In the proxy hot path, against live daily-spend state.

Any filter can be disabled without removing it ("disabled": true), and any filter can be made conditional so it only applies to some requests (see Conditional filters).


endpoint

Restrict which upstream paths the token may reach. Glob patterns: * matches one segment, ** matches any depth.

{ "type": "endpoint", "name": "Completions only", "mode": "allowlist",
  "patterns": ["/v1/chat/completions", "/v1/models/*"] }
Field Type Description
mode allowlist | blocklist Whether patterns permits or forbids.
patterns string[] Glob patterns matched against the request path.

Denies when the path is not in the allowlist, or matches the blocklist.

method

Restrict HTTP verbs. The bluntest read-only control there is.

{ "type": "method", "name": "Read only", "allowed": ["GET", "HEAD"] }
Field Type Description
allowed ("GET"|"POST"|"PUT"|"PATCH"|"DELETE"|"HEAD"|"OPTIONS")[] Permitted verbs.

Denies when the request's method is not listed.

rate_limit

A sliding-window request cap.

{ "type": "rate_limit", "name": "30 per minute",
  "maxRequests": 30, "windowSeconds": 60, "keyBy": "latch" }
Field Type Description
maxRequests integer Requests permitted in the window.
windowSeconds integer Sliding window length.
keyBy latch | ip | json_path What the counter is keyed on.
jsonPath string Required when keyBy is json_path: the field in the request body to key on (for example, per end-user).

Denies when the cap is exceeded, returning 429 with Retry-After.

payload

Assert things about the request body. The most expressive request filter.

{ "type": "payload", "name": "No expensive models", "rules": [
  { "path": "model", "operator": "in", "value": ["gpt-4o-mini", "claude-haiku-4-5"],
    "reason": "This token may only use small models." },
  { "path": "max_tokens", "operator": "less_than_or_equal", "value": 4096 }
]}

Each rule has a path (a dot path into the JSON body), an operator, an optional value, and an optional reason shown in the denial.

Operator Meaning
equals, not_equals Exact match.
in, not_in Membership in value (an array).
exists, not_exists Field presence.
greater_than, greater_than_or_equal Numeric comparison.
less_than, less_than_or_equal Numeric comparison.
matches Regular expression.
max_length, min_length Length bounds on a string or array.
required_fields Every field in value must be present.
type_is The value's JSON type.

Denies when any rule fails, with that rule's reason.

body_size

Cap the request body in bytes. Cheap protection against a runaway or hostile caller.

{ "type": "body_size", "name": "Max 100KB", "maxBytes": 102400 }

Denies when the body exceeds maxBytes.

time_window

Only permit requests during given hours, in a given timezone.

{ "type": "time_window", "name": "Business hours", "timezone": "America/New_York",
  "windows": [{ "days": [1,2,3,4,5], "startTime": "09:00", "endTime": "17:00" }] }
Field Type Description
timezone string IANA zone. Evaluated in this zone, not the caller's.
windows[].days number[] Days of the week, 0 = Sunday.
windows[].startTime / endTime "HH:MM" Window bounds.

Denies when the request falls outside every window.

ip_allowlist

Restrict callers by source IP or CIDR range.

{ "type": "ip_allowlist", "name": "Office only", "allowed": ["203.0.113.0/24", "198.51.100.7"] }

Denies when the caller's IP is in no listed range.

header_requirement

Require headers to be present, optionally matching a pattern. Useful for asserting a tenant id or a correlation header that downstream systems depend on.

{ "type": "header_requirement", "name": "Tenant required",
  "headers": [{ "name": "X-Tenant-Id", "pattern": "^t_[a-z0-9]+$" }] }

Denies when a required header is missing, or present but does not match pattern.

identity

Gate the latch to specific hardware-bound agent keys. The caller must present a signed request slip; Latch verifies the ECDSA-P256 signature against an enrolled key held in a Secure Enclave, so possession of the lat_ token alone is not enough.

{ "type": "identity", "name": "Only Ada's laptop",
  "allowedAgentKids": ["kid_9f3c1a2b", "kid_4d7e8f01"] }
Field Type Description
allowedAgentKids string[] Enrolled agent key ids permitted to use this latch.

Denies when the request carries no valid slip, or the signing key is not in allowedAgentKids. This is what makes a stolen token useless on an unenrolled machine.

custom_code

Run your own policy code. JavaScript executes in an isolated-vm sandbox; Rust compiles to a WASM component and can be evaluated inside the TEE enclave, so even Latch cannot see the request it is deciding on.

{ "type": "custom_code", "name": "Business rules", "language": "javascript",
  "code": "if (ctx.body.amount > 10000) return deny('Over limit'); return allow();" }
Field Type Description
code string The policy source.
language javascript | rust Rust is required for in-enclave evaluation.
fetchToken string Optional token permitting the policy to call out to live external state.

Denies when your code returns a denial.

cedar

Declarative authorization in Cedar. Evaluated in-enclave for TEE latches.

{ "type": "cedar", "name": "Cedar policy",
  "policy": "permit(principal, action == Action::\"POST\", resource) when { context.amount <= 100 };" }

Denies when no permit matches, or a forbid does. Cedar is default-deny.

response

The only filter that runs after the upstream replies. Blocks or redacts the response before it reaches the caller. This is how an upstream that returns more than the caller should see gets contained.

{ "type": "response", "name": "Strip PII", "rules": [{
  "blockStatusCodes": [500, 503],
  "redactPaths": ["$.customers[*].ssn", "$.customers[*].email"],
  "redactMode": "placeholder",
  "maxResponseBytes": 1048576
}]}
Field Type Description
blockStatusCodes number[] Upstream statuses to withhold from the caller.
redactPaths string[] JSON paths to strip from the body.
redactMode remove | null | placeholder | zero How a redacted value is replaced.
maxResponseBytes integer Cap the response size.

Denies when the upstream returns a blocked status or an over-size body. Redaction does not deny; it rewrites.

spend_limit

A daily spend cap, enforced in the proxy hot path against live spend state keyed by latch. Use it to bound what a token can cost you, not just what it can do.

{ "type": "spend_limit", "name": "$50 a day",
  "maxDailyCents": 5000, "pricing": "model_based",
  "modelPricing": { "claude-opus-4-8": { "inputPer1k": 1.5, "outputPer1k": 7.5 } } }
Field Type Description
maxDailyCents integer The daily cap, in cents.
pricing flat | model_based How a request's cost is computed.
costPerRequestCents number Flat cost per request, when pricing is flat.
modelPricing Record<string, { inputPer1k, outputPer1k }> Per-model token pricing, when pricing is model_based.

Denies when the day's spend would exceed maxDailyCents.


Conditional filters

Any filter can carry a condition, so it only applies to matching requests. A filter whose condition does not match is skipped, not denied.

{ "type": "body_size", "name": "Cap uploads", "maxBytes": 102400,
  "condition": { "methods": ["POST", "PUT"], "pathPrefix": "/v1/files", "hasBody": true } }
Field Type Description
methods string[] Only apply to these verbs.
pathPrefix string Only apply below this path.
hasBody boolean Only apply to requests that do (or do not) carry a body.