corsair.ts
How it works
Every plugin endpoint has a risk level (read, write, or destructive). Your permission mode maps each risk level to a policy. When an agent calls a gated endpoint:
- Corsair evaluates the policy for that endpoint
- If
allow→ the call proceeds immediately - If
deny→ the call is blocked with no database record - If
require_approval→ Corsair writes a row tocorsair_permissionsand blocks the call until a human approves
completed and cannot be replayed.
Add the permissions table
If you already ran the quick start migration, add this table once. The schema matches what Corsair expects at runtime.- SQLite
- PostgreSQL
View migration SQL
View migration SQL
permissions.sql
- macOS / Linux
- Windows (PowerShell)
Column reference
Permission modes
Set a default mode per plugin withpermissions.mode. Each mode maps risk levels to policies:
cautious is a good default for agent workloads — agents can read and write freely, but destructive actions need a human in the loop.
corsair.ts
Approval policies
Three resolved policies control what happens at call time:
Policies come from the mode matrix above, unless you override a specific endpoint.
Overrides
Usepermissions.overrides to tighten or loosen individual endpoints beyond the mode default. Keys are dot-notation paths through the plugin’s endpoint tree — invalid paths are compile-time errors.
corsair.ts
deny always wins — no approval record is created.
Status lifecycle
Each approval request moves through these states:
Corsair deduplicates pending requests. If the same plugin, endpoint, args, and tenant already have a non-expired
pending record, a second call returns the existing token instead of creating a duplicate.
Timeout
Configure timeouts and blocking behavior at the root withcreateCorsair({ permissions: ... }):
The old
approval: { ... } key still works but is deprecated — rename it to permissions. TypeScript will strike through approval in your editor. A runtime warning is logged on startup if you still use it.corsair.ts
timeout— how long apendingrecord stays valid. Defaults to10mif not set. Written toexpires_atwhen the record is created.onTimeout— intended behavior when the window closes without a response. Withdeny, expired records are treated as blocked. Useapproveonly in low-risk, fully trusted environments.
expires_at, the record is no longer actionable. Synchronous mode returns a timeout error; asynchronous retries see the request as expired.
Synchronous vs asynchronous
Control how blocked calls behave withpermissions.mode. Agent-facing messages come from hub (hosted, automatic) or manual.onApprovalRequired (self-hosted review URLs).
Asynchronous (default)
The tool call returns immediately with an error. The agent sees the blocked result and must stop or retry after the user approves. Best when:- The agent should explicitly tell the user to visit a review page
- You want the model to handle denial gracefully and not burn tokens polling
approvalBaseUrl and optionally customize via onApprovalRequired:
corsair.ts
Synchronous
The tool call blocks and pollscorsair_permissions every 500 ms until the user approves, denies, or the timeout elapses. From the agent’s perspective, it is just a slow tool call — the model does not need to handle a separate approval step.
Best when:
- You have a review UI open alongside the agent session
- You want approval to feel seamless — approve in the UI, the agent continues automatically
corsair.ts
Dynamic mode
Pass a function to switch modes per request — useful when approval behavior depends on runtime context:corsair.ts
Handling permission approvals
When an action requires approval, Corsair inserts a row intocorsair_permissions with a unique token. That token is what you put in review URLs, Slack messages, or anywhere else you surface the request. Look up the row by token to see exactly what the agent wants to do — the args column holds the JSON-encoded arguments frozen at request time.
status:
approved.
Build your own review flow
How you approve is entirely up to you. A few common patterns: Manual review UI — Add a page in your app that lists pending requests, showsplugin, endpoint, and parsed args, and renders Approve / Deny buttons that run the UPDATE above.
Automated reviewer agent — Send the pending request to a second agent that evaluates whether the action is safe, then programmatically sets status to approved or denied. Useful when you want policy checks without a human in the loop for every write.
review-page.ts
status is approved, either the original agent retries the call or you invoke executePermission yourself to run the action without waiting for a retry.
Integrating with an agent
MCP / coding agents
When using MCP adapters, permissions gaterun_script calls automatically. Configure per-plugin permissions: { mode, overrides } and global permissions: { timeout, mode } on createCorsair.
With hub config, blocked calls automatically include a hosted approval URL for the agent. With manual config, set manual.approvalBaseUrl (and optionally manual.onApprovalRequired) so agents receive a review link:
corsair.ts
approved record, runs the endpoint, and marks it completed.
executePermission (optional)
If you don’t want to wait for the agent to retry after approval, call executePermission once status is approved. It replays the frozen args directly — no LLM involved:
approve-handler.ts
executePermission scopes to the correct tenant via withTenant, navigates corsair[plugin].api[endpoint], and marks the record completed on success.
The
corsair.permissions namespace exposes find_by_token and find_by_permission_id for reads, but intentionally does not include approve/deny transitions — those happen in your review flow.Multi-tenancy
In multi-tenant setups, each approval record stores thetenant_id from the active withTenant() context. When the action executes, Corsair scopes to that tenant’s credentials and data.
What’s next
MCP Adapters
Wire Corsair into Cursor, Claude Code, or any MCP-compatible agent.
Multi-Tenancy
Scope approvals and credentials per user with withTenant().
Database
The four core tables Corsair uses for synced integration data.
Hooks
Add custom logic before and after API calls — logging, validation, side effects.