{% extends "base.html" %} {% block title %}Trust & data residency — Tourniquet{% endblock %} {% block content %}

🔒 Your data, your machine

Tourniquet is a local-first proxy. Every byte of your usage data lives on this Mac. There is no cloud, no telemetry, no remote “Tourniquet account”.

✅ Stays on this machine

  • Your Anthropic API key, AES-256 encrypted on disk (Fernet)
  • Your tq_ tokens, bcrypt-hashed (not recoverable, even by us)
  • Per-request token counts and cost in pence/cents
  • Model name, timestamp, user-agent, optional metadata.user_id
  • Daily cap totals, alert log (~/.tourniquet/alerts.jsonl)
  • Insights — computed locally from your DB; no external lookups

🌐 Goes to the network (and only here)

  • Anthropic — your proxied request body, exactly as you sent it. Same as if you called them directly.
  • Your alert channels — only if you've configured them: Slack webhook, Telegram bot, Resend email, generic webhook. Each receives only the alert text (key name + spend amount). No prompt content, no response content.

No telemetry. Nothing goes to a Tourniquet-operated server — there isn't one.

🚫 Never stored, never sent

  • Prompt content — the body is forwarded opaquely; we don't read or persist it
  • Response content — streamed straight to your client; nothing is written to disk
  • Your IP address — not logged
  • Your Anthropic admin key (used once at bootstrap for history lookup) — held only in process memory, del'd immediately after use, never written anywhere

Threat model

ThreatMitigation
Prompt leakage via TourniquetTourniquet never reads or stores message bodies — it streams them opaquely
Anthropic key theft via DB dumpKey is AES-256 encrypted; attacker needs both the DB and the FERNET_KEY env var
tq_ token forgeryTokens are bcrypt'd (cost 12); timing-safe comparison in auth
Outbound exfiltration via insightsThe insights module is statically asserted to import no network library — tests/test_insights.py::test_no_network_imports
Someone else on your network reading the dashboardTourniquet binds to 127.0.0.1 only. Not reachable from your LAN unless you tunnel it.

Verify it yourself

From the directory you cloned Tourniquet into, run any of these to audit independently:

  1. Search for telemetry hooks
    macOS/Linux: grep -r "telemetry\|posthog\|segment\|sentry" src/
    Windows PowerShell: Select-String -Path src\* -Pattern "telemetry|posthog|segment|sentry" -Recurse
    The only Sentry hit is in main.py behind an opt-in SENTRY_DSN env var (empty by default).
  2. Inspect open network connections while the proxy is running:
    macOS/Linux: lsof -i -P | grep python
    Windows PowerShell: Get-NetTCPConnection -State Listen | Where-Object OwningProcess -in (Get-Process python).Id
    You should only see 127.0.0.1 (dashboard), api.anthropic.com (proxied requests), and any alert channels you configured.
  3. Read docs/data-residency.md for the full architecture explanation
  4. Inspect the SQLite DB directly: sqlite3 tourniquet_dev.db ".schema" — confirm it has columns for cost_usd_cents and friends, but no column for prompt or response content
Back to dashboard
{% endblock %}