Skip to main content

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/env endpoint and dashboard only return variable names, never values.
  • Available immediately — set now, available as process.env.KEY on 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:

  1. Generate a new secret in the upstream provider's dashboard (Slack, GitHub, Stripe).
  2. bankr webhooks env set SLACK_SIGNING_SECRET=<new-value>
  3. 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

ActionAllowed
Make outbound HTTP requestsYes
Read process.env variablesYes (your own only)
Write to /tmp (ephemeral)Yes (per-invocation)
Access other users' secretsNo
Access Bankr's backendNo
Access other webhooks' storageNo

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:

  1. Signature verification in the handler (the provider scaffolds include this).
  2. Optional allowedIps allowlist 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.