Skip to content

Latest commit

 

History

History
583 lines (428 loc) · 16.6 KB

File metadata and controls

583 lines (428 loc) · 16.6 KB

Infrastructure Guide

This guide is for operators who need to deploy contracts, run indexers, or build on top of XPR Trustless Agents.

To deploy an agent, use npx create-xpr-agent my-agent — see the main README.


Prerequisites

  • Node.js 18+
  • Proton CLI: npm install -g @proton/cli
  • XPR Network account with sufficient resources

Smart Contract Deployment

1. Build Contracts

# Clone repository
git clone https://github.com/XPRNetwork/xpr-agents
cd xpr-agents

# Build each contract
cd contracts/agentcore && npm install && npm run build && cd ../..
cd contracts/agentfeed && npm install && npm run build && cd ../..
cd contracts/agentvalid && npm install && npm run build && cd ../..
cd contracts/agentescrow && npm install && npm run build && cd ../..

2. Create Contract Accounts

proton chain:set proton-test  # or proton for mainnet

# Create accounts (requires XPR for RAM)
proton account:create agentcore
proton account:create agentfeed
proton account:create agentvalid
proton account:create agentescrow

3. Deploy Contracts

# Testnet
./scripts/deploy-testnet.sh

# Mainnet (interactive, with safety confirmations)
./scripts/deploy-mainnet.sh

Or manually:

proton contract:set agentcore ./contracts/agentcore/assembly/target
proton contract:set agentfeed ./contracts/agentfeed/assembly/target
proton contract:set agentvalid ./contracts/agentvalid/assembly/target
proton contract:set agentescrow ./contracts/agentescrow/assembly/target

4. Enable Inline Actions

proton contract:enableinline agentcore
proton contract:enableinline agentfeed
proton contract:enableinline agentvalid
proton contract:enableinline agentescrow

5. Initialize Contracts

Use the init script:

# Testnet (lower stakes for testing)
# NOTE: agentcore min_stake is in XPR units (getSystemStake divides raw by 10000)
#        agentvalid min_stake is in raw units (4 decimal places)
./scripts/init-contracts.sh proton-test agentcore agentfeed agentvalid agentescrow 100 5000000 100000 100

# Mainnet (production stakes)
./scripts/init-contracts.sh proton agentcore agentfeed agentvalid agentescrow 1000 50000000 100000 100

Or manually:

# agentcore: min_stake (XPR units!), claim_fee (raw units), sibling contracts
# IMPORTANT: agentcore min_stake is in XPR, NOT raw units
#   getSystemStake() divides voter.staked by 10000, so comparison is in XPR
#   Testnet: min_stake=100 (100 XPR), Mainnet: min_stake=1000 (1000 XPR)
proton action agentcore init '{"owner":"agentcore","min_stake":1000,"claim_fee":100000,"feed_contract":"agentfeed","valid_contract":"agentvalid","escrow_contract":"agentescrow"}' agentcore

# agentfeed: core_contract
proton action agentfeed init '{"owner":"agentfeed","core_contract":"agentcore"}' agentfeed

# agentvalid: core_contract, min_stake (raw units — contract-stored stake)
#   Testnet: min_stake=500 XPR (5000000 raw), Mainnet: min_stake=5000 XPR (50000000 raw)
proton action agentvalid init '{"owner":"agentvalid","core_contract":"agentcore","min_stake":50000000}' agentvalid

# agentescrow: core_contract, feed_contract, platform_fee (100 = 1%)
proton action agentescrow init '{"owner":"agentescrow","core_contract":"agentcore","feed_contract":"agentfeed","platform_fee":100}' agentescrow

6. Test Actions

./scripts/test-actions.sh proton-test

Running the Indexer

The indexer streams blockchain events and provides a REST API for fast queries.

Configuration

cd indexer
cp .env.example .env

Edit .env:

PORT=3001
DB_PATH=./data/agents.db

# Testnet:
# HYPERION_ENDPOINTS=https://api-xprnetwork-test.saltant.io
# Mainnet:
HYPERION_ENDPOINTS=https://proton.eosusa.io

AGENT_CORE_CONTRACT=agentcore
AGENT_FEED_CONTRACT=agentfeed
AGENT_VALID_CONTRACT=agentvalid
AGENT_ESCROW_CONTRACT=agentescrow

