Skip to content
OVA

Advanced

Self-hosting

OVA is open-source and can be self-hosted on your own infrastructure. This gives you complete control over your data and allows customization.

Prerequisites

  • Node.js 20.9+ (22 recommended)
  • Docker Engine on the same host, for the per-session desktops
  • A Solana wallet address for receiving deposits (mainnet only; devnet uses a derived test treasury)
  • A Surplus Intelligence buyer key and/or an OpenRouter key (or direct Anthropic / OpenAI / Google / Moonshot keys)

Quick start

1. Clone the repository

bash
git clone <this-repository>
cd ova

2. Configure environment

bash
cp .env.example .env
openssl rand -base64 48   # paste into JWT_SECRET

Every variable is listed and commented in .env.example. The ones that matter:

  • JWT_SECRET — Signs sessions. Production refuses to start without it.
  • NEXT_PUBLIC_APP_URL — Your public origin, e.g. https://agent.example.com
  • SURPLUS_API_KEY — Primary inference through the Surplus Intelligence market (see Inference market)
  • OPENROUTER_API_KEY — Fallback router when the market errors or has no offer
  • DEPOSIT_WALLET_ADDRESS and SOLANA_RPC_URL — Needed on mainnet. Only the public address is used; the app never holds a private key.

3. Build the sandbox image

bash
npm run sandbox:build

The app creates the solana-agent-network bridge on first launch.

4. Start the application

bash
npm ci
npm run build   # NEXT_PUBLIC_* values are inlined here
npm start       # serves on port 3847

Or run it in a container with docker compose up -d --build app. The compose file reads .env, mounts the Docker socket and uses host networking so the desktop proxy can reach sandbox ports on 127.0.0.1.

Environment variables

VariableDescriptionRequired
JWT_SECRETSession signing secretYes
NEXT_PUBLIC_APP_URLPublic origin of the siteYes
SURPLUS_API_KEYPrimary: Surplus Intelligence marketYes*
OPENROUTER_API_KEYFallback router for all modelsYes*
ANTHROPIC_API_KEYDirect AnthropicNo
OPENAI_API_KEYDirect OpenAINo
GOOGLE_API_KEYDirect GoogleNo
MOONSHOT_API_KEYDirect MoonshotNo
DOMAINPublic hostname for Caddy / https://DOMAINVPS
NETWORKdevnet, mainnet or testnet (alias for the cluster vars)VPS
SOLANA_CLUSTERmainnet, devnet or testnet (devnet)No
SOLANA_RPC_URLDedicated RPC for deposit checksMainnet
DEPOSIT_WALLET_ADDRESSAddress that receives depositsMainnet
PAYOUT_WALLET_SECRET_KEYHot wallet that pays withdrawalsTo enable withdrawals
WITHDRAW_ADMIN_SECRETBearer token to approve held payoutsIf you set a threshold
PAYMENT_TOKEN_MINTSPL token mint instead of SOLNo
SANDBOX_PROVIDERdocker (default), local or autoNo
MAX_SPEND_PER_DAYPer-wallet daily cap in lamportsNo
SESSION_MAX_STEPSMax agent steps per task (80)No

* Provide SURPLUS_API_KEY, OPENROUTER_API_KEY or a direct provider key. Routes are tried in order: Surplus, then a direct key, then OpenRouter. Without any key the app and desktop still run, and chat replies that no model is configured. A legacy LLM_API_KEY + LLM_API_BASE_URL still works as a custom OpenAI-compatible endpoint.

Production deployment

On one Debian 13 VPS, use the repo deploy/ pack: point an A record at the server, then sudo DOMAIN=agent.example.com NETWORK=mainnet bash deploy/bootstrap.sh. It installs Node, Docker, Caddy (automatic HTTPS and WebSockets for /desktop/), systemd, ufw (22/80/443) and SQLite backups. Copy secrets into .env afterwards. Steps and the recommended Hetzner size are in deploy/README.md.

  • Put TLS in front and forward Host, X-Forwarded-Host and X-Forwarded-Proto. Sign-in binds to the request origin.
  • Allow WebSocket upgrades on /desktop/ for the live desktop stream.
  • Back up the SQLite file at DATABASE_PATH. It holds balances and the deposit ledger.
  • Keep sandboxes away from the metadata service, private ranges and the host:
bash
sudo scripts/sandbox-firewall.sh

Run it once the first session has created solana-agent-network, and again after Docker restarts. Sandboxes keep internet access; the live desktop proxy keeps working.

Search documentation

Jump to a docs page