Overview
The Relay Oracle attests to the onchain facts that drive Relay Protocol settlement. This guide covers how it fits into the protocol and how to deploy and operate an external oracle outside Relay’s internal infrastructure.What the Relay Oracle is
The Relay Oracle is the service that attests to onchain facts used by the Relay Protocol settlement flow. It is:- An operationally stateless HTTP service.
- A Dockerized service that exposes an HTTP API.
- A signer that produces authenticated messages and execution signatures.
- A verifier that reads chain state from RPC endpoints and chain-specific APIs.
How the oracle fits into the protocol
At a high level, the Relay Protocol needs reliable confirmation that key lifecycle steps actually happened onchain before downstream settlement actions are accepted. The oracle is responsible for attesting these core actions:- Deposits into depository contracts.
- Withdrawals from depository contracts.
- Solver fills.
- Solver refunds.
- Withdrawal-address flows used during withdrawal handling.
- The oracle fetches the relevant chain configuration.
- It reads chain state from the configured RPC endpoint or chain-specific service.
- It builds a protocol message that describes what happened.
- It signs the message with its configured signer.
- For endpoints that produce execution payloads, it may also collect signatures from peer oracles.
- The caller can then use those signatures in the wider settlement flow.
What the service actually does
The service exposes HTTP endpoints for:- Health checks.
- Chain discovery.
- Attestation requests.
- Swagger-based API documentation.
GET /lives/v1GET /chains/v1GET /documentation
POST /attestations/depository-deposits/v1POST /attestations/depository-withdrawals/v1POST /attestations/solver-fills/v1POST /attestations/solver-refunds/v1POST /attestations/withdrawal-initiation/v1POST /attestations/withdrawal-initiated/v1
/documentation is the best reference for exact request and response schemas.
Supported chains and data sources
The oracle supports multiple VM families, with chain-specific attestation logic behind a unified API. The current VM attestor implementation supports:ethereum-vmsolana-vmbitcoin-vmhyperliquid-vmlighter-vmtron-vm
configs/chains.<ENVIRONMENT>.jsonconfigs/chains.hub.<ENVIRONMENT>.json
Signing model
Each running instance uses one configured signer. The service supports two signing modes:raw-private-keyaws-kms
- Message signatures returned by attestation endpoints.
- Execution signatures used in settlement flows.
Peer oracles and quorum
A single oracle can answer requests on its own, and the code also supports peering across multiple oracle instances to collect signatures. When an incoming request setsrequestPeerSignatures=true and PEERS are configured, the oracle starts its local attestation and every configured peer request concurrently.
When ORACLE_SIGNERS is also configured, the first response group whose unsigned payload accumulates the required number of unique allowlisted signatures is returned. The local oracle is not privileged on that path, so a quorum can be satisfied entirely by remote peers.
Responses are combined only when their unsigned payloads and signing metadata match.
The oracle rejects aggregation across:
- Differing attestation payloads.
- Differing signing domains.
- Differing order of any non-signature array in the payload.
- Duplicate signers.
- Non-allowlisted signers, when
ORACLE_SIGNERSis configured. - Multi-message responses where a signer is missing from any signature array.
- Responses that fail the endpoint schema.
ORACLE_SIGNERSis a semicolon-separated allowlist of authorized signer addresses.ORACLE_SIGNERS_THRESHOLDis the total number of unique allowlisted signatures required for a valid quorum (defaults to1).
ORACLE_SIGNERS list and never sends it to peers, so operators are not required to configure identical lists. A signature counts only at instances that list its signer, and the signer authorized onchain decides which signatures settlement accepts.
For example, a 2-of-3 configuration looks like this:
ORACLE_SIGNERS_THRESHOLD is set to something that is not an integer, or to an integer below 1.
When ORACLE_SIGNERS is configured, the code also fails at startup when:
ORACLE_SIGNERS_THRESHOLDexceeds the number of unique entries inORACLE_SIGNERS.- Any entry in
ORACLE_SIGNERSis not a valid nonzero address. - The local signer address is not included in
ORACLE_SIGNERS.
ORACLE_SIGNERS is omitted, the threshold is ignored for quorum purposes, so a threshold set without an allowlist doesn’t enforce anything. The code provides a startup warning when a threshold is set without the allowlist.
Without the allowlist, the service waits for the local operation and every configured peer, bounded by PEER_REQUEST_TIMEOUT_MS, instead of completing early at a threshold. Only peer responses that match the local unsigned payload are aggregated, and the request fails if the local attestation fails.
Peer calls are best-effort attempts. If a peer times out, errors, fails response validation, or returns a payload that does not match any signable group, it is skipped and a warning is emitted.
With an allowlist, if no group reaches the threshold, the strongest valid partial group is returned once all sources settle, and a warning records the matched vs required signature counts.
Security and trust model
If you operate a third-party oracle, you are expected to treat it as production infrastructure. Minimum security expectations:- Use a dedicated signing key that is not shared with any other oracle.
- Prefer a hardened key-management approach.
aws-kmsis supported; if you use raw keys, store them in a real secret manager. - Require API keys on your public endpoints.
- Use HTTPS for any publicly reachable deployment.
- Use dedicated peer API keys when exchanging traffic with other oracle operators.
- Restrict management access to the host platform and secret storage.
Infrastructure requirements
You should run the oracle as a Dockerized service on a platform that can run a Docker container and expose an HTTP service. For most third-party operators, Railway should be treated as the preferred starting point. It is the recommended default when you want the simplest path to running a single external oracle instance. Typical Docker-hosting options:- Railway
- ECS
- Kubernetes
- Fly.io
- A VM running Docker
- A long-running Docker runtime.
- Stable outbound access to all configured RPC and chain service endpoints.
- Stable inbound access from callers and peer oracles.
- Secure secret storage for signing material and API keys.
Configuration model
The runtime configuration is mostly environment-variable driven. For a recommended raw-key mainnet deployment, define:SIGNING_MODULE=raw-private-key is optional because raw-key signing is the default code path, but setting it explicitly is clearer for operators.
You can also set:
HTTP_PORTis optional if your platform injectsPORT. The server resolves ports in this order:HTTP_PORT, thenPORT, then3000.API_KEYSis optional in code, but strongly recommended for any deployment that is reachable over a network.PEER_REQUEST_TIMEOUT_MSdefaults to10000.ORACLE_SIGNERSis the semicolon-separated allowlist used to count unique oracle signatures during quorum aggregation. See Peer oracles and quorum for behavior details.ORACLE_SIGNERS_THRESHOLDis the total number of unique allowlisted signatures required for a quorum. It defaults to1, must be an integer of at least1, and is validated against the size ofORACLE_SIGNERSwhen that allowlist is configured.
Chain RPC and additional service variables
The chain config files reference environment variables dynamically. Your deployment is only as reliable as the endpoints and credentials you provide there. There are two categories to think about:- Standard RPC URLs for the chains the oracle reads.
- Supplemental service variables for chains that need more than a plain RPC endpoint.
Core RPC variables
For a mainnet deployment, the most important RPC variables to populate first are:- The oracle validates chain state in real time, so weak or rate-limited RPCs directly reduce reliability.
- If a configured chain does not have a working RPC endpoint, any attestation that depends on that chain will fail.
ENVIRONMENT. The chain config files are the source of truth for the full required set.
Supplemental non-RPC variables
Some chains and integrations need service-specific URLs or credentials in addition to a raw RPC endpoint. Common examples are:- Some ecosystems expose critical settlement-related data more reliably through companion APIs than through a single generic RPC method.
- Some providers require API credentials even when the underlying access pattern still looks like a chain read.
- These variables improve reliability and coverage for chain-specific reads without changing the public API of the oracle itself.
Practical guidance
- Public RPC endpoints may work for lower-volume or non-critical traffic, but are typically less predictable.
- Hyperliquid and Bitcoin commonly need supplemental services, not just a single RPC URL.
- When adding or removing supported chains, recheck the required environment variables before deploying.
Authentication
WhenAPI_KEYS is set, the service requires a valid x-api-key header for all protected routes.
These routes remain accessible without an API key:
/documentation/lives/v1
/chains/v1 and all attestation endpoints should be treated as protected endpoints in a real deployment.
API_KEYS uses this format:
Peering with other oracle operators
UsePEERS to define other oracle instances this oracle should call when peer signatures are requested.
Format:
- A base URL.
- A per-peer API key, separated by
|.
pass-through is also supported as a peer credential, which forwards the incoming x-api-key instead of a dedicated peer key. That is generally better suited to tightly controlled internal environments. For separate organizations, use dedicated peer keys.
If Relay-managed oracles need to call your instance, the usual pattern is:
- You expose a stable public HTTPS URL.
- You create a dedicated incoming API key for peer traffic.
- Relay adds your URL and that key to the
PEERSlist used by its oracle instances.
Recommended deployment sequence
The safest way to bring up a new third-party oracle is:- Create a new signer dedicated to this oracle.
- Provision RPC and chain-service endpoints.
- Deploy the container on Railway first unless you already have an established alternative platform.
- Verify health and chain loading.
- Protect the service with API keys.
- Expose the service over HTTPS.
- Configure peering and receive live requests in shadow mode.
- Run long enough to observe stability, latency, and RPC usage.
- Only then consider onboarding the signer for active participation.
Running the service
Build and run the service as a Docker container:/vault/secrets when present, which is useful for secret-injection systems.
If your platform builds from source automatically, the important requirement is still the same: the service should be deployed and run as the repository’s Docker image, with the required environment variables injected at runtime.
First-run verification checklist
After deployment, verify:GET /lives/v1returns{"status":"ok"}.GET /chains/v1returns the chains expected for your selectedENVIRONMENTwhen called with a validx-api-keyifAPI_KEYSis configured.GET /documentationloads the Swagger UI.- Your logs show the service started successfully.
- Your logs show the signer address you expect, and that address matches one of the entries in
ORACLE_SIGNERSif the allowlist is configured.
x-api-key if API_KEYS is configured.
Operational monitoring
At minimum, monitor:- Container restarts.
- Request latency.
- Error rate by endpoint.
- Peer timeout and peer mismatch warnings.
- RPC provider failures and rate limits.
- Memory and CPU consumption.
- Signer address correctness after deploys.