Welcome to the SoroTask Keeper network! This guide provides step-by-step instructions on how to set up and run a SoroTask Keeper bot. By running a keeper, you help ensure tasks in the SoroTask network are executed reliably and on time.
See the centralized Glossary for definitions of domain-specific terms like Keeper, Resolver, and TaskConfig.
- Prerequisites
- Environment Variables
- RPC Load Balancer Configuration
- Setup Instructions
- P2P Keeper Discovery
- Dead-Letter Queue
- Serverless Resolvers
- Mock Soroban RPC
- Chaos Testing
- Docker Deployment
- Troubleshooting
Before you begin, ensure you have the following installed on your machine:
The keeper bot requires certain configuration details to interact with the Stellar/Soroban network.
Create a .env file in the keeper directory and configure the following variables:
# The URL of the Soroban RPC server you are connecting to
SOROBAN_RPC_URL="https://rpc-futurenet.stellar.org"
# The network passphrase for the network you are targeting
NETWORK_PASSPHRASE="Test SDF Future Network ; October 2022"
# The secret key of the keeper account that will submit the transactions
KEEPER_SECRET="S..."
# The contract ID of the deployed SoroTask contract
CONTRACT_ID="C..."
# Polling interval in milliseconds (default: 10000ms = 10 seconds)
POLLING_INTERVAL_MS=10000
# Maximum number of concurrent task reads during polling (default: 10)
MAX_CONCURRENT_READS=10
# Maximum number of concurrent task executions (default: 3)
MAX_CONCURRENT_EXECUTIONS=3
# Maximum task ID to check (default: 100) - or use TASK_IDS for specific tasks
MAX_TASK_ID=100
# Optional: Comma-separated list of specific task IDs to monitor
# TASK_IDS="1,2,3,5,8"
# Wait for transaction confirmation (default: true, set to 'false' to disable)
WAIT_FOR_CONFIRMATION=true
# Structured logging
LOG_LEVEL=info
# Optional: pretty console output for local development only
# LOG_FORMAT=pretty
# Optional: folder/file for keeper execution idempotency state
# Default file: ./data/execution_locks.json
# KEEPER_STATE_DIR=./data
# IDEMPOTENCY_STATE_FILE=./data/execution_locks.json
# Optional: lock expiration controls (milliseconds)
# EXECUTION_LOCK_TTL_MS=120000
# EXECUTION_COMPLETED_MARKER_TTL_MS=30000
# === SLO (Service Level Objectives) ===
# Maximum allowed milliseconds between poll cycle completions (freshness)
# SLO_POLL_FRESHNESS_MS=60000
# Maximum allowed execution lateness in milliseconds.
# If not set, defaults to 3x the polling interval.
# SLO_EXECUTION_TIMELINESS_MS=30000
# Approximate wall-clock time per ledger in milliseconds.
# Used to convert ledger-based timeliness measurements to wall-clock.
# LEDGER_TIME_MS=5000
# Maximum number of retry tasks to process each cycle (fair scheduling)
# MAX_RETRIES_PER_CYCLE=5METRICS_PORT=3000 HEALTH_STALE_THRESHOLD_MS=60000
FRAUD_ALERT_WEBHOOK_URL=https://alerts.example.com/keeper-fraud
RECONCILIATION_ALERT_WEBHOOK_URL=https://alerts.example.com/keeper-reconciliation
KEEPER_SHARD_INDEX=0 KEEPER_SHARD_COUNT=1
DB_SHARD_BASE_COUNT=1 DB_SHARD_MAX_COUNT=8 DB_SHARD_SCALE_UP_THRESHOLD=0.75 DB_SHARD_SCALE_DOWN_THRESHOLD=0.45 DB_SHARD_USER_CAPACITY=1000 DB_SHARD_TASK_CAPACITY=5000 DB_SHARD_AUTO_SCALING=true
P2P_ENABLED=false
P2P_PUBLIC_URL=http://127.0.0.1:4100
P2P_LISTEN_HOST=0.0.0.0 P2P_LISTEN_PORT=4100
P2P_BOOTSTRAP_PEERS=http://keeper-b.example.com:4100,http://keeper-c.example.com:4100
DRIFT_WARNING_SECONDS=60 DRIFT_CRITICAL_SECONDS=300
RESOLVER_DEFAULT_TIMEOUT_MS=250 RESOLVER_FAILURE_MODE=skip
### Explanation of Variables:
- **`SOROBAN_RPC_URL`**: This is the endpoint the bot uses to communicate with the network. You can use public nodes provided by Stellar or set up your own.
- **`NETWORK_PASSPHRASE`**: This ensures your bot is talking to the right network (e.g., Futurenet, Testnet, or Public Network).
- **`KEEPER_SECRET`**: Your keeper wallet's secret key. _Keep this private and never commit it to version control (we've ensured `.env` is ignored by git)._
- **`CONTRACT_ID`**: The deployed SoroTask contract address that the keeper will monitor and execute tasks from.
- **`POLLING_INTERVAL_MS`**: How often (in milliseconds) the keeper checks for due tasks. Lower values mean more frequent checks but higher RPC usage.
- **`MAX_CONCURRENT_READS`**: Maximum number of tasks to query in parallel during each poll. Higher values speed up polling but increase RPC load.
- **`MAX_CONCURRENT_EXECUTIONS`**: Maximum number of tasks that can be executed simultaneously. Controls execution throughput.
- **`MAX_TASK_ID`**: The keeper will check task IDs from 1 to this value. Alternatively, use `TASK_IDS` to specify exact task IDs.
- **`TASK_IDS`**: Optional comma-separated list of specific task IDs to monitor (e.g., "1,2,3,5"). If set, overrides `MAX_TASK_ID`.
- **`WAIT_FOR_CONFIRMATION`**: Whether to wait for transaction confirmation after submitting. Set to 'false' for fire-and-forget mode.
- **`LOG_LEVEL`**: Minimum log severity to emit (`trace`, `debug`, `info`, `warn`, `error`, `fatal`).
- **`LOG_FORMAT`**: Optional log renderer. Leave unset for JSON logs; set to `pretty` for local human-readable output.
- **`KEEPER_STATE_DIR` / `IDEMPOTENCY_STATE_FILE`**: Location of persisted execution idempotency locks used to prevent duplicate submissions.
- **`EXECUTION_LOCK_TTL_MS`**: How long an in-progress execution lock is considered valid before stale recovery allows new work.
- **`EXECUTION_COMPLETED_MARKER_TTL_MS`**: Short-lived post-success marker to reduce accidental immediate duplicate submissions.
- **`SLO_POLL_FRESHNESS_MS`**: Maximum allowed milliseconds between poll cycle completions. A poll taking longer than this increments the freshness SLO failure counter.
- **`SLO_EXECUTION_TIMELINESS_MS`**: Maximum allowed execution lateness (ms). Tasks completing after this delay count as timeliness SLO failures. Defaults to 3 × `POLLING_INTERVAL_MS`.
- **`LEDGER_TIME_MS`**: Estimated wall-clock milliseconds per ledger closure. Used to convert ledger-based lateness to milliseconds for SLO evaluation (default: 5000).
- **`MAX_RETRIES_PER_CYCLE`**: Maximum number of retry tasks to process in a single cycle (fair scheduling). Default: 5.
- **`EXECUTION_LOCK_TTL_MS`**: How long an in-progress execution lock is considered valid before stale recovery allows new work.
- **`EXECUTION_COMPLETED_MARKER_TTL_MS`**: Short-lived post-success marker to reduce accidental immediate duplicate submissions.
- **`KEEPER_ADMIN_TOKEN`**: Bearer token required to call the keeper admin pause/resume API.
- **`FRAUD_ALERT_WEBHOOK_URL`**: Optional webhook target that receives fraud/anomaly alerts as JSON.
- **`FRAUD_ALERT_DEBOUNCE_MS`**: Minimum time between duplicate alerts for the same signature.
- **`FRAUD_BURST_WINDOW_MS`**: Rolling window used for burst and drain heuristics.
- **`FRAUD_FAILURE_WINDOW_MS`**: Rolling failure window used for repeated error detection.
- **`FRAUD_ALERT_THRESHOLD`**: Minimum heuristic score required before an alert is queued.
- **`FRAUD_FEE_SPIKE_MULTIPLIER`**: Fee multiplier used to flag sudden execution cost spikes.
- **`FRAUD_MIN_FEE_SPIKE`**: Minimum absolute fee value before a spike can alert.
- **`FRAUD_DRAIN_MULTIPLIER`**: Rolling-window fee growth multiplier used for rapid-drain detection.
- **`FRAUD_MIN_DRAIN_FEE`**: Minimum rolling fee volume before a drain alert can trigger.
- **`FRAUD_TASK_BURST_THRESHOLD`**: Number of executions of the same task in the window before it is considered bursty.
- **`FRAUD_FAILURE_BURST_THRESHOLD`**: Number of failures in the window before it is considered a failure storm.
- **`FRAUD_CROSS_TASK_THRESHOLD`**: Distinct task count in the window before cross-task velocity becomes suspicious.
- **`FRAUD_CROSS_TASK_FEE_THRESHOLD`**: Total fee volume required for cross-task velocity alerts.
- **`FRAUD_ALERT_WEBHOOK_TIMEOUT_MS`**: Timeout for outbound fraud alert webhook delivery.
- **`FRAUD_ALERT_MAX_ATTEMPTS`**: Number of delivery attempts before an alert is marked failed.
- **`RECONCILIATION_ALERT_WEBHOOK_URL`**: Optional webhook target for balance-vs-fee reconciliation mismatches.
- **`RECONCILIATION_ALERT_DEBOUNCE_MS`**: Minimum time between duplicate reconciliation alerts for the same signature.
- **`RECONCILIATION_EXECUTION_SETTLING_MS`**: Grace period before an unmatched execution is escalated.
- **`RECONCILIATION_TOLERANCE`**: Accepted balance drift tolerance; keep at `0` for exact accounting.
- **`RECONCILIATION_ALERT_WEBHOOK_TIMEOUT_MS`**: Timeout for outbound reconciliation alert webhook delivery.
- **`RECONCILIATION_ALERT_MAX_ATTEMPTS`**: Number of delivery attempts before a reconciliation alert is marked failed.
- **`KEEPER_SHARD_INDEX` / `KEEPER_SHARD_COUNT`**: Stable shard assignment controls so multiple keeper instances can partition work without ambiguous ownership.
- **`KEEPER_SHARD_LABEL`**: Optional human-readable shard identifier used in metrics and logs.
- **`DB_SHARD_BASE_COUNT` / `DB_SHARD_MAX_COUNT`**: Minimum and maximum Postgres shard counts for automatic scaling. The manager scales shard usage within these bounds.
- **`DB_SHARD_SCALE_UP_THRESHOLD` / `DB_SHARD_SCALE_DOWN_THRESHOLD`**: Hysteresis thresholds for shard scaling decisions, preventing flapping during load transitions.
- **`DB_SHARD_USER_CAPACITY` / `DB_SHARD_TASK_CAPACITY`**: Capacity heuristics used to translate active user load and task volume into shard count.
- **`DB_SHARD_AUTO_SCALING`**: Enable or disable Postgres shard auto-scaling while preserving safe fallback behavior.
- **`P2P_ENABLED` / `P2P_SHARED_SECRET`**: Enables signed peer discovery and load-aware ownership. See [P2P Keeper Discovery](./docs/p2p-keeper-discovery.md).
- **`P2P_PUBLIC_URL` / `P2P_BOOTSTRAP_PEERS`**: Advertised peer URL and initial peer list used to join the keeper mesh.
- **`DRIFT_WARNING_SECONDS` / `DRIFT_CRITICAL_SECONDS`**: Thresholds for recurring execution drift classification.
- **`SOROBAN_RPC_URLS` / `RPC_FAILOVER_ENABLED`**: Optional multi-region RPC list and failover toggle (automatically enabled when multiple URLs are configured).
- **`RPC_FAILOVER_FAILURE_THRESHOLD` / `RPC_FAILOVER_COOLDOWN_MS`**: Endpoint quarantine policy after repeated failures.
- **`RPC_FAILOVER_HEALTH_CHECK_INTERVAL_MS`**: Background health-probe interval used for endpoint recovery and rebalancing.
## RPC Load Balancer Configuration
The keeper supports a custom load balancer for Soroban RPC nodes to improve reliability, performance, and scalability. This enables distributing RPC requests across multiple Soroban RPC endpoints with health monitoring, automatic failover, and load distribution.
### Environment Variables for RPC Load Balancing
To enable the RPC load balancer, configure the following environment variables in your `.env` file:
```env
# RPC Load Balancer Configuration
RPC_ENDPOINTS="https://rpc1.soroban.network,https://rpc2.soroban.network,https://rpc3.soroban.network"
RPC_ENDPOINT_WEIGHTS="3,2,1"
RPC_HEALTH_CHECK_INTERVAL_MS=30000
RPC_HEALTH_CHECK_TIMEOUT_MS=5000
RPC_LOAD_BALANCING_STRATEGY=weighted_round_robin
RPC_ENDPOINTS: Comma-separated list of RPC URLs to distribute requests across (e.g., "https://rpc1.example.com,https://rpc2.example.com")RPC_ENDPOINT_WEIGHTS: Optional comma-separated weights for weighted round-robin distribution (e.g., "2,1" - first endpoint gets twice as many requests as second)RPC_HEALTH_CHECK_INTERVAL_MS: Interval in milliseconds between health checks (default: 30000ms = 30 seconds)RPC_HEALTH_CHECK_TIMEOUT_MS: Timeout in milliseconds for health check requests (default: 5000ms = 5 seconds)RPC_LOAD_BALANCING_STRATEGY: Load balancing algorithm to use ("round_robin", "least_connections", or "weighted_round_robin")
- When using the load balancer, the
SOROBAN_RPC_URLenvironment variable is ignored - The load balancer automatically handles failover when endpoints become unhealthy
- Health monitoring uses
getNetwork()andgetLatestLedger()calls to verify RPC endpoint availability - Metrics for the load balancer are exposed via the standard
/metrics/prometheusendpoint - The load balancer is backward compatible - if only one RPC endpoint is configured, it operates in single-server mode
The keeper health endpoint reports richer state than a binary up/down signal. Operators will see:
healthy: keeper is polling and RPC connectivity is working normally.degraded: partial issues are present, such as RPC disconnects or a half-open circuit breaker.stale: polling has not refreshed within the configuredHEALTH_STALE_THRESHOLD_MSwindow.unhealthy: the keeper is not operational due to missing polls or failed RPC health.
This makes it easier to distinguish slow recovery from critical outages during incident response.
The optional P2P layer lets keepers discover each other, advertise load, and split ownership with load-aware rendezvous hashing. It is disabled by default; when disabled or unhealthy, the keeper falls back to configured shard ownership.
See P2P Keeper Discovery for setup, security review notes, health fields, and failure behavior.
The keeper supports automated multi-region RPC failover for disaster recovery. Configure a primary endpoint in SOROBAN_RPC_URL and additional regions in SOROBAN_RPC_URLS.
See Disaster Recovery and Failover Guide for architecture, metrics, and operational runbooks.
The keeper includes a Dead-Letter Queue (DLQ) for handling repeatedly failing tasks:
# Maximum number of failures before a task is quarantined (default: 5)
DLQ_MAX_FAILURES=5
# Time window for counting failures in milliseconds (default: 3600000 = 1 hour)
DLQ_FAILURE_WINDOW_MS=3600000
# Enable automatic quarantine of repeatedly failing tasks (default: true)
DLQ_AUTO_QUARANTINE=true
# Maximum number of dead-letter records to keep (default: 1000)
DLQ_MAX_RECORDS=1000The DLQ automatically isolates tasks that fail repeatedly, preventing resource waste and providing diagnostic information for operators. See Dead-Letter Queue Documentation for details.
For fraud and anomaly alerting, see Fraud Detection and Anomaly Alerting. For strict fee-vs-balance accounting, see Real-time Financial Reconciliation.
Docker provides a reproducible, portable deployment that works on any cloud VM, VPS, or container orchestrator. This is the recommended approach for production deployments.
-
Navigate to the Repository Root
cd /path/to/sorotask -
Configure Environment Variables
Copy the example environment file and configure it:
cp keeper/.env.example keeper/.env
Edit
keeper/.envwith your configuration (see Environment Variables section below). -
Start the Keeper
From the repository root:
docker compose up -d
This will:
- Build the Docker image with multi-stage optimization
- Start the keeper container in detached mode
- Mount
./keeper/datafor task registry persistence - Expose port 3001 for health checks and metrics
- Automatically restart the container unless explicitly stopped
-
View Logs
docker compose logs -f keeper
-
Check Health Status
curl http://localhost:3001/health
-
Stop the Keeper
docker compose down
Build the image:
cd keeper
npm run docker:buildRun standalone container:
cd keeper
npm run docker:runManual Docker commands:
# Build
docker build -t sorotask-keeper ./keeper
# Run with environment file and volume
docker run -d \
--name sorotask-keeper \
--env-file ./keeper/.env \
-p 3001:3001 \
-v $(pwd)/keeper/data:/app/data \
--restart unless-stopped \
sorotask-keeper
# View logs
docker logs -f sorotask-keeper
# Stop and remove
docker stop sorotask-keeper
docker rm sorotask-keeper- Multi-stage build: Optimized image size with separate dependency and runtime stages
- Security hardening: Runs as non-root user (
node) - Health checks: Built-in health monitoring via
/healthendpoint - Data persistence: Task registry persisted in
./keeper/datavolume - Automatic restart: Container restarts automatically on failure
- Log rotation: Configured with 10MB max size and 3 file retention
- Minimal base: Uses
node:20-alpinefor smallest footprint
AWS EC2 / DigitalOcean / Linode:
# SSH into your VM
ssh user@your-server-ip
# Clone repository
git clone https://github.com/your-org/sorotask.git
cd sorotask
# Configure environment
cp keeper/.env.example keeper/.env
nano keeper/.env # Edit with your settings
# Start with Docker Compose
docker compose up -d
# Verify it's running
docker compose ps
curl http://localhost:3001/healthKubernetes:
# Build and push to registry
docker build -t your-registry/sorotask-keeper:latest ./keeper
docker push your-registry/sorotask-keeper:latest
# Create ConfigMap from .env
kubectl create configmap keeper-config --from-env-file=keeper/.env
# Deploy (create your k8s manifests based on docker-compose.yml)
kubectl apply -f k8s/keeper-deployment.yamlOnce you have your prerequisite software and environment variables ready, follow these steps on a clean environment:
-
Navigate to the Keeper Directory
Open your terminal and navigate to thekeeperfolder if you haven't already:cd keeper -
Install Dependencies
Run the following command to install the required Node.js packages (soroban-client,dotenv, andnode-fetch):npm install
-
Run the Keeper Bot
Start the Node.js application to begin listening for and executing SoroTask tasks:node index.js
If successful, you will see output indicating that the Keeper has started, along with logs of its periodic checks for due tasks!
The keeper includes a Dead-Letter Queue (DLQ) system that automatically handles repeatedly failing tasks:
The DLQ captures and isolates tasks that fail repeatedly due to:
- Invalid configuration
- Broken target contracts
- Persistent permission problems
- Insufficient gas balance
- Automatic Quarantine: Tasks exceeding failure thresholds are automatically isolated
- Diagnostic Context: Full error history, stack traces, and task configuration preserved
- Operator Visibility: HTTP endpoints and Prometheus metrics for monitoring
- Recovery Mechanism: Manual recovery after issues are resolved
# Get DLQ overview
curl http://localhost:3000/dead-letter
# Get specific task details
curl http://localhost:3000/dead-letter/123keeper_quarantined_tasks_count # Current quarantined tasks
keeper_tasks_quarantined_total # Total tasks quarantined
keeper_tasks_recovered_total # Total tasks recovered
keeper_tasks_quarantined_skipped_total # Tasks skipped due to quarantine
Run the interactive demo to see the DLQ in action:
node examples/dead-letter-demo.jsFor complete documentation, see Dead-Letter Queue Guide.
If you want to test keeper flows without a full Soroban node, the keeper includes a lightweight mock JSON-RPC server.
cd keeper
npm run mock-rpcThen point the keeper at it:
export SOROBAN_RPC_URL=http://127.0.0.1:4100
export NETWORK_PASSPHRASE="Test SDF Future Network ; October 2022"Detailed usage, supported methods, and test examples are in docs/mock-soroban-rpc.md.
- Cause: The account associated with your
KEEPER_SECRETdoes not exist on the network you are trying to use. - Solution: Fund your keeper account. If you are on Testnet or Futurenet, use the Stellar Laboratory Friendbot to fund the public key associated with your secret. Ensure you've set the correct
NETWORK_PASSPHRASEand match the network on Stellar Laboratory.
- Cause: The bot cannot reach the specified RPC endpoint, or the endpoint rejected the request due to rate-limiting or an invalid URL setup.
- Solution:
- Double-check your
SOROBAN_RPC_URLin the.envfile for any typos. Ensure it includes the proper protocol (e.g.,https://). - If you're using a public RPC, you might be rate-limited. Wait a few moments and try again, or switch to a dedicated/private RPC provider node.
- Double-check your
- Cause: Application dependencies were not correctly or fully installed.
- Solution: Ensure you ran
npm installinside thekeeper/directory correctly. Try clearing cache or removingnode_modules(rm -rf node_modules) and runningnpm installagain.
The keeper includes a comprehensive chaos testing framework to validate resilience under realistic failure conditions. This helps ensure the keeper can handle network issues, RPC failures, and degraded performance.
# Run all chaos scenarios
npm run chaos-test
# Run specific scenarios
npm run chaos-test -- --scenario=latency,ratelimit
# List available scenarios
npm run chaos-test:list
# Run single scenario
npm run chaos-test:single latency
# Save report to file
npm run chaos-test -- --output=json --file=chaos-report.json- Latency Spikes - Test timeout handling with random delays
- Partial RPC Failure - Test graceful degradation when some methods fail
- Rate Limiting - Test backoff behavior under throttling
- Flaky Network - Test circuit breaker recovery
- Gradual Degradation - Test adaptive behavior to worsening conditions
- Complete Outage - Test worst-case scenario handling
Chaos tests are integrated into the Jest test suite:
# Run all tests including chaos tests
npm test
# Run only chaos tests
npm test -- chaos.test.jsSee Chaos Testing Documentation for detailed information.
The Keeper ships with a multi-stage Dockerfile and a docker-compose.yml at the repo root so you can run it on any server with a single command — no local Node.js installation required.
- Docker Engine ≥ 24 (tested on 29.x)
- Docker Compose v2 (
docker compose— space, not hyphen)
# 1. From the repo root, copy and fill in the environment file
cp keeper/.env.example keeper/.env
# Edit keeper/.env with your KEEPER_SECRET, CONTRACT_ID, etc.
# 2. Build the image and start the keeper in the background
docker compose up --build -d
# 3. Tail logs
docker compose logs -f keeperThe keeper's health and metrics endpoint will be reachable at http://localhost:3000/health.
Prometheus-formatted metrics are available at http://localhost:3000/metrics/prometheus. See docs/prometheus-metrics.md for detailed metric descriptions and Grafana dashboard examples.
# Should return {"status":"ok","uptime":...}
curl http://localhost:3000/health
# Or let Docker tell you (after ~30 s start_period)
docker compose ps
# Look for "healthy" in the STATUS columnThe task registry (data/tasks.json) is stored in ./keeper/data/ on the host and mounted into the container. It survives container restarts and upgrades automatically.
Note: task IDs are expected to be sequential for a healthy registration history. The keeper exposes allocation summaries that surface missing IDs or duplicate registration events so operators can distinguish delayed task discovery from actual registry drift.
If you prefer to manage the container yourself without Compose, two npm convenience scripts are available. Run them from inside the keeper/ directory:
# Build the image
npm run docker:build
# Run the container (reads .env from the current directory)
npm run docker:run# Stop (data volume is preserved)
docker compose down
# Restart after config changes
docker compose up -d --buildIf you're still running into issues, feel free to open a GitHub issue or reach out to our community channels.