Security
Webhooks let external services drive the Bankr agent on your behalf. That's powerful — so the permission model is designed to be safe by default, with multiple layers that all need to line up before an agent can move funds.
The Threat Model
Treat every incoming webhook body as untrusted data. An attacker who controls what's in the payload should not be able to:
- Make the agent sign or submit a transaction you didn't authorize
- Send funds to an address you didn't approve
- Exfiltrate your encrypted env vars
- Invoke another user's webhook
- Read your handler's source code
The design below makes each of these structurally impossible, not just discouraged.
Permission Layers
Every invocation passes through four independent checks. Any one of them is enough to block a malicious request.
1. Signature Verification (Your Handler)
The first line of defense is in the handler itself. The provider scaffolds (--provider slack|github|stripe) include a signature verifier inline:
- Slack — HMAC-SHA256 over
v0:<ts>:<body>, with a 5-minute timestamp window. - GitHub — HMAC-SHA256 over the raw body,
sha256=prefix. - Stripe — Composite header
t=<ts>,v1=<sig>, timing-safe compare, 5-minute window.
Unverified requests return 401 before your agent logic ever runs. Do not deploy a --provider generic handler to production without adding a verifier.
2. readOnly Permission
Every webhook has a readOnly boolean (default true). When true, the agent running your handler's prompt has all write tools physically removed from its available tool list — not just policy-gated, structurally absent.
That means the agent cannot:
- Transfer tokens or native assets
- Swap tokens
- Approve ERC-20 spending
- Submit arbitrary transactions
- Sign messages
It can still read prices, portfolio data, news, on-chain state, and anything else read-only.
Deploy-time check: flipping readOnly to false with no allowedRecipients is rejected up front — you cannot accidentally ship a write-enabled webhook with no transfer allowlist.
3. Recipient Allowlist
When readOnly: false, the agent can write — but every transfer-style tool call is validated against your allowedRecipients list before the transaction is prepared:
"allowedRecipients": {
"evm": ["0xAaAa...", "0xBbBb..."],
"solana": ["9xq..."]
}
Your own wallet address is always implicitly allowed. Any other recipient outside the list causes the tool call to throw — the agent cannot route around it.
Tools where recipients can't be pre-validated (airdrops to dynamically-resolved addresses, automation schedule creation) block entirely when an allowlist is configured.
4. Rate Limit & Payload Cap
rateLimit.perMinute/rateLimit.perDay— hit the ceiling, the next request returns 429 before your handler runs.maxPayloadBytes— oversize bodies return 413.
These protect you from a noisy or compromised upstream hammering the agent.
Environment Variable Protection
Secrets set via bankr webhooks env set are:
- Encrypted at rest — stored in an encrypted, isolated configuration scoped to your user.
- Scoped per user — no other user, handler, or part of Bankr can read them.
- Never logged — secret values are never written to invocation logs or any monitoring system.
- Never returned via API — the
GET /webhooks/envendpoint and dashboard only return variable names, never values. - Available immediately — set now, available as
process.env.KEYon the next invocation.
What You See in the Dashboard
SLACK_SIGNING_SECRET [Remove]
GITHUB_WEBHOOK_SECRET [Remove]
Values are never displayed, transmitted to the browser, or included in API responses.
Key Validation
Env var keys are validated before being accepted:
- Must match
^[A-Za-z_][A-Za-z0-9_]{0,127}$ - Certain reserved prefixes are rejected to avoid conflicts with the runtime
Invalid keys are rejected at the API layer before being stored.
Rotating a Secret
If you suspect a signing secret has leaked:
- Generate a new secret in the upstream provider's dashboard (Slack, GitHub, Stripe).
bankr webhooks env set SLACK_SIGNING_SECRET=<new-value>- Update the provider to send future events with the new secret.
No redeploy needed — the new value is picked up by the next invocation.
Code Execution Isolation
Each deployed webhook runs in its own isolated execution environment with:
- Dedicated compute — your handler runs in its own serverless function, completely separate from other users' handlers.
- Minimal permissions — the handler only has permission to read its own env vars and write logs. It cannot reach Bankr's backend systems, other users' secrets, or any shared filesystem.
- No shared state — no shared memory, disk, or network namespace between handlers. One webhook cannot read another webhook's code, env vars, or payloads.
- Resource limits — bounded memory (256 MB) and execution time (30 seconds). Handlers that exceed the limit return an error and no agent invocation is enqueued.
What Your Code Can Do
| Action | Allowed |
|---|---|
| Make outbound HTTP requests | Yes |
Read process.env variables | Yes (your own only) |
Write to /tmp (ephemeral) | Yes (per-invocation) |
| Access other users' secrets | No |
| Access Bankr's backend | No |
| Access other webhooks' storage | No |
Authentication
Management API
All webhook management operations (deploy, list, update, delete, env vars) require your Bankr API key. The API verifies:
- The API key is valid and active
- The requesting user owns the webhook being modified
- Operations are scoped to the authenticated user's webhooks only
User A cannot view, modify, or delete User B's webhooks or secrets.
Dashboard
The dashboard requires authentication via Privy (email, Twitter, Farcaster, or Telegram). All data shown is scoped to the signed-in user.
Invocation Endpoint
The public URL https://webhooks.bankr.bot/u/<wallet>/<name> accepts anonymous POST requests — it's meant to be called by external services. Auth for invocations is your responsibility:
- Signature verification in the handler (the provider scaffolds include this).
- Optional
allowedIpsallowlist in the config.
Even with neither in place, every agent action is still bounded by readOnly and allowedRecipients. Those cannot be bypassed by a bad payload.
Network Security
- All traffic encrypted via TLS 1.2+
- HTTPS enforced — plaintext HTTP is not accepted
- Rate limiting at the gateway level (in addition to per-webhook rate limits)
Data Retention
- Invocation logs (status, duration, error messages, handler console output) are retained for 90 days.
- Environment variables persist until you delete them or delete the webhook.
- Handler source is stored encrypted and versioned. Old versions are automatically cleaned up after 90 days.
Reporting Vulnerabilities
If you discover a security vulnerability, please report it responsibly via our Discord.