CoordinatorSetup & Deployment

Coordinator Setup

Deploy and run the Veil coordinator service.

Overview

The coordinator watches for due schedules, stores recipient payloads and proofs off-chain, and drives the execution flow through MagicBlock ER.

Flow:

  1. Accept schedule registration payloads over POST /api/schedules
  2. Validate registration against the on-chain vault and schedule
  3. Poll Solana for schedules due for execution
  4. Delegate schedule state to ER
  5. Execute claim_payment attempts
  6. Commit final state back to Solana
  7. Record execution runs and attempts in PostgreSQL

Prerequisites

  • Node.js 18+
  • PostgreSQL database
  • Redis database (recommended for distributed rate limiting)
  • ER authority keypair
  • Solana RPC endpoint

Installation

cd coordinator
npm install

Database Setup

  1. Create PostgreSQL database
createdb veil_coordinator
  1. Configure DATABASE_URL in .env
DATABASE_URL=postgresql://user:password@localhost:5432/veil_coordinator
  1. Run migrations
npm run db:migrate
  1. Optional: inspect data with Drizzle Studio
npm run db:studio

Configuration

Copy .env.example to .env and configure the following variables.

Solana Configuration

SOLANA_RPC_URL=https://api.devnet.solana.com
CLAIM_EXECUTION_LAYER=solana

Claim execution layer:

  • solana - execute payout claims on the base Solana RPC
  • er - execute payout claims on the ER RPC

Keep this on solana unless your payout flow is explicitly prepared for ER-side token-account writes.

ER Configuration

ER_RPC_URL=https://devnet-as.magicblock.app/
ER_VALIDATOR=MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57
ER_AUTHORITY_KEYPAIR_PATH=./er-authority-keypair.json

You can also load the ER authority from environment variables instead of a file:

ER_AUTHORITY_KEYPAIR_JSON=[1,2,3,...]

or

ER_AUTHORITY_KEYPAIR_B64=base64-encoded-json

Server Configuration

PORT=3001
POLL_INTERVAL_MS=60000
API_JSON_LIMIT=1mb
MAX_EXECUTION_ATTEMPTS=5
RETRY_BASE_DELAY_MS=30000
RETRY_MAX_DELAY_MS=7200000
MAX_RUNNABLE_EXECUTIONS_PER_POLL=10

Metrics Configuration

METRICS_ENABLED=true
METRICS_PUBLIC=false
METRICS_AUTH_TOKEN=replace-with-a-random-secret

Metrics access:

  • METRICS_ENABLED=false disables /api/metrics
  • METRICS_PUBLIC=false protects /api/metrics behind a bearer token
  • METRICS_AUTH_TOKEN is required when metrics are private

Rate Limiting Configuration

RATE_LIMIT_ENABLED=true
RATE_LIMIT_DRY_RUN=false
RATE_LIMIT_REDIS_URL=redis://default:password@redis-host:6379
RATE_LIMIT_REDIS_KEY_PREFIX=veil:rate-limit
 
RATE_LIMIT_REGISTER_CAPACITY=5
RATE_LIMIT_REGISTER_REFILL_RATE_PER_SECOND=0.0016666667
RATE_LIMIT_REGISTER_CONCURRENCY=3
 
RATE_LIMIT_SCHEDULE_READ_CAPACITY=60
RATE_LIMIT_SCHEDULE_READ_REFILL_RATE_PER_SECOND=1
 
RATE_LIMIT_EXECUTION_HISTORY_CAPACITY=30
RATE_LIMIT_EXECUTION_HISTORY_REFILL_RATE_PER_SECOND=1

How the limiter behaves:

  • POST /api/schedules is wallet-aware when vaultEmployer is present, otherwise IP-based
  • schedule registration also has a concurrency cap
  • schedule reads and execution-history reads use separate token-bucket policies
  • Redis is the primary backend
  • if Redis is unavailable, registration falls back to in-memory limiting and read routes fail open

Safe rollout:

  1. Deploy with RATE_LIMIT_DRY_RUN=true
  2. Verify logs and normal dashboard traffic
  3. Flip RATE_LIMIT_DRY_RUN=false
  4. Redeploy to enforce limits

Complete Example .env

# Solana
SOLANA_RPC_URL=https://api.devnet.solana.com
CLAIM_EXECUTION_LAYER=solana
 
# ER
ER_RPC_URL=https://devnet-as.magicblock.app/
ER_VALIDATOR=MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57
ER_AUTHORITY_KEYPAIR_PATH=./er-authority-keypair.json
 
# Server
PORT=3001
POLL_INTERVAL_MS=60000
API_JSON_LIMIT=1mb
MAX_EXECUTION_ATTEMPTS=5
RETRY_BASE_DELAY_MS=30000
RETRY_MAX_DELAY_MS=7200000
MAX_RUNNABLE_EXECUTIONS_PER_POLL=10
 
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/veil_coordinator
DB_POOL_MAX=10
 
# Metrics
METRICS_ENABLED=true
METRICS_PUBLIC=false
METRICS_AUTH_TOKEN=replace-with-a-random-secret
 
# Rate Limiting
RATE_LIMIT_ENABLED=true
RATE_LIMIT_DRY_RUN=false
RATE_LIMIT_REDIS_URL=redis://default:password@redis-host:6379
RATE_LIMIT_REDIS_KEY_PREFIX=veil:rate-limit
RATE_LIMIT_REGISTER_CAPACITY=5
RATE_LIMIT_REGISTER_REFILL_RATE_PER_SECOND=0.0016666667
RATE_LIMIT_REGISTER_CONCURRENCY=3
RATE_LIMIT_SCHEDULE_READ_CAPACITY=60
RATE_LIMIT_SCHEDULE_READ_REFILL_RATE_PER_SECOND=1
RATE_LIMIT_EXECUTION_HISTORY_CAPACITY=30
RATE_LIMIT_EXECUTION_HISTORY_REFILL_RATE_PER_SECOND=1

ER Validators

Available devnet validators:

  • US: MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd
  • EU: MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e
  • Asia: MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57
  • TEE: FnE6VJT5QNZdedZPnCoLsARgBwoE6DeJNjBs2H1gySXA

Running

Development

npm run dev

Production

npm run build
npm run start

Production Considerations

  • use environment-specific RPC endpoints
  • secure the ER authority keypair and never commit it
  • protect /api/metrics unless you explicitly want it public
  • use Redis for distributed rate limiting across coordinator replicas
  • roll out rate limiting in dry-run mode before enforcing it
  • monitor both RPC usage and limiter backend health