Network & Permissions

How plugins make HTTP requests, declare permissions, and use the CORS proxy.

Plugins can use fetch() to make HTTP requests. Since plugins run client-side in a Web Worker, requests go through the browser's native fetch API — subject to standard CORS rules.

Declaring permissions

Declare which hosts your plugin accesses using @permissions for documentation and future permission gating:

// @permissions network:api.github.com

Permission syntax

PatternExample
Single hostnetwork:api.github.com
Wildcard subdomainnetwork:*.slack.com
Any hostnetwork:*

Multiple hosts are comma-separated:

// @permissions network:api.linear.app,network:hooks.slack.com,network:api.github.com

Making requests

Standard fetch() works for CORS-friendly APIs:

// @stoa-plugin
// @name GitHub Status
// @type widget
// @icon Globe
// @permissions network:api.github.com
// @refresh 5m

module.exports = {
  async render(context) {
    const resp = await fetch('https://api.github.com/repos/myorg/myrepo/actions/runs?per_page=5')
    const data = await resp.json()

    return {
      type: 'list',
      items: data.workflow_runs.map(run => ({
        label: run.name,
        subtitle: run.conclusion || run.status,
        iconColor: run.conclusion === 'success' ? 'green' : run.conclusion === 'failure' ? 'red' : 'orange',
      }))
    }
  }
}

CORS proxy

For websites and APIs that don't support CORS, use the built-in stoaProxy() helper:

// Direct fetch (works for CORS-friendly APIs):
const resp = await fetch('https://api.github.com/repos/myorg/myrepo')

// Proxy fetch (for non-CORS websites):
const resp = await fetch(stoaProxy('https://example.com/page'))

stoaProxy(url) is a global function available in all plugins. It returns the full proxy URL so fetch() works from the Web Worker. The proxy fetches the URL server-side and returns the response with CORS headers.

Proxy limits

LimitValue
SSRF protectionBlocks private IP ranges and metadata endpoints
Response size1MB maximum
Timeout10 seconds
AuthenticationRequired (Clerk middleware)

Server runtime

You can set @runtime server to run a plugin in a sandboxed V8 isolate. This is only needed for future features like server-side secrets and persistent state.

Server runtime provides:

  • Deny-by-default — no filesystem, no network, no env vars, no child processes unless explicitly granted
  • SSRF protection — blocks requests to private IP ranges
  • 64MB memory limit
  • 5 second CPU timeout
  • npm module access via require()

Warning

Server runtime requires self-hosted infrastructure. It is not available on Vercel serverless.