Running

npm install
npm start

API Endpoints

Endpoint Description
GET /api/agents List agents (filter: active_only, sort)
GET /api/agents/:account Get agent by account
GET /api/agents/:account/feedback Get agent's feedback
GET /api/agents/:account/validations Get agent's validations
GET /api/validators List validators (filter: active_only)
GET /api/validators/:account Get validator by account
GET /api/jobs List jobs (filter: state, client, agent)
GET /api/jobs/:id Get job by ID
GET /api/jobs/:id/milestones Get job milestones
GET /api/jobs/:id/disputes Get job disputes
GET /api/arbitrators List arbitrators (filter: active_only)
GET /api/arbitrators/:account Get arbitrator by account
GET /api/plugins List plugins (filter: category, verified_only)
GET /api/stats Aggregate statistics
GET /api/search?q=term Search agents by name/account
GET /api/events Recent events (filter: contract, action)
GET /health Health check

Docker Deployment

cd indexer
docker build -t xpr-agents-indexer .
docker run -p 3001:3001 -v ./data:/app/data xpr-agents-indexer

Snapshot Seeding

The indexer uses synthetic IDs. For new deployments, either:

  1. Replay from genesis (recommended):

    rm ./data/agents.db
    npm start  # Will replay all history
  2. Seed from chain state:

    # Export current state
    proton table agentcore agents --limit 10000 > agents.json
    # Import (use provided script)
    node scripts/seed-from-export.js

Running the Frontend

cd frontend
npm install
npm run dev

Environment Variables

Create .env.local:

# Mainnet (default):
NEXT_PUBLIC_NETWORK=mainnet

# Testnet:
# NEXT_PUBLIC_NETWORK=testnet

# Override individual settings (optional — auto-configured from NEXT_PUBLIC_NETWORK)
# NEXT_PUBLIC_RPC_URL=https://proton.eosusa.io
# NEXT_PUBLIC_INDEXER_URL=http://localhost:3001

Production Build

npm run build
npm start

Contract Addresses

Testnet

Contract Account
agentcore agentcore
agentfeed agentfeed
agentvalid agentvalid
agentescrow agentescrow

Mainnet

Contract Account
agentcore agentcore
agentfeed agentfeed
agentvalid agentvalid
agentescrow agentescrow

Mainnet Parameters:

Parameter Value Description
Agent min stake 1,000 XPR Minimum system stake to register an agent
Validator min stake 5,000 XPR (50000000) Minimum to register as validator
Claim fee 10 XPR (100000) Refundable deposit for claiming an agent
Platform fee 1% (100 basis points) Fee on escrow payouts

Mainnet RPC Endpoints:

Provider URL
EOS USA https://proton.eosusa.io
ProtNZ https://proton.protonnz.com

Mainnet Hyperion Endpoints:

Provider URL
EOS USA https://proton.eosusa.io

Monitoring

Contract Tables

# View all agents
proton table agentcore agents

# View feedback
proton table agentfeed feedback

# View validators
proton table agentvalid validators

# View jobs
proton table agentescrow jobs

Indexer Health

curl http://localhost:3001/health
curl http://localhost:3001/api/stats

Security Considerations

Contract Security

  • Contracts have been through 2 rounds of security audit (see SECURITY_AUDIT.md)
  • Validators have slashable stake to prevent collusion
  • Arbitrators must stake to be eligible
  • All payments go through escrow

Indexer Security

  • Indexer is read-only from chain data
  • API should be rate-limited in production
  • Database should be backed up regularly

Key Management

  • Never commit private keys
  • Use separate accounts for each contract
  • Consider multisig for contract owner accounts

Troubleshooting

Contract deployment fails

Error: Account does not have enough RAM

→ Buy more RAM at resources.xprnetwork.org

Indexer missing events

→ Check Hyperion endpoint is accessible → Verify contract accounts in config match deployed accounts → Consider replaying from genesis

Frontend can't connect

→ Check RPC endpoint is accessible → Verify chain ID matches network → Check browser console for CORS errors


Exposing an Agent with Cloudflare Tunnel

If your agent runs on a home server or machine without a public IP, use Cloudflare Tunnel to expose it securely. No port forwarding required — the tunnel creates an outbound-only connection to Cloudflare's edge, providing free TLS and DDoS protection.

1. Install cloudflared

# macOS (ARM)
curl -fsSL https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-darwin-arm64.tgz \
  | tar -xz -C /usr/local/bin/

# macOS (Intel)
curl -fsSL https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-darwin-amd64.tgz \
  | tar -xz -C /usr/local/bin/

# Linux
curl -fsSL https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 \
  -o /usr/local/bin/cloudflared && chmod +x /usr/local/bin/cloudflared

2. Authenticate and create tunnel

# Login (opens browser to authorize your Cloudflare domain)
cloudflared tunnel login

# Create a named tunnel
cloudflared tunnel create my-agent

# Add DNS routes (replace with your domain)
cloudflared tunnel route dns my-agent agent.yourdomain.com
cloudflared tunnel route dns my-agent api.yourdomain.com   # optional: indexer API

3. Configure

Create ~/.cloudflared/config.yml:

tunnel: <TUNNEL-UUID>
credentials-file: /path/to/.cloudflared/<TUNNEL-UUID>.json

ingress:
  - hostname: agent.yourdomain.com
    service: http://localhost:8080
  - hostname: api.yourdomain.com
    service: http://localhost:3001
  - service: http_status:404

4. Install as a service

macOS (launchd):

cloudflared service install        # starts on login
# or
sudo cloudflared service install   # starts on boot

Linux (systemd):

sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared

5. Update agent config

Set AGENT_PUBLIC_URL in your .env:

AGENT_PUBLIC_URL=https://agent.yourdomain.com

Restart the agent — it will auto-update its on-chain endpoint.

6. Verify

curl https://agent.yourdomain.com/health
curl https://agent.yourdomain.com/.well-known/agent.json

Managed Agent Deployment (deploy.xpragents.com)

The deploy service provides 1-click agent provisioning via a web wizard at deploy.xpragents.com. It creates an XPR account, registers the agent on-chain, and deploys a Cloudflare Worker running OpenClaw with all XPR agent skills.

Two Deployment Modes

Mode How It Works Deploy Time When Used
Single-tenant One CF Worker per agent (xpr-{owner}-{agent}.workers.dev) ~5 min Default when KV not configured
Multi-tenant Shared CF Worker + per-agent KV config ~2 sec When CF_KV_NAMESPACE_ID + CF_GATEWAY_WORKER_NAME env vars set

Multi-Tenant Architecture

In multi-tenant mode, all agents share a single Cloudflare Worker deployment (the "gateway"). Each agent gets its own:

  • KV config entry with API keys, XPR account, tokens
  • Sandbox container (Cloudflare Durable Object + container instance)
  • R2 storage prefix for persistent data
Request: https://alice.xpragents.com/chat
  |
  v
Gateway Worker: extract subdomain "alice"
  |
  v
KV lookup: AGENT_KV.get("agent:alice") -> {xprAccount, anthropicApiKey, ...}
  |
  v
Sandbox: getSandbox(env.Sandbox, "alice") -> separate container per agent
  |
  v
Merge env: global Worker secrets + tenant KV config -> container env vars
  |
  v
Proxy: HTTP/WebSocket to OpenClaw gateway on port 18789

Key Components

Component Location Description
Deploy Frontend deploy/frontend/ (Vercel) React wizard for agent provisioning
Deploy Backend xpr-deploy-service (Railway) Express API for account creation + CF deployment
Gateway Worker moltworker-xpr (CF Workers) Shared Cloudflare Worker running OpenClaw containers
Agent Registry agentcore (on-chain) Smart contract storing agent metadata + endpoints

Gateway Startup Flow

When a request hits a multi-tenant agent:

  1. Tenant resolution — Extract subdomain from hostname, look up KV config
  2. Env merge — Overlay tenant-specific config onto global Worker env
  3. Gateway prewarmwaitUntil(ensureMoltbotGateway()) starts the container BEFORE auth (critical — otherwise auth middleware blocks container startup)
  4. Wallet auth — User signs with XPR wallet, JWT issued (stored in localStorage)
  5. Proxy — HTTP/WebSocket proxied to OpenClaw gateway inside container

Required Environment Variables

Deploy Service (Railway)

# Core
XPR_RPC_ENDPOINT=https://rpc.api.mainnet.metalx.com
XPR_NETWORK=mainnet
API_SECRET=<openssl rand -hex 32>
WEBHOOK_SECRET=<openssl rand -hex 32>
KEY_ENCRYPTION_SECRET=<openssl rand -hex 32>   # AES-256, exactly 64 hex chars
DEPLOY_ACCOUNT=agentcreate
DEPLOY_PRIVATE_KEY=PVT_K1_...
CF_ACCOUNT_ID=<cloudflare-account-id>
CF_API_TOKEN=<cloudflare-api-token>
JWT_SECRET=<openssl rand -hex 32>

# Multi-tenant mode (enables shared gateway)
# Replace with your own KV namespace ID — the value below is the production ID
CF_KV_NAMESPACE_ID=<your-kv-namespace-id>
CF_GATEWAY_WORKER_NAME=xpr-agent-sandbox

Gateway Worker (CF Secrets)

Legacy path. The Cloudflare Worker / moltworker deployment was deprecated in favor of Railway mode (cleaner container model, fewer cold-start bugs). The XPR_PRIVATE_KEY shown below only exists because the CF sandbox can't shell out to a host proton binary — the standalone scaffold and the Pinata harness both route signing through the proton CLI keychain and have no XPR_PRIVATE_KEY env var at all.

# Always required
ANTHROPIC_API_KEY=<key>
MOLTBOT_GATEWAY_TOKEN=<openssl rand -hex 32>

# Single-tenant (set per worker) — legacy CF sandbox only.
# DO NOT use these on the standalone scaffold or in a Pinata harness:
# those agents refuse to start when XPR_PRIVATE_KEY is set.
XPR_ACCOUNT=<agent-account>
XPR_PRIVATE_KEY=PVT_K1_...
XPR_OWNER_ACCOUNT=<owner-account>

# Multi-tenant agents get these from KV instead of Worker secrets

Token Architecture

Three different tokens serve different layers — do not confuse them:

Token Where Set Purpose
MOLTBOT_GATEWAY_TOKEN CF Worker secret Master gateway auth token. The Worker reads this.
OPENCLAW_GATEWAY_TOKEN Container env var Derived from above. The Worker passes MOLTBOT_GATEWAY_TOKEN as OPENCLAW_GATEWAY_TOKEN to the container. OpenClaw requires this for --bind lan (since v2026.1.29).
OPENCLAW_HOOK_TOKEN Container env var / KV Webhook auth token for indexer-to-agent communication. Completely separate from gateway auth.

The gateway Worker automatically:

  1. Passes MOLTBOT_GATEWAY_TOKEN as OPENCLAW_GATEWAY_TOKEN to the container
  2. Injects the token server-side into WebSocket URLs via wsConnect()
  3. Users never see any token — it's handled transparently

Cold start takes ~90 seconds. The loading page polls /api/status every 5 seconds with a 5-minute max timeout.

R2 Storage Isolation

Multi-tenant agents use prefix-based isolation in a shared R2 bucket:

  • Single-tenant: r2:moltbot-data/openclaw/
  • Multi-tenant: r2:moltbot-data/{agentName}/openclaw/

Operational Commands

Replace $KV_NS_ID with your CF_KV_NAMESPACE_ID value.

# Check KV config for an agent
npx wrangler kv key get --remote --namespace-id $KV_NS_ID "agent:myagent"

# List all tenant configs
npx wrangler kv key list --remote --namespace-id $KV_NS_ID

# View live gateway logs
cd /tmp/moltworker-xpr && npx wrangler tail

# Build and deploy gateway
cd /tmp/moltworker-xpr && npm run build && npx wrangler deploy

Important: Always run npm run build before npx wrangler deploy — Wrangler uses pre-built dist/ and does NOT rebuild automatically.


Architecture Details

See CLAUDE.md for:

  • Complete table schemas
  • State machine diagrams
  • Staking model details
  • Trust score algorithm

See MODEL.md for:

  • Economic model
  • Incentive design
  • Fee structures