Policies & Approval

Policies & Approval

Policies control which requests are auto-approved and which require human approval. When approval is required, approvers can respond via Telegram (one-tap buttons) or passkey (Face ID, fingerprint, YubiKey). Policies are configured per-credential through the admin dashboard, API, or CLI.

Configuring Policies

Set a policy via the admin API:

curl -X PUT https://proxy.toolsec.org/admin/policies/slack \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "auto_approve_methods": ["GET", "HEAD"],
    "require_approval_methods": ["POST", "PUT", "DELETE"],
    "auto_approve_urls": ["/conversations.list", "/users.list"]
  }'

View the current policy for a credential:

curl https://proxy.toolsec.org/admin/policies/slack \
  -H "Authorization: Bearer $SESSION_TOKEN"

On managed hosting, the dashboard shows policy status at a glance for each credential.

Policy Evaluation Order

When a request arrives, the proxy evaluates policy rules in this order:

  1. URL patterns — if the target URL contains any auto_approve_urls substring, auto-approve regardless of HTTP method
  2. Method rules — if the HTTP method is in auto_approve_methods, auto-approve. HEAD follows GET policy
  3. Require approval — if the method is in require_approval_methods, require human approval
  4. Default — if the method isn’t in either list, require approval (fail closed)

For example, a policy for slack with auto_approve_methods: ["GET"], require_approval_methods: ["POST", "PUT", "DELETE"], and auto_approve_urls: ["/conversations.list", "/users.list"] means POST /conversations.list is auto-approved (URL pattern match), but POST /chat.postMessage requires approval (method rule).

No Policy = Fail Closed

If a credential has no policy configured, all requests require approval. This is intentional — you must explicitly opt in to auto-approval.

Approval Channels

When a request requires approval, the proxy routes it to the configured approval channel. The proxy blocks until an approver responds or the timeout expires (default 5 minutes, configurable via AGENTSEC_APPROVAL_TIMEOUT_SECS).

Telegram

The default approval channel. The proxy sends a message to your Telegram chat with:

  • Agent name and credential being used
  • HTTP method and target URL
  • Request body preview
  • Approve and Deny inline buttons

Passkey (WebAuthn)

For hardware-backed approval via Face ID, fingerprint, or YubiKey. When passkey approval is configured for a credential, the Telegram message includes a secure URL. The approver opens the link, authenticates with their passkey, and the request is approved.

Approvers register their own passkeys the first time they receive an approval request — no admin setup needed. Passkeys are persisted across restarts.

Long-Polling (Default)

The proxy polls Telegram’s getUpdates API with a 30-second server-side timeout. This works behind firewalls and NAT without exposing a public URL.

Webhook Mode

For production deployments with a public URL, you can configure Telegram webhooks via the Telegram Bot API. The proxy handles callbacks at POST /telegram/webhook.

Per-Credential Approval Routing

You can restrict who can approve requests and route approval messages to different Telegram chats per credential.

curl -X PUT https://proxy.toolsec.org/admin/policies/production-db \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "require_approval_methods": ["GET", "POST", "PUT", "DELETE"],
    "allowed_approvers": ["123456789", "987654321"],
    "telegram_chat_id": "-100999888777"
  }'

allowed_approvers

A list of Telegram user IDs. When set:

  • Only listed users can tap Approve/Deny
  • Other users who tap the buttons get an “unauthorized” alert
  • Empty list (default) = anyone in the chat can approve

Per-credential chat_id

Overrides the global TELEGRAM_CHAT_ID for this credential’s approval messages. Useful for routing sensitive credentials to a restricted channel.

You can also configure notification channels per team via the admin API:

curl -X POST https://proxy.toolsec.org/admin/notification-channels \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"channel_type": "telegram", "name": "ops-alerts", "config": {"chat_id": "-100123456789"}}'

Rate Limiting

Per-agent rate limiting is configured when creating an agent:

# Via admin API
curl -X POST https://proxy.toolsec.org/admin/agents \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"id": "my-agent", "rate_limit_per_hour": 100}'
 
# Via CLI (self-hosted)
tap agent create --name my-agent --rate-limit 100 \
  --db agentsec.db --encryption-key $AGENTSEC_ENCRYPTION_KEY

When the limit is exceeded, the proxy returns 429 Too Many Requests. Rate limits reset on a rolling 1-hour window. Omit the rate limit for unlimited requests.