Plugin Runtime & Limits
What exists inside the sandbox, and every budget
An entrypoint exports a function of (arvexa, input) and returns a JSON-serializable value:
module.exports = async function handler(arvexa, input) { ... }
module.exports.handler and module.exports.default are also accepted. Input and output must be JSON-serializable; anything else is rejected by the worker.
`arvexa` is the only door out. It has call(operationId, args) and context.hookName. There is no database client, no process.env, no filesystem, no network, and no module loading — not because a well-behaved handler declines to use them, but because the worker denies module loading and freezes the process object before your file is compiled. Plugin code never receives a Prisma client, DATABASE_URL, an encryption key, a JWT secret, or a peer writer, regardless of what it passes to call.
Budgets for one invocation:
- CPU: 5,000 ms
- Memory: 256 MB
- Input: 1,000,000 bytes
- Output: 1,000,000 bytes
- Logs: 65,536 bytes
- API calls: 32
- Invocation depth: 4
- Entrypoint source: 1,000,000 bytes
Exceeding a budget terminates the invocation with a specific reason rather than a generic error: TIMEOUT, MEMORY_LIMIT_EXCEEDED, OUTPUT_TOO_LARGE, LOG_TOO_LARGE, INPUT_TOO_LARGE, API_CALL_LIMIT_EXCEEDED, INVOCATION_DEPTH_EXCEEDED, CONCURRENCY_LIMIT_EXCEEDED. Other reasons you may see are OK, HANDLER_ERROR, HANDLER_REJECTED, STATIC_SCAN_DENIED, WORKER_CRASHED, WORKER_PROTOCOL_ERROR, and CANCELLED.
Runtime output is persisted, bounded, and redacted by the host. What you return is retained, so return correlation ids and derived summaries — never a phone number, a caller name, a recording URL, or a transcript. Coarse buckets instead of exact values are the usual right answer for anything that could re-identify a person.
Three sandbox modes, and arvexa sandbox run defaults to the safest one that still proves something:
DRY_RUN— verifies installation status, scan currency, package hash, the declared entrypoint, audience roles, and the static source guard, then stops before handing source to the isolation adapter. No handler code runs and no side effect is possible.SANDBOX— executes the handler in isolation. Capability calls are authorized for real and then answered with deterministic mocks.LIVE— really dispatches capability calls. Authorized by the host only: it requiresDEVELOPER_PLATFORM_LIVE_EGRESS=true, per-organization configuration, and any configured approval, all evaluated server-side and re-evaluated at execution time. The CLI does not pre-check any of it, because a second copy of a security gate in a client can only drift from the real one.
The scheduler is not wired. There is no worker fleet, no cron, and no queue in V1: a sandbox run and a scan advance on an explicit tick. The API reports this as schedulerWired: false and the CLI prints it, rather than implying a queue that does not exist.