Security
This page documents TameFlare's security model, including the proxy architecture, cryptographic design, data handling, and operational security guidance.
How HTTPS interception works
TameFlare uses a cloud proxy model for HTTPS interception. The gateway runs at proxy.tameflare.com - there is no local binary to install and no local CA certificate to manage.
How it works
tf runsetsHTTPS_PROXY=https://{gateway_token}@proxy.tameflare.comand spawns your process- Your agent's HTTP library sends all outbound requests through the cloud proxy via the standard
CONNECTtunnel - The cloud gateway at
proxy.tameflare.com:- Identifies your gateway by the token in the proxy authorization header
- Terminates TLS, inspects the request, and applies permission checks
- If allowed, injects credentials from the encrypted vault and forwards to the real destination
- If denied, returns 403 immediately
- Only traffic metadata (domain, action type, decision, status code, latency) is logged to the dashboard. Request/response bodies are never stored.
Why this is safe
- No request/response bodies stored. The cloud gateway processes traffic for enforcement but does not persist bodies. Only metadata is logged.
- Credential isolation. Your agent process never sees real API keys. Credentials are injected by the gateway at request time from an AES-256-GCM encrypted vault.
- Per-gateway tokens. Each gateway has a unique token. Tokens are validated on every request.
- EU-hosted infrastructure. Both the dashboard (tameflare.com) and the cloud gateway (proxy.tameflare.com) are hosted in the EU.
- Same enforcement model as corporate proxies. Organizations like banks and hospitals use proxy-based interception for DLP and compliance. TameFlare applies the same pattern to AI agent traffic.
.tf/ directory stores your gateway configuration (config.yaml) and local credentials. It is in .gitignore by default. Do not commit gateway tokens to version control.Proxy compatibility
Most HTTP libraries respect HTTPS_PROXY automatically. tf run sets this for the child process. No additional trust store configuration is needed - the cloud proxy uses a valid public TLS certificate.
Proxy bypass analysis
What tf run enforces
tf run sets HTTP_PROXY, HTTPS_PROXY, and NO_PROXY environment variables for the child process. Most HTTP libraries (Python requests, Node.js axios/fetch, Go net/http) respect these automatically.
What it cannot prevent
TameFlare is honest about its limitations:
| Bypass method | Can TameFlare prevent it? | Mitigation |
|---|---|---|
Agent ignores HTTP_PROXY env var | No | Use OS-level network isolation (firewall rules, network namespaces) |
| Agent uses raw TCP sockets | No | Proxy only intercepts HTTP/HTTPS |
| Agent hardcodes IP addresses | No | Firewall rules blocking direct outbound on ports 80/443 |
| Agent uses non-HTTP protocols (gRPC, WebSocket, UDP) | No | TameFlare is HTTP/HTTPS only |
| Agent reads gateway token from env | Unlikely (env var scoped to child process) | Restrict .tf/config.yaml permissions; use short-lived tokens |
iptables or network namespaces to block direct outbound traffic from agent processes. On macOS, use pf firewall rules. This ensures the proxy is the only path to the internet.Threat model
TameFlare is designed to protect against:
| Threat | How TameFlare mitigates it |
|---|---|
| Agent calls unauthorized APIs | Deny-all default. No connector configured = no access. Agent cannot reach any domain you haven't set up. |
| Agent bypasses policy checks | Mode A: Agent doesn't hold external API keys - the Gateway does. Without permission, the request is blocked. |
| Agent forges a decision token | Tokens are ES256-signed (ECDSA P-256). The private key lives on the control plane. Agents never see it. |
| Agent replays a used token | Tokens include a nonce. The control plane marks nonces as used on first verification. Replay attempts are rejected. |
| Agent reuses a token for a different action | Tokens carry an action spec hash. The Gateway verifies the hash matches the action being executed. |
| Credential leakage | In proxy mode, agents never see real API keys. Credentials are injected by the gateway at request time. |
| Unauthorized dashboard access | RBAC with 4 roles (owner > admin > member > viewer). Session tokens stored in DB. Optional DASHBOARD_PASSWORD for shared access. |
| Secrets leaked from database | Slack tokens, GitHub PATs, and other secrets are encrypted at rest with AES-256-GCM when SETTINGS_ENCRYPTION_KEY is configured. |
| Runaway agent activity | Rate limiting (120 req/min per agent, configurable). Kill switch halts all traffic instantly. Scoped to all/connector/agent. |
What TameFlare does NOT protect against
- Prompt injection / hallucination content - TameFlare evaluates the action spec, not the LLM reasoning. If a hallucinated action has a valid type and parameters that pass policy checks, it will be allowed.
- Local file system / shell access - TameFlare operates at the network layer. It cannot prevent an agent from reading files, running shell commands, or accessing local resources. Use OS-level sandboxing (containers, seccomp) for that.
- Non-HTTP protocols - gRPC, WebSocket upgrades, raw TCP, and UDP are not intercepted by the proxy.
- Compromised host - If an attacker gains root access to the TameFlare server, they can read the credential vault, modify policies, or extract secrets. Secure your TameFlare deployment like any other production service.
- Third-party security audit - TameFlare has not yet been audited by a third party. We plan to commission an independent audit as the project matures. The full source code is available for your own review under the Elastic License v2.
Credential vault
The credential vault stores API keys for connectors (GitHub tokens, Stripe keys, OpenAI keys, etc.) encrypted at rest.
How it works
- Encryption: AES-256-GCM with a key derived from the server's
SETTINGS_ENCRYPTION_KEY - Storage: Encrypted in the dashboard database (EU-hosted). Credentials entered via the dashboard gateway wizard are stored here.
- Access pattern: The cloud gateway fetches credentials from the dashboard at config sync time, holds them in memory, and injects them into allowed requests at proxy time.
- Agent isolation: Agents never see credentials. The proxy adds
Authorizationheaders (or equivalent) after permission checks pass.
Who can access credentials
| Actor | Can access? | How |
|---|---|---|
| Agent process | No | Agent only sees the proxy URL, never real API keys |
| Cloud gateway process | Yes | Receives credentials from dashboard at config sync, injects at request time |
| Dashboard user (admin) | Partial | Can add/remove credentials via wizard, but stored values are masked |
| Dashboard user (viewer) | No | Cannot see or modify credentials |
Key loss recovery
If the SETTINGS_ENCRYPTION_KEY is lost:
- Encrypted credentials cannot be recovered
- Re-add credentials via the dashboard gateway wizard
- Generate a new key and re-enter all connector credentials
Control plane secrets
The control plane (Next.js) also encrypts integration secrets (Slack tokens, GitHub PATs configured via Settings) using AES-256-GCM when SETTINGS_ENCRYPTION_KEY is set:
- Each value encrypted with a unique IV
- Stored as
enc:<base64(iv + ciphertext + auth_tag)>in SQLite - If the key is lost, re-enter secrets in the dashboard
Data logging scope
What tf logs
| Data | Logged? | Where |
|---|---|---|
| Request URL and path | Yes | Traffic log (gateway SQLite) |
| HTTP method | Yes | Traffic log |
| Request headers | Selected only (Host, Content-Type, User-Agent) | Traffic log |
| Response status code | Yes | Traffic log |
| Latency (ms) | Yes | Traffic log |
Parsed action type (e.g., github.pr.merge) | Yes | Traffic log + audit log |
| Decision (allow/deny/require_approval) | Yes | Traffic log + audit log |
| Agent name | Yes | Traffic log + audit log |
| Connector type | Yes | Traffic log |
| Approver identity and timestamp | Yes | Audit log |
What TameFlare does NOT log
| Data | Logged? | Reason |
|---|---|---|
| Request body | No | May contain PII, secrets, or sensitive business data |
| Response body | No | May contain PII or proprietary data |
| Authorization headers | No | Contains API keys (injected by proxy) |
| Cookie values | No | Session data |
| Query parameters | Partial (URL logged, but sensitive params like ?token= are not parsed) | Privacy |
Inter-component security
Cloud proxy ↔ Dashboard communication
The cloud gateway at proxy.tameflare.com communicates with the dashboard at tameflare.com over HTTPS:
| Direction | Protocol | Auth |
|---|---|---|
| Cloud gateway → Dashboard (config sync, token verification) | HTTPS | Per-gateway token (gwtk_*) |
| Cloud gateway → Dashboard (traffic metadata logging) | HTTPS | Per-gateway token (gwtk_*) |
Both components are hosted in the EU. All inter-component traffic is encrypted in transit.
Agent isolation
- Each gateway has a unique token. Traffic is identified and scoped by gateway token.
- An agent can only see its own traffic logs via the v1 API (scoped by API key)
- Agents cannot see other agents' actions, credentials, or permissions
- The dashboard shows all agents' activity (scoped by RBAC role)
Data residency
The gateway's only outbound traffic is (1) your configured connectors and (2) encrypted control plane sync for configuration and audit metadata:
| Integration | What data is sent externally | When |
|---|---|---|
| None configured | Nothing. Zero outbound calls. | Always |
| Slack approvals | Action type, agent name, resource target, approval status | When an action requires approval |
| Webhook callbacks | Action type, decision, agent ID (configurable payload) | When webhook_url is set on an action request |
| Configured connectors | The proxied HTTP request itself (forwarded to the real API) | When an action is allowed |
Slack notifications do not include credentials, request/response bodies, or policy details.
SENTRY_DSN and POSTHOG_KEY env vars are disabled by default and only activate if you explicitly set them.Fail-closed model
TameFlare uses a fail-closed (deny-by-default) security model:
| Scenario | Behavior |
|---|---|
| No connector configured for a domain | Request blocked (403) |
| No permission rule matches | Request denied (default deny) |
| Policy engine encounters an error | Action denied |
| Gateway cannot reach control plane | Gateway operates independently using local permissions and cached config. |
| Approval timeout (5 minutes) | Request denied (408) |
| Kill switch active | All matching traffic blocked immediately |
| Rate limit exceeded | Request rejected (429) with Retry-After header |
| Invalid or expired decision token | Execution rejected |
| Nonce already used | Execution rejected (replay protection) |
There is no "fail-open" mode. If TameFlare cannot make a decision, the default is deny.
Cryptographic design
Decision tokens (ES256)
TameFlare uses ES256 (ECDSA with P-256 curve and SHA-256) for decision tokens, implemented via the jose library.
Token lifecycle:
- Agent requests an action via
POST /api/v1/actions - Policy engine evaluates and returns a decision
- If
alloworrequires_approval(after approval), the control plane signs a JWT containing:action_request_id- the specific action this token authorizesaction_spec_hash- SHA-256 hash of the canonical action spec JSONnonce- unique random value for replay protectioniat/exp- issued-at and expiration (5 minutes)
- Agent sends the token to
POST /api/v1/actions/:id/execute - Gateway forwards the token to the control plane for verification
- Control plane verifies signature, checks expiry, validates nonce (marks as used), and confirms action hash matches
Key management:
- ES256 key pair is generated on first boot and stored in the database
- The private key never leaves the control plane process
- There is currently no automatic key rotation - rotate manually by generating a new key pair and restarting
Key loss recovery
If the ES256 private key is lost:
- All outstanding (unused) decision tokens become unverifiable
- Generate a new key pair and restart the control plane
- Agents will need to re-request any pending actions to get new tokens
- Historical audit data is unaffected - tokens are verified at execution time, not retroactively
Encryption at rest
Settings encryption
When SETTINGS_ENCRYPTION_KEY is set, TameFlare encrypts sensitive settings (Slack tokens, GitHub PATs, signing secrets) using AES-256-GCM before storing them in the database.
Setup:
# Generate a 256-bit key
openssl rand -hex 32
# Set in your environment
SETTINGS_ENCRYPTION_KEY=your_64_char_hex_key_hereHow it works:
- Each value is encrypted with a unique IV (initialization vector)
- Encrypted values are stored as
enc:<base64(iv + ciphertext + auth_tag)> - Decryption happens in-memory when settings are read
- If the key is lost, encrypted settings cannot be recovered - re-enter them in the dashboard
Database
The SQLite database file is not encrypted by default. For production deployments:
- Use filesystem-level encryption (LUKS, FileVault, BitLocker)
- Or use Turso (hosted libSQL) which encrypts data at rest
- Restrict file permissions:
chmod 600 local.db
Authentication
Agent authentication
Agents authenticate via API keys in the Authorization: Bearer <key> header.
- Keys are generated with a cryptographically random value
- Only the bcrypt hash is stored in the database
- Keys are prefixed (
tf_test_ortf_live_) for easy identification - The prefix is stored separately for lookup without exposing the full key
- Keys are shown once at creation - they cannot be retrieved later
User authentication
Dashboard users authenticate via email/password:
- Passwords are hashed with bcrypt (cost factor 10)
- Sessions are stored in the database (not in-memory) and survive restarts
- Session tokens are cryptographically random, stored in HTTP-only cookies
- Legacy mode:
DASHBOARD_PASSWORDenvironment variable for shared access
Rate limiting
- 120 requests per minute per agent (sliding window)
- Returns
429 Too Many RequestswithRetry-Afterheader - Rate limit state is in-memory (resets on restart)
Policy engine safety
The policy engine is not Turing-complete. It evaluates a fixed set of condition operators against action spec fields:
- No loops, recursion, or arbitrary code execution
- Maximum nesting depth for
any/allcombinators is bounded by YAML structure - Evaluation is O(policies × rules × conditions) - linear, not exponential
- A malformed policy cannot crash the engine - parse errors are caught and the policy is skipped
Operational security checklist
| Item | Status | Action |
|---|---|---|
SETTINGS_ENCRYPTION_KEY set | Required for production | openssl rand -hex 32 |
SIGNING_KEY_PRIVATE/PUBLIC set | Required for production | Generate with openssl ecparam -genkey -name prime256v1 |
| HTTPS enabled | Required for production | Use a reverse proxy (nginx, Caddy) with TLS |
.tf/config.yaml not in version control | Required | Already in .gitignore - verify with git status |
| Gateway tokens rotated | Recommended | Rotate tokens periodically in the dashboard |
| Dashboard password or user auth | Required | Set DASHBOARD_PASSWORD or register users at /register |
| Rate limiting | Enabled by default | 120 req/min per agent |
| Kill switch tested | Recommended | Toggle on/off in Settings to verify it works |
| Audit log retention | Recommended | Set AUDIT_RETENTION_DAYS and run cleanup job |
| Backup strategy | Recommended | Copy .db file or use Turso |
Responsible disclosure
If you discover a security vulnerability in TameFlare, please report it responsibly:
- Email: security@tameflare.com
- Do not open a public GitHub issue for security vulnerabilities
- We will acknowledge receipt within 48 hours and provide a timeline for a fix
- See
SECURITY.mdfor the full disclosure policy, response timeline, and scope
Third-party security audit
TameFlare has not yet undergone a third-party security audit. This is planned and will be prioritized as the project matures.
| Item | Status |
|---|---|
| Third-party penetration test | Planned - not yet scheduled |
| SOC 2 compliance | Planned |
| Code audit by external firm | Planned - seeking firms with Go + Node.js expertise |
| Bug bounty program | Not yet - will consider after first audit |
What we do today
- Source-available code - the entire codebase is inspectable. You can audit it yourself or hire a firm to audit it.
- SECURITY.md - responsible disclosure process with 48h acknowledgment SLA
- Dependency scanning - GitHub Dependabot alerts enabled
- 51 unit tests for the policy engine (conditions, evaluator, nested combinators)
- Integration test scripts -
dogfood.js(99 tests),dogfood-gateway.js(81 tests),simulate-new-user.js(40 tests)
Test coverage
| Component | Tests | Type |
|---|---|---|
| Policy engine (conditions) | 14 tests | Unit (vitest) |
| Policy engine (evaluator) | 37 tests | Unit (vitest) |
| API routes | 99 tests | Integration (dogfood.js) |
| Gateway proxy | 81 tests | Integration (dogfood-gateway.js) |
| New-user flow | 40 tests | End-to-end (simulate-new-user.js) |
| Real agent (7 tasks) | 7 tests | Integration (test-TameFlare-agent/) |
Security-specific test coverage:
- Auth bypass - tested in dogfood (invalid keys, expired sessions, missing headers)
- RBAC enforcement - tested per role (owner, admin, member, viewer)
- Kill switch - tested activation, scoping, and deactivation
- Rate limiting - tested 429 responses and Retry-After headers
- Nonce replay - tested token reuse rejection
- Zod validation - tested malformed payloads, missing fields, extra fields
Next steps
- Deployment - production deployment guidance
- Concepts & Terminology - understand tokens, enforcement levels, and the deny-wins model
- Troubleshooting - common issues and solutions
- Support - get help and report issues