Self-Hosting
The full BlindMarkets stack — gateway, coordinator, observer, and reference solver — can run on any machine with Docker. This page walks through deploying it yourself, pointed at the contracts already on Sepolia.
What you're running
gatewayRust API server. Accepts intents, manages the database, relays to solvers.
coordinatorRust scheduler. Opens and closes batch windows on-chain every 30 seconds.
observerRust indexer. Watches Starknet for settlement events and updates the database.
postgresDatabase for intents, batches, and audit history.
redisRate limit counters and nonce windows. Lost on restart — no persistent state.
The frontend is a separate Next.js app deployed on Vercel. It talks to the gateway over HTTP — you only need to point it at your gateway URL.
Prerequisites
- Docker and Docker Compose installed
- A funded Starknet Sepolia account (for the coordinator and gateway to send transactions)
- The repo cloned locally
1. Clone and configure
git clone https://github.com/winsznx/blindmarkets
cd blindmarkets
cp .env.compose.example .envOpen .env and fill in:
- GATEWAY_API_KEY — any random string, e.g.
openssl rand -hex 32 - GATEWAY_ACCOUNT_ADDRESS and GATEWAY_ACCOUNT_PRIVATE_KEY — your Starknet account
- COORDINATOR_ACCOUNT_ADDRESS and COORDINATOR_PRIVATE_KEY — your Starknet account (can be the same for testnet)
Everything else — contract addresses, RPC URL, event keys — is already filled in with the Sepolia deployment values.
Keep your private key out of git
The.env file is in .gitignore by default. Never commit it. Use openssl rand -hex 32 to generate a strong API key.2. Register your gateway
The gateway account must be registered in the GatewayRegistry contract before it can relay intents. Run this once:
sncast --account your_account invoke \
--network sepolia \
--contract-address 0x0572569d692b6711f7da40d9d196bccf56b703cf8dafe03580aa713e974557b0 \
--function register_gateway \
--calldata YOUR_GATEWAY_ADDRESS YOUR_GATEWAY_PUBLIC_KEYTo get your public key: sncast account list — look for the public key field next to your account.
3. Start the stack
docker compose up --buildFirst build takes a few minutes — Rust compiles from source. After that, rebuilds are much faster because Docker caches the dependency layer.
Services come up in order: postgres and redis first, then gateway, then coordinator and observer once the gateway is healthy.
Local URLs
| Gateway API | http://localhost:3000 |
| Health check | http://localhost:3000/health |
| PostgreSQL | localhost:5432 |
| Redis | localhost:6379 |
4. Point the frontend at your gateway
In your Vercel project settings (or your local frontend/.env.local), set:
GATEWAY_URL=http://localhost:3000 # or your public URL
GATEWAY_API_KEY=your-api-key
GATEWAY_API_KEY_HEADER=X-API-KEYDeploying on Railway
The easiest way to run the backend publicly is Railway. Create a project, add PostgreSQL and Redis plugins, then add three services from the same GitHub repo:
| Service | RAILWAY_DOCKERFILE_PATH |
|---|---|
| gateway | backend/gateway/Dockerfile |
| coordinator | backend/coordinator/Dockerfile |
| observer | backend/observer/Dockerfile |
Set env vars from your .env in each service's Variables tab. For coordinator and observer, replace GATEWAY_URL with http://gateway.railway.internal:3000.
Replace DATABASE_URL and REDIS_URL with Railway's reference syntax: ${{Postgres.DATABASE_URL}} and ${{Redis.REDIS_URL}}.
Internal networking saves egress costs
Usinggateway.railway.internal:3000 instead of the public URL routes traffic internally within Railway. This avoids egress charges and is faster.Running a solver
The reference solver in solver-reference/ demonstrates the full solver loop: polling the gateway for open batches, computing a solution, and submitting it on-chain. To run it:
# Add to docker-compose.yml services or run directly:
SOLVER_ACCOUNT_ADDRESS=0x... \
SOLVER_ACCOUNT_PRIVATE_KEY=0x... \
GATEWAY_URL=http://localhost:3000 \
GATEWAY_API_KEY=your-key \
cargo run --release -p blindmarkets-solverBond required before a solver can submit
A solver must have a bond deposited in the SolverBond contract before it can submit solutions. The minimum bond is 1 STRK. See the SolverBond contract on Voyager to deposit.