Docs

Self-hosting

Run your own Porch server with in-memory or Convex storage.

Porch is open source (MIT). Run your own copy for a private instance, different limits, or a domain of your own. It's a Node.js 22+ server with two storage options:

Storage Use it for Notes
Memory (default) Development, single-instance servers Everything is lost on restart. No setup.
Convex Production Durable, shared by any number of server instances, cleans up on its own. Waits subscribe to queries, so they wake the moment a request is stored.

Run it locally#

bash
git clone https://github.com/computer-agent-labs/porch.git
cd porch
yarn install
yarn dev

Connect a client to http://localhost:8787/mcp. Senders need to reach your machine for inbox URLs to work, so for real webhooks run Porch somewhere public.

Docker#

bash
docker build -t porch .
docker run -p 8787:8787 -e PORCH_PUBLIC_URL=https://porch.example.com porch

Use Convex for storage#

  1. Deploy the functions in convex/ to your Convex project:

    bash
    npx convex deploy
  2. Choose a long random secret and set it on the deployment:

    bash
    npx convex env set PORCH_CONVEX_SECRET <secret> --prod
  3. Start the server with CONVEX_URL set to your deployment URL and PORCH_CONVEX_SECRET set to the same secret.

Every Convex function checks the secret, because Convex functions are reachable by anyone who knows the deployment URL.

Deploy it#

Any platform that runs a long-lived container works. A few things matter:

The repository includes the AWS setup behind https://porch.okibi.ai: CloudFormation templates for ECS Fargate behind an Application Load Balancer, and a deploy script (deploy/aws).

Configuration#

All settings are environment variables.

Variable Default Meaning
PORT 8787 Port to listen on.
PORCH_HOST 0.0.0.0 Interface to bind.
PORCH_PUBLIC_URL from the Host header Base URL used in inbox links. Set it in production.
PORCH_TRUST_PROXY false Express trust proxy setting: true, a hop count, or subnets.
CONVEX_URL unset Convex deployment URL. When set, Porch stores everything in Convex.
PORCH_CONVEX_SECRET unset Shared secret the Convex functions check. Required with CONVEX_URL.
PORCH_MAX_INBOXES 1000 Server-wide cap on active inboxes.
PORCH_CAPACITY_MODE reject reject refuses new inboxes at the cap. per-ip lets IPs holding fewer than PORCH_PER_IP_MAX_INBOXES past it.
PORCH_PER_IP_MAX_INBOXES 3 Per-IP allowance once the cap is reached, in per-ip mode.
PORCH_HARD_MAX_INBOXES 2 × the cap Absolute ceiling in per-ip mode.
PORCH_DEFAULT_INBOX_TTL_MINUTES 1440 Inbox lifetime when the agent doesn't choose one.
PORCH_MIN_INBOX_TTL_MINUTES 5 Shortest lifetime an agent can choose.
PORCH_MAX_INBOX_TTL_MINUTES 10080 Longest lifetime an agent can choose (7 days).
PORCH_INBOX_IDLE_TIMEOUT_MINUTES 240 Inboxes are deleted after this long without a keyed tool call.
PORCH_MAX_REQUESTS_PER_INBOX 1000 Requests kept per inbox.
PORCH_REQUEST_TTL_MINUTES 240 How long each request is kept.
PORCH_MAX_BODY_BYTES 262144 Largest body recorded (256 KB).
PORCH_MAX_INBOX_BYTES 8388608 Body bytes kept per inbox (8 MB).
PORCH_MAX_WAIT_SECONDS 300 Longest wait an agent can ask for.
PORCH_ADMIN_TOKEN unset Enables GET /admin/stats with Authorization: Bearer <token>.
PORCH_LOG_LEVEL info debug, info, warn, error or silent. Logs never include request contents.

A per-IP cap isn't on by default because many agents share an IP: MCP calls from hosted assistants, for example, come from the assistant provider's servers.

Monitoring#

With PORCH_ADMIN_TOKEN set, GET /admin/stats?days=30 reports daily counts: inboxes created and refused, requests captured, approval links, custom responses, and redirects, including the hosts redirects point to. Use it to spot abuse of custom responses.