Filtir is an asynchronous reverse proxy written in Rust, designed to intercept traffic from LLM clients, detect and redact personally identifiable information (PII) using regular expressions, and then forward the cleaned request body to the actual API. A local web interface (dashboard) allows for real-time configuration adjustments without needing to recompile the executable.
-
Two HTTP services run within the same binary on two different ports:
- Proxy port (input) The endpoint for clients (CLI, editors, scripts) that previously called the provider's API directly. Default:
3100. - Dashboard port Configuration interface + REST API. Default:
3200.
- Proxy port (input) The endpoint for clients (CLI, editors, scripts) that previously called the provider's API directly. Default:
-
The application state (
AppConfigstatistics, logs) is shared via anArcand locks, allowing the dashboard and proxy to read/write the same logic (PII rules, output URL, etc) .
- The client sends an HTTP request to the input port (using the same path and query as the target API, e.g.,
POST /v1/chat/completions). - The body is read and interpreted as JSON: all strings within the JSON tree are processed through the active PII rules. If the body is not valid JSON, it is treated as plain text.
- Each active rule is a precompiled regex; matches are replaced with the replacement string (e.g.,
[EMAIL REDACTED]). - The modified body is forwarded via
reqwestto the Output URL + the original path (e.g.,https://api.openai.com/v1/chat/completions). - The response from the backend API is returned to the client. The
X-Filtir-Redactedheader indicates the number of detected matches.
- No persistence on disk: a restart resets the configuration to default.
- Redaction applies to the body of requests, not a global network audit of the system.
- Start the executable (see Section 3), then open
http://localhost:3200in a browser. - Statistics Cards: Processed requests, total PII matches, active rules. Counters refresh automatically.
- Configuration: Modify the proxy input port and output URL, then save. Changing the input port triggers a graceful shutdown of the current listener and a restart on the new port (the dashboard does not restart).
- PII Rules: Each rule has a name, a regex pattern, replacement text, and an on/off switch. You can add, modify, or delete them on the fly.
- Redaction Test: Paste free text and click "Test" to see the redacted result and the count of detected PII without going through the proxy.
- Request Log: The last 100 requests seen by the proxy (time, method, path, PII count, HTTP code).
| Méthode | Chemin | Rôle |
|---|---|---|
GET |
/ |
Serves the HTML interface. |
GET |
/api/config |
Returns the current configuration (JSON). |
POST |
/api/config |
Updates the config; restarts the proxy if the input port changes. |
GET |
/api/stats |
Global counters (requests, PII). |
GET |
/api/logs |
Last 100 requests. |
POST |
/api/test-redaction |
Body: {"text":"..."}. Response: original, redacted, count. |
- Rust (2021 edition, latest stable): install viarustup.rs.
- No additional system dependencies (no OpenSSL): the HTTP client uses rustls, which is built into the binary.
git clone https://github.com/Improba/Filtir filtir
cd filtir
cargo build --releaseThe binary is available in target/release/filtir (or target\release\filtir.exe on Windows).
./target/release/filtir
# or in development mode:
cargo runThe default log level is info. For more detail:
RUST_LOG=filtir=debug ./target/release/filtirStopping : Ctrl+C (graceful shutdown of both servers).
# Is the dashboard responding?
curl -s http://localhost:3200/api/stats
# Test redaction directly from the terminal:
curl -s -X POST http://localhost:3200/api/test-redaction \
-H "Content-Type: application/json" \
-d '{"text": "Call John Doe at 06 12 34 56 78, email: john@example.com"}'The principle: point the client to http://127.0.0.1:3100 (Filtir's proxy port) instead of the remote API, and specify the real API URL in the Output URL field of the dashboard.
Claude Code natively supports ANTHROPIC_BASE_URL. Since calls are made directly from your machine, 127.0.0.1 works without any network exposure.
Create or modify ~/.claude/settings.json :
{
"env": {
"ANTHROPIC_BASE_URL": "http://127.0.0.1:3100",
"ANTHROPIC_API_KEY": "sk-ant-your-real-key"
}
}This file is loaded every time claude is launched. The real API key is transmitted via Filtir to Anthropic in the header.
# For the entire shell session
export ANTHROPIC_BASE_URL="http://127.0.0.1:3100"
claude
# For a single command
ANTHROPIC_BASE_URL="http://127.0.0.1:3100" claude -p "Bonjour"
# To disable the proxy for one command (without touching exports)
env -u ANTHROPIC_BASE_URL claudeIn the dashboard (http://localhost:3200), Output URL :
https://api.anthropic.com
Claude Code performs startup requests (autoupdater, telemetry) that may fail via a strict proxy. Add to settings.json :
{
"env": {
"ANTHROPIC_BASE_URL": "http://127.0.0.1:3100",
"ANTHROPIC_API_KEY": "sk-ant-your-real-key",
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
}
}claude -p "My professional email is alice@acme-energy.fr and my IBAN is FR76 3000 1234 5678 9012 345"
# Claude's response should arrive normally.
curl -s http://localhost:3200/api/stats
# "pii_blocked" should have increased by 2.Important Limitation: Cursor assembles its prompts via its own backend servers, then calls the provider API from those servers. When you enter an "Override Base URL" in Cursor, it is Cursor's backend—not your machine—that contacts that URL. http://127.0.0.1 would therefore point to Cursor's servers, not your local Filtir.
Cursor's "Override OpenAI Base URL" option is only usable with Filtir if your proxy is exposed on a network address accessible from the outside (see 4.2.2). For purely local use without network exposure, extensions that make calls directly from the client are better suited (Continue.dev, Cline — see 4.3).
- Open Cursor settings:
Ctrl+,(Linux/Windows) orCmd+,(macOS). - n the sidebar, click on Models.
- Find the OpenAI API Key section.
- Enter your OpenAI key, then enable the "Override OpenAI Base URL (when using key)" toggle.
- In the field that appears, enter your proxy URL (see 4.2.2 below).
- Click Verify to test the connection.
This setting only affects OpenAI models configured with your own key. Tab-completion uses Cursor's internal models and never passes through this proxy.
# Terminal 1 : launch Filtir
./target/release/filtir
# Terminal 2 : expose the proxy port
ngrok http 3100ngrok will display a public URL like https://xxxx-xx-xx.ngrok-free.app. Enter this into Cursor's Override OpenAI Base URL field. Cursor adds /v1 and the request paths itself—so do not add /v1 to the provided URL.
In the Filtir dashboard, URL de sortie :
https://api.openai.com
Continue is an open-source extension whose API calls are made directly from your machine, making 127.0.0.1 fully functional without network exposure.
- Install the Continue extension from the VS Code Marketplace (compatible with Cursor).
- Open
~/.continue/config.jsonand add a model pointing to Filtir:
{
"models": [
{
"title": "GPT-4o via Filtir",
"provider": "openai",
"model": "gpt-4o",
"apiKey": "sk-your-openai-key",
"apiBase": "http://127.0.0.1:3100/v1"
}
]
}- In the Filtir dashboard, Output URL :
https://api.openai.com. - In the Continue panel, select the "GPT-4o via Filtir" model.
Any tool that respects standard OpenAI environment variables:
export OPENAI_BASE_URL="http://127.0.0.1:3100/v1"
export OPENAI_API_KEY="sk-your-openai-key"Example with curl :
curl -s http://127.0.0.1:3100/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "gpt-4o",
"messages": [{
"role": "user",
"content": "Contact John Doe at 06 12 34 56 78, IBAN FR76 3000 1234 5678 9012 345"
}]
}'Example with Python SDK :
import openai # pip install openai
client = openai.OpenAI(
base_url="http://127.0.0.1:3100/v1",
api_key="sk-your-openai-key",
)
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "My email is alice@energy.fr, PRM: 12345678901234"}]
)
print(response.choices[0].message.content)In the Filtir dashboard, Output URL : https://api.openai.com.
| Tool | Calls from Local Machine | 127.0.0.1 Proxy Functional |
Mechanism |
|---|---|---|---|
| Claude Code CLI | Yes | Yes | ANTHROPIC_BASE_URL in ~/.claude/settings.json |
| Continue.dev (extension) | Yes | Yes | apiBase in ~/.continue/config.json |
| Cline (VS Code extension) | Yes | Yes | "Base URL" field in extension settings |
| OpenAI Python / Node SDK | Yes | Yes | base_url parameter or OPENAI_BASE_URL variable |
| curl / shell scripts | Yes | Yes | Explicit URL in command |
| Cursor | No (serveurs backend Cursor) | No | Requires a public URL (ngrok, etc.) |
- Local HTTP: Filtir listens on clear HTTP. Do not expose it on a public interface without a firewall.
- TLS to target API: Filtir handles outbound HTTPS (via rustls); no TLS configuration is required on your end.
- Order of Rule Application: PII rules are applied in the order shown in the dashboard. Reorganize them if two patterns might overlap.
- Cursor Tab-completion: Uses Cursor's internal models and never goes through Filtir.
- Validation: Use POST http://localhost:3200/api/test-redaction to test your regex before connecting a client.
