Self-Hosted Facilitator

This tutorial shows how to deploy and operate your own T402 facilitator for greater control, privacy, and customization.

Why Self-Host?

FeaturePublic FacilitatorSelf-Hosted
SetupNone requiredInfrastructure needed
CostFree tier availableYour infrastructure
PrivacyShared logsFull control
CustomizationLimitedFull control
Rate LimitsSharedYour limits
SLABest effortYour guarantee

Architecture

┌─────────────────────────────────────────────────┐
│                  Your Infrastructure            │
│  ┌───────────┐  ┌───────────┐  ┌───────────┐   │
│  │ Facilitator│  │   Redis   │  │  Grafana  │   │
│  │  (Go API) │◀─│  (Cache)  │  │(Monitoring)│   │
│  └─────┬─────┘  └───────────┘  └───────────┘   │
│        │                                        │
│        ▼                                        │
│  ┌───────────────────────────────────────────┐ │
│  │           Blockchain RPC Nodes            │ │
│  │   EVM  │  TON  │  TRON  │  Solana  │ ...  │ │
│  └───────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘

Prerequisites

  • Docker and Docker Compose
  • Domain with SSL certificate
  • RPC endpoints for each supported chain
  • Private keys for facilitator wallets

Clone Repository

git clone https://github.com/t402-io/t402.git
cd t402/services/facilitator

Configure Environment

Create .env file:

# Server
PORT=8080
ENVIRONMENT=production
CORS_ALLOWED_ORIGINS=https://yourdomain.com
 
# Redis (required for idempotency)
REDIS_URL=redis://redis:6379
REDIS_PASSWORD=your_redis_password
 
# Rate Limiting
RATE_LIMIT_REQUESTS=1000
RATE_LIMIT_WINDOW=60
 
# API Keys (optional)
API_KEYS=key1:client1,key2:client2
API_KEY_REQUIRED=false
 
# EVM Chains
EVM_PRIVATE_KEY=0x...
ETH_RPC=https://eth.llamarpc.com
BASE_RPC=https://mainnet.base.org
ARBITRUM_RPC=https://arb1.arbitrum.io/rpc
OPTIMISM_RPC=https://mainnet.optimism.io
 
# TON
TON_MNEMONIC=word1 word2 ... word24
TON_RPC=https://toncenter.com/api/v2/jsonRPC
 
# TRON
TRON_PRIVATE_KEY=your_tron_private_key_hex
TRON_RPC=https://api.trongrid.io
 
# Solana
SVM_PRIVATE_KEY=your_solana_private_key_base58
SOLANA_RPC=https://api.mainnet-beta.solana.com

Docker Compose Setup

Create docker-compose.yaml:

version: '3.8'
 
services:
  facilitator:
    image: ghcr.io/t402-io/facilitator:latest
    ports:
      - "8080:8080"
    environment:
      - PORT=8080
      - ENVIRONMENT=production
      - REDIS_URL=redis://redis:6379
    env_file:
      - .env
    depends_on:
      - redis
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
 
  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis_data:/data
    restart: unless-stopped
 
  prometheus:
    image: prom/prometheus:latest
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    ports:
      - "9090:9090"
    restart: unless-stopped
 
  grafana:
    image: grafana/grafana:latest
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/dashboards:/etc/grafana/provisioning/dashboards
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
    restart: unless-stopped
 
volumes:
  redis_data:
  prometheus_data:
  grafana_data:

Deploy

# Pull latest images
docker-compose pull
 
# Start services
docker-compose up -d
 
# Check logs
docker-compose logs -f facilitator
 
# Verify health
curl http://localhost:8080/health

Configure Reverse Proxy

Nginx configuration:

server {
    listen 443 ssl http2;
    server_name facilitator.yourdomain.com;
 
    ssl_certificate /etc/letsencrypt/live/facilitator.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/facilitator.yourdomain.com/privkey.pem;
 
    location / {
        proxy_pass http://localhost:8080;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
 
        # CORS headers
        add_header Access-Control-Allow-Origin $http_origin;
        add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
        add_header Access-Control-Allow-Headers "Content-Type, Payment-Signature, Idempotency-Key";
    }
}

Configure Your Application

Update your application to use your facilitator:

import { createFacilitatorClient } from '@t402/core/server';
 
const facilitator = createFacilitatorClient({
  url: 'https://facilitator.yourdomain.com',
  // Optional: API key if required
  apiKey: 'your-api-key',
});

Wallet Management

Generate Wallets

# EVM - use any Ethereum wallet generator
# The same address works on all EVM chains
 
# TON - generate 24-word mnemonic
# Use official TON wallet or tools
 
# TRON - generate private key
# Use TronLink or tronweb
 
# Solana - generate keypair
solana-keygen new --outfile facilitator-wallet.json

Fund Wallets

Each facilitator wallet needs funds for gas:

ChainMinimum BalanceRecommended
Ethereum0.1 ETH0.5 ETH
Base0.01 ETH0.1 ETH
Arbitrum0.01 ETH0.1 ETH
TON1 TON10 TON
TRON100 TRX1000 TRX
Solana0.1 SOL1 SOL

Security Best Practices

⚠️

Never commit private keys to version control. Use secret management solutions.

  • Use hardware wallets or HSMs for production
  • Implement key rotation procedures
  • Set up alerts for low balances
  • Use separate wallets per chain

Monitoring

Prometheus Metrics

The facilitator exposes metrics at /metrics:

# prometheus.yml
scrape_configs:
  - job_name: 'facilitator'
    static_configs:
      - targets: ['facilitator:8080']
    metrics_path: /metrics

Key Metrics

MetricDescription
t402_verify_totalTotal verification requests
t402_verify_successSuccessful verifications
t402_settle_totalTotal settlement requests
t402_settle_successSuccessful settlements
t402_settle_duration_secondsSettlement latency

Grafana Dashboard

Import the included dashboard or create custom panels:

{
  "panels": [
    {
      "title": "Settlements per Minute",
      "type": "graph",
      "targets": [
        {
          "expr": "rate(t402_settle_success[1m])"
        }
      ]
    }
  ]
}

Scaling

Horizontal Scaling

services:
  facilitator:
    deploy:
      replicas: 3

Use a load balancer with sticky sessions for idempotency.

Redis Cluster

For high availability:

services:
  redis:
    image: redis:7-alpine
    command: redis-server --cluster-enabled yes

Troubleshooting

Common Issues

IssueSolution
Health check failingCheck Redis connection
Settlement timeoutVerify RPC endpoints
Signature invalidCheck private key format
Rate limit exceededIncrease limits or add API keys

Logs

# View facilitator logs
docker-compose logs -f facilitator
 
# Filter by level
docker-compose logs facilitator 2>&1 | grep ERROR