Configuration
How settings are given
The proxy is a Spring Boot application. Its settings come from, in increasing precedence:
- The defaults in the image's
application.yaml. - A file of your own, loaded with
SPRING_CONFIG_ADDITIONAL_LOCATION=file:/path/to/application.yaml. - Environment variables.
- Command-line options, such as
--proxy.defaults.policy=support.
An environment variable's name is the setting's name in capitals, with . and [] as _ and hyphens removed: proxy.providers.openai.target is PROXY_PROVIDERS_OPENAI_TARGET, proxy.security.trusted-proxies is PROXY_SECURITY_TRUSTEDPROXIES, and proxy.routes[0].match.model is PROXY_ROUTES_0_MATCH_MODEL.
Two settings also have shorter environment variables, used in the examples:
| Variable | Setting |
|---|---|
PHILTER_AI_PROXY_ENCRYPTION_KEY |
proxy.security.encryption-key |
PHILTER_AI_PROXY_BOOTSTRAP_API_KEY |
proxy.security.bootstrap-api-key |
--validate-config checks the settings without serving requests; see Installation.
Example
proxy:
providers:
bedrock:
region: us-east-1
openai-compatible:
vllm:
target: http://vllm.internal:8000
defaults:
policy: default
routes:
# Scan responses for one model and keep replacements consistent across requests.
- match:
model: gpt-4o
context: support-conversations
outbound:
enabled: true
action: redact
# A stricter policy for one team, chosen by a header their gateway sets.
- match:
header: x-team
value: finance
policy: finance
limits:
max-concurrent-requests: 200
security:
trusted-proxies:
- 10.0.0.0/8
Settings
Security and storage
| Setting | Default | Meaning |
|---|---|---|
proxy.security.encryption-key |
(required) | Encrypts values stored at rest: policies, and each context's replacements. 32 random bytes as 64 hex characters or base64: openssl rand -hex 32. The proxy refuses to start with a key other than the one its database was created with. |
proxy.security.bootstrap-api-key |
An admin key created on first start, while no API key exists. At least 32 characters. | |
proxy.security.proxy-key-header |
x-philter-proxy-key |
The request header that carries the proxy key on provider traffic. It is never forwarded. |
proxy.security.trusted-proxies |
none | Load balancers and proxies (IP addresses or CIDR ranges) whose X-Forwarded-For is believed for the client address in the audit log. The header is read from the right, skipping trusted proxies. |
proxy.storage.sqlite-path |
/var/lib/philter-ai-proxy/philter-ai-proxy.db |
The SQLite database: policies, API keys, contexts, and the audit log. Created readable only by the proxy's user. One instance per file. |
proxy.audit.file |
A file each audit entry is also appended to, as one JSON line. See Audit log. | |
proxy.audit.retention-days |
0 (kept) |
Entries older than this are deleted from the database, once a day. Standard output and the audit file are not pruned. |
Policies and models
| Setting | Default | Meaning |
|---|---|---|
proxy.models.directory |
/opt/philter-ai-proxy/models |
Local PhEye models, one per subdirectory. See Models. |
proxy.policy-files.directories |
none | Directories the files a policy names (such as dictionary files) must be inside. With none, a policy may name no file. |
proxy.policy-files.allowed-environment-variables |
none | Environment variables a policy may name as env:NAME, such as a key for CRYPTO_REPLACE. With none, a policy may name none. |
Defaults and routes
| Setting | Default | Meaning |
|---|---|---|
proxy.defaults.policy |
default |
The policy for a request no route matches. |
proxy.defaults.context |
none | The context for a request no route matches. See Contexts. |
proxy.defaults.outbound.enabled |
false |
Whether responses to requests no route matches are scanned. |
proxy.defaults.outbound.action |
redact |
redact, block, or flag. See Actions. |
proxy.defaults.unsupported-endpoints |
reject |
reject refuses a request with a body to an endpoint the proxy cannot redact; when response scanning is on, a request to an endpoint whose response it cannot scan; and any request to such an endpoint of a provider the proxy authenticates to itself (Vertex AI, Bedrock, and Azure OpenAI with Entra ID). forward sends such requests on unredacted, with responses unscanned. See Supported request types. |
proxy.defaults.media |
reject |
reject refuses a request carrying images, audio, video, or files, which the proxy does not redact; forward sends it on with the media unchanged. See Images, audio, and files are not redacted. |
proxy.routes |
none | Routes, in order; see Routes. |
Limits and streaming
| Setting | Default | Meaning |
|---|---|---|
proxy.limits.max-request-body-bytes |
10485760 (10 MiB) |
Larger request bodies are refused with 413. |
proxy.limits.max-concurrent-requests |
200 |
Requests handled at once; more are refused with 503 and Retry-After: 1. A negative value removes the limit. |
proxy.limits.redaction-timeout-ms |
30000 |
How long redacting one request, or scanning one response or stream window, may take. A request that takes longer is refused with 503 redaction_timeout; a stream is ended with an error. |
proxy.limits.max-response-body-bytes |
33554432 (32 MiB) |
The largest response read to scan it. A larger one is refused with 502 response_too_large. Applies only when response scanning is on; streams are bounded per line, event, and held text instead. |
proxy.streaming.holdback-characters |
256 |
How much of a streamed response is held back so a value split across fragments is scanned whole. See Streamed responses. |
Server and TLS
| Setting | Default | Meaning |
|---|---|---|
server.port |
8080 |
Provider traffic and the admin API. |
management.server.port |
9090 |
Prometheus metrics at /metrics, always plain HTTP. |
proxy.tls.dev-self-signed-cert |
true in the image, false otherwise |
Serve HTTPS with a self-signed certificate generated at startup. Configured key material (server.ssl.certificate, server.ssl.key-store, or server.ssl.bundle) takes precedence. |
server.ssl.certificate, server.ssl.certificate-private-key |
Your certificate and key in PEM form, to serve HTTPS. | |
server.ssl.enabled-protocols |
Limit the TLS versions, such as TLSv1.3,TLSv1.2. Any server.ssl setting turns TLS on, so set it together with a certificate and key. |
|
server.ssl.trust-certificate, server.ssl.client-auth |
For mutual TLS: the PEM file of the CA that signs client certificates, and need. |
|
server.tomcat.connection-timeout |
10s |
How long the proxy waits for a request to start, and between reads of it. Not a deadline for the whole request, so a client that sends a byte at a time can hold a connection; a load balancer in front of the proxy absorbs slow clients. |
server.max-http-request-header-size |
1MB |
Larger request headers are refused. |
The proxy does not limit how many TLS handshakes run at once. Put a load balancer or ingress in front of it to absorb a flood of handshakes or connections.
See Monitoring for the tracing settings.
Providers
| Provider | Setting | Enabled |
|---|---|---|
| OpenAI | proxy.providers.openai |
By default, at https://api.openai.com |
| Anthropic | proxy.providers.anthropic |
By default, at https://api.anthropic.com |
| Gemini | proxy.providers.gemini |
By default, at https://generativelanguage.googleapis.com |
| Ollama | proxy.providers.ollama |
By default, at http://localhost:11434 |
| Azure OpenAI | proxy.providers.azure |
When target is set |
| Vertex AI | proxy.providers.vertex |
When project is set |
| Amazon Bedrock | proxy.providers.bedrock |
When region is set |
| OpenAI-compatible | proxy.providers.openai-compatible.<name> |
For each name with a target |
Every provider takes:
| Field | Default | Meaning |
|---|---|---|
target |
as above | The provider's base URL. |
connect-timeout-ms |
5000 |
How long to wait to connect. |
response-timeout-ms |
120000 |
How long to wait for the response to begin. A stream may then run as long as it lasts. |
ca-cert |
A PEM file of certificate authorities to trust for this provider instead of the system's, such as for a self-hosted server with a private CA. | |
disabled |
false |
Turns off a provider that is on by default. A request for it is refused with 404. |
Redirects from providers are not followed.
Azure OpenAI
proxy:
providers:
azure:
target: https://my-resource.openai.azure.com
api-version: 2024-10-21
entra-id: false
api-version is added to requests that do not send one. With entra-id: true, the proxy authenticates with an Entra ID token from the default Azure credential chain (managed identity, workload identity, environment variables, and so on) instead of passing the client's api-key through.
Vertex AI
proxy:
providers:
vertex:
project: my-project
location: us-central1
Requests go to https://<location>-aiplatform.googleapis.com unless target is set; with neither location nor target, the proxy does not start. A request whose path names another project, or another location when location is set, is refused with 403 vertex_project_denied, since the proxy's credential is used for it. The proxy authenticates with Google application default credentials (a service account key named in GOOGLE_APPLICATION_CREDENTIALS, workload identity, or the metadata server), replacing any Authorization the client sends.
Amazon Bedrock
proxy:
providers:
bedrock:
region: us-east-1
role-arn: arn:aws:iam::123456789012:role/philter-ai-proxy
Requests go to https://bedrock-runtime.<region>.amazonaws.com unless target is set. The proxy signs each request with SigV4, with credentials from the AWS default chain (environment variables, a profile, web identity, or instance and container roles), assuming role-arn when it is set. Only Content-Type, the client's X-Amzn-Bedrock-* headers (guardrail, trace, latency tier, and service tier settings), and the signature are forwarded; the client's other headers are not.
OpenAI-compatible providers
proxy:
providers:
openai-compatible:
vllm:
target: http://vllm.internal:8000
mistral:
target: https://api.mistral.ai
Each is reached at /<name>/v1/... and redacted like OpenAI. A name is lowercase letters, digits, -, and _, and cannot be one the proxy's own paths use (v1, v1beta, api, openai, model, admin).
Routes
A route chooses the policy, context, and response scanning for the requests it matches. The first route whose every set condition matches applies; a request no route matches uses proxy.defaults.
| Field | Meaning |
|---|---|
match.path |
The request path, exactly, after any OpenAI-compatible prefix is removed. |
match.model |
The model the request names, exactly. |
match.header, match.value |
A request header and the value it must have. |
policy |
The policy to apply. Without one, proxy.defaults.policy. |
context |
The context to keep replacements in. Without one, proxy.defaults.context. |
outbound.enabled, outbound.action |
Response scanning for the route. A route without outbound does not scan responses. |
media |
reject or forward for requests carrying images, audio, video, or files. Without one, proxy.defaults.media. |
unsupported-endpoints |
reject or forward for requests the proxy cannot redact or scan. Without one, proxy.defaults.unsupported-endpoints. Pair forward with match.path to allow one endpoint. |
A key created with a policy overrides the route's policy. See Choosing the policy for a request.
proxy:
routes:
- match:
path: /v1/embeddings
policy: embeddings
- match:
header: x-scan-responses
value: "true"
outbound:
enabled: true
action: block
- match:
model: gpt-4o
header: x-team
value: vision
media: forward
- match:
path: /v1/batches
unsupported-endpoints: forward
A header match is only as trustworthy as whoever sets the header. When clients can set it themselves, they can choose their route, including one that forwards media or turns off response scanning. A key with a bound policy skips every route that matches on a header: its requests use only routes that match on path and model, then proxy.defaults. Bind a policy to the keys of clients that must not choose.