Self-host the coordinator
Run your own coordinator against a VORQ deployment, with Postgres, an RPC endpoint and an object store.
Requirements
- Node.js 22 or newer, or Docker.
- PostgreSQL 16 or newer. Migrations refuse older versions.
- A JSON-RPC endpoint for the chain the VORQ contracts are deployed on.
- An S3-compatible object store that pins each object to IPFS and returns its CID, either as a
CIDelement in theCompleteMultipartUploadresponse or as anx-amz-meta-cidresponse header. A plain S3 bucket does not mint CIDs, and every upload through it fails. - A relayer account (
RELAYER_KEY) funded with ETH. It pays the gas for every relayed transaction. - The deployment's address book (see Configuration).
1. Get the code and a database
git clone https://github.com/vorq-ai/vorq-coordinator-node.git
cd vorq-coordinator-node
npm install
docker compose -f compose.dev.yml up -d # Postgres 16 on localhost:5433, user/password/db: vorq2. Start it
ADDRESSES_FILE=./addresses.json \
DATABASE_URL=postgres://vorq:vorq@localhost:5433/vorq \
RPC_URL=https://rpc.example.com \
RELAYER_KEY=0x… \
PIN_S3_ENDPOINT=https://s3.example.com PIN_S3_KEY=… PIN_S3_SECRET=… PIN_S3_BUCKET=vorq \
npm startThe server listens on port 8402. Every variable is listed in Configuration.
With Docker
docker build -t vorq-coordinator .
docker run -p 8402:8402 \
-e ADDRESSES_JSON="$(cat addresses.json)" \
-e DATABASE_URL=… -e RPC_URL=… -e RELAYER_KEY=… \
-e PIN_S3_ENDPOINT=… -e PIN_S3_KEY=… -e PIN_S3_SECRET=… -e PIN_S3_BUCKET=… \
vorq-coordinatorThe image runs as an unprivileged user, listens on 0.0.0.0:$PORT and has a HEALTHCHECK on /readyz.
3. Wait for it to be ready
On boot the node:
- Applies database migrations.
- Checks the EIP-712 domains of the payment token and the three registries against the chain, and exits on a mismatch.
- With
ESCROW_MODE=mock, checks escrow key retention against the chain's limits. - Starts listening.
- Indexes contract events from its last indexed block, or from the address book's
deployBlockon an empty database.
Until the index is within READY_LAG_BLOCKS of the head, /healthz is 200, /readyz is 503 and index-backed routes answer 503 not_ready. A first replay can take minutes. Use /healthz for liveness and /readyz for readiness. If the replay fails, the process exits non-zero so a supervisor can restart it.
4. Set up the object store
The node uploads every payload as a multipart upload and aborts it when a request fails. An upload interrupted by a crash or a dropped connection can stay incomplete; add an AbortIncompleteMultipartUpload lifecycle rule (or your store's equivalent) with a one-day expiry.
The node deletes unattached uploads after 300 seconds and every object older than FILE_RETENTION_SECONDS. See Payloads and file retention.
5. Serve browsers (optional)
Set CORS_ORIGINS to the exact origins of your web apps:
CORS_ORIGINS=https://app.example.com,http://localhost:3000Unset, the node sends no CORS headers.
Keep it running
- Relayer balance. When the relayer runs out of ETH, relayed writes answer
503 relay_unavailablewithcode: "relayer_funds". Top it up. The order'sgas_feeis set by theJobRegistryand paid to its treasury, not to the relayer. - Reclaims. The node calls
reclaimonce a minute for claimed jobs past their SLA, so clients are refunded even when nobody else acts. - Reorgs. If
/readyzreportsreason: "reorg", rebuild the index: see Recover from a reorg. - Open bids. To accept open-bid orders, run the escrow: see Run the escrow.
- Error reporting. Set
SENTRY_DSNto report errors. The Docker image preloads the reporter; withnpm startit is preloaded too.