| Version | Supported | Notes |
|---|---|---|
| Unreleased alpha | ✅ | Security fixes are made on the default branch |
| Published releases | ❌ | No public release has been declared yet |
Pitwall is pre-1.0. The API may change in backwards-incompatible ways between minor releases. When a release reaches end-of-life, its security advisories are archived but not backported.
Please do not open public GitHub issues for security vulnerabilities.
Private disclosure is preferred and expected. You can report vulnerabilities through:
- GitHub Private Vulnerability Reporting — use the Security tab on the repository, then "Report a vulnerability". This routes directly to the maintainers without exposing the details publicly.
- Email — no public security mailbox has been approved yet; use GitHub Private Vulnerability Reporting until one is listed here
The project targets acknowledgement within 72 hours and a substantive update within 14 days. These are best-effort targets, not an SLA.
For non-sensitive security questions or process questions, open a regular GitHub Discussion.
This policy covers the following Pitwall components and their security boundaries:
| Component | What is in scope |
|---|---|
FastAPI control plane (src/pitwall/api/) |
Admin auth middleware, all /v1/admin/* routes including kill-switch, budget gates, and audit trails |
MCP server (src/pitwall/mcp/) |
Admin tooling and any tool that exercises privileged operations |
Kill-switch (src/pitwall/api/admin/emergency.py, src/pitwall/api/admin/kill_switch.py) |
POST /v1/admin/kill-switch; atomic termination and verification; optional network-revocation path |
Inbound webhook HMAC (src/pitwall/webhook_receiver/, src/pitwall/webhook_dispatcher/signer.py) |
POST /webhooks/runpod; constant-time signature verification with bounded replay window |
SSRF allow-list (src/pitwall/resolver/provider_urls.py) |
runpod_endpoint_id validation via ^[A-Za-z0-9][A-Za-z0-9_-]{1,63}$ at resolver/provider_urls.py::_require_endpoint_id; outbound URL construction for serverless_lb / serverless_queue / public_endpoint providers |
| Secrets management | Central redaction for bearer/admin/webhook credentials, authorization headers, and credential-bearing URLs; AES-GCM storage for outbound webhook secrets |
Pitwall Agent Routing (packages/agent-routing/) |
CLI/shim argument handling, local command execution, route and run-state permissions, lifecycle hooks, provider installers, bootstrap/update integrity, plugin packages, local endpoint materialization, and the loopback receiver |
Pitwall has two operational modes with fundamentally different trust requirements:
Pitwall has one operator, one RunPod account, and one shared budget; it is not a multi-tenant authorization system. Bearer scopes limit what a credential can do but do not add tenant ownership or row-level isolation.
Production deployments must:
- Set
PITWALL_API_TOKENandPITWALL_ADMIN_SECRET; non-loopback API startup refuses to proceed without both. - Set
PITWALL_WEBHOOK_SECRET; non-loopback webhook startup refuses to proceed without it. - Use scoped bearer tokens for delegated callers and reserve the all-scopes token for operators.
- Bind published ports to loopback or a private interface and terminate TLS at a trusted proxy.
See the full trust model and security controls in:
The alpha MCP server is supported over local stdio only. Every network transport is rejected because HTTP authentication is not implemented. Local process access is therefore the MCP trust boundary.
Agent Routing delegates work to locally installed provider CLIs. Those children inherit the
operator's filesystem, process, credential, and network authority subject to each provider's own
sandbox. The default SUBAGENT_MODEL_ROUTING_UNRESTRICTED=1 profile permits unattended child
execution and can bypass interactive approval prompts; set it to 0 when the selected harness can
operate under its restricted policy. Treat prompts, generated patches, provider output, lifecycle
hooks, route files, and workflow definitions as untrusted input.
Run records and output are private local state, but can contain source or secrets printed by a
child. Prompt bodies are retained only when explicitly requested. No automatic retention period is
imposed; inspect and remove records with the documented model-routing runs commands. Plugins and
the source installer expose the same local execution boundary as direct shims. Optional provider
installation is an explicit, confirmed mutation surface and never performs provider login.
Custom and self-hosted endpoints remain operator-supplied. API keys stay in named environment variables; route JSON stores the variable name, never the value. The optional Pitwall event receiver binds only to loopback and writes a user service only when explicitly requested; enabling that service is a separate action.
Credential roles are deliberately separate:
PITWALL_API_TOKENis the broker's all-scope server credential and a legacy Agent Routing fallback.PITWALL_AGENT_ROUTING_API_TOKENis the preferredread/spendclient token for route metadata and proxy traffic.PITWALL_AGENT_ROUTING_SUBSCRIPTION_TOKENis the preferredwebhook:admintoken used only to create a receiver subscription.PITWALL_WEBHOOK_SECRETsigns inbound broker webhooks and is only a legacy receiver fallback.PITWALL_AGENT_ROUTING_WEBHOOK_SECRETis the preferred receiver verification secret;PITWALL_WEBHOOK_SECRET_ENVmay explicitly name a different environment variable.
The detailed component threat model is maintained in
packages/agent-routing/SECURITY.md, but disclosure and
advisory coordination remain here under one Pitwall security home.
| Feature | Implementation | Default |
|---|---|---|
| API authorization | Constant-time opaque bearer lookup; explicit read, spend, lease:mutate, webhook:admin, and server:admin scopes |
Required for non-loopback API; optional with warning on loopback |
| Admin auth | server:admin bearer scope plus constant-time X-Pitwall-Secret comparison |
Required for non-loopback API; admin routes fail closed if absent |
| Inbound webhook HMAC | X-Pitwall-Webhook-Signature verified by webhook_dispatcher/signer.verify; bounded timestamp window |
Required for non-loopback receiver; optional on loopback |
| SSRF allow-list | runpod_endpoint_id validated by ^[A-Za-z0-9][A-Za-z0-9_-]{1,63}$ at resolver/provider_urls.py::_require_endpoint_id |
Always on |
| Kill-switch | POST /v1/admin/kill-switch; ordered network deny → device revoke → compute terminate; < 30s budget; audit-logged |
Gated by admin auth |
| Fail-closed boot | Refuses to start when the runtime variables required by a service are unset | Always on |
- Multi-tenant ownership isolation is not provided; report authorization bypasses against the documented bearer scopes, but not the absence of tenant-specific row ownership.
- Third-party services that Pitwall calls (RunPod API, Tailscale, Redis, PostgreSQL). Report issues with those services to their respective vendors.
- Social-engineering attacks against operators.
- Provider-model behavior, provider subscription policies, and documented unrestricted local execution are out of scope unless Agent Routing broadens authority beyond the documented boundary or mishandles arguments, credentials, state, or installer verification.