Install — Self-host gateway
This is Mode B from the install overview. You run your own gateway, your own database, and point your bridges at it. We ship the same code we run in production at gateway.arp.run; you deploy it on infra you control.
When to pick this: privacy-strict orgs, regulated industries, or anyone who wants connection metadata + audit chains to stay on infrastructure they control. The managed cloud doesn't see encrypted message bodies, but it does see who-talks-to-whom — Mode B keeps that on your side too.
What you're deploying
┌─ Your infrastructure ───────────────────────────────────┐
│ │
│ apps/cloud-gateway (stateless Node service) │
│ │ │
│ ├── Postgres (your DB) │
│ ├── scope-catalog (HTTP service) │
│ └── audit storage (in same Postgres) │
│ │
│ gateway.<your-domain> (TLS termination) │
│ │
└─────────────────────────────────────────────────────────┘
▲ ▲
│ outbound WS │ HTTPS
│ │
bridges (arpc) owner dashboard
(you also self-host
apps/cloud-app)
The two services you operate:
apps/cloud-gateway— DIDComm fanout, PDP integration, audit chain. Stateless, scales horizontally, talks to Postgres.apps/cloud-app— Next.js owner dashboard (sign-in, pairing UX, connection management). Also stateless.
Plus Postgres (any Postgres works; we use Neon at the managed cloud).
Prerequisites
- A Linux host or container platform (Railway, Fly, Render, your own VPS / Kubernetes)
- A Postgres database (≥14)
- A domain you control with TLS (e.g.,
gateway.example.comandapp.example.com) - Node.js 24+ on the deploy target
- The ARP monorepo cloned locally
Steps
1. Clone and build
git clone https://github.com/KybernesisAI/arp
cd arp
pnpm install
pnpm --filter @kybernesis/arp-cloud-gateway build
pnpm --filter @kybernesis/arp-cloud-app build
2. Provision Postgres + run migrations
export DATABASE_URL=postgres://…
pnpm --filter @kybernesis/arp-cloud-db migrate
Schema includes connections, audit chain rows, pairing state, scope catalog cache.
3. Deploy the gateway
Minimum env vars:
DATABASE_URL=postgres://…
GATEWAY_WS_URL=wss://gateway.example.com
SCOPE_CATALOG_URL=https://gateway.example.com/scope-catalog
SIGNING_KEY=<base58 ed25519 secret> # gateway's own signing identity
PORT=8080
Deploy apps/cloud-gateway as a long-running Node service. The container or platform should give it a persistent WebSocket-friendly endpoint with TLS termination in front of it.
4. Deploy the dashboard
GATEWAY_API_URL=https://gateway.example.com
DATABASE_URL=postgres://…
NEXTAUTH_SECRET=…
PORT=3000
Deploy apps/cloud-app as a Next.js app. Owners sign in here, pair agents, manage connections, view audit.
5. Point bridges at your gateway
On any agent host running arpc, edit the agent folder's arp.json to override the gateway URL:
{
"gateway_ws_url": "wss://gateway.example.com",
"scope_catalog_url": "https://gateway.example.com/scope-catalog"
}
Restart the bridge:
arpc service restart
arpc host status # confirms it's connected to your gateway
Differences from the managed cloud
Managed (gateway.arp.run) | Self-host (this guide) | |
|---|---|---|
| You operate | Bridge only | Gateway, Postgres, dashboard, bridge |
| TLS / certs | We handle | You handle (Let's Encrypt, your CA) |
| Postgres | Multi-tenant Neon, ours | Yours |
| Audit storage | Our DB | Your DB |
| Dashboard URL | cloud.arp.run | app.example.com |
| Connection metadata visibility | We see it | Only you |
| Updates | Auto | You pull + redeploy |
Operating notes
- Stateless gateway. Run multiple instances behind a load balancer; they coordinate via Postgres + signed envelopes. No sticky sessions required for HTTP, but WebSocket connections from bridges should be sticky to the same gateway pod (most platforms handle this by default).
- Postgres backups. Audit chains are append-only and signed, so corruption is detectable — but a dropped DB still loses recent state. Daily backups are the floor.
- Scope catalog. Bridges fetch templates from
scope_catalog_url. The gateway serves the same JSON the managed cloud does, compiled frompackages/arp-scope-catalog/scopes/*.yaml. Pin a version in your deploys so policy compilation stays deterministic. - Migration to/from managed cloud. The handoff bundle is portable. Switching is a
gateway_ws_urlchange inarp.jsonand aservice restart. Existing connections keep working because they bind to the agent's DID, not the gateway URL.
Hardening checklist
- Postgres on a private network, not public
- Gateway behind a real TLS terminator (Caddy, Nginx, your platform's edge)
-
SIGNING_KEYstored in a secret manager, not env file in repo - Audit chain export job (e.g., daily snapshot to S3) for forensic preservation
- Monitoring: gateway WS connection count, Postgres connection pool, audit-write latency
- Rate-limiting on the public WS endpoint (the bridge protocol allows reasonable abuse without it)
Related
- Install overview — picking a deployment mode
- Local (Mac) — Mode A install on macOS
- VPS — Mode A install on a Linux VPS
- SDKs — package reference, including
apps/cloud-gateway