Overview
Architecture
How a price moves from the venues through a node to the SDK or the web proxy, what each crate does, and why the nodes do not talk to each other.
The oracle is a set of independent nodes, a registry on the Internet Computer, and software on the consumer side. Each node reads the venues, computes prices, signs them and serves them. The consumer verifies the signed updates from several nodes and takes the median.
The data path
Venues: Binance, Bybit, OKX, Coinbase, Kraken, Deribit
│ WebSocket market data
▼
┌─ oracle-node (one per node, N nodes) ────────────────────────────┐
│ │
│ oracle-ingest ──▶ oracle-agg ──▶ signing ──▶ oracle-server │
│ decode, price, conf, node key, /v2/ws: listed │
│ reconnect flags secp256k1 clients, encrypted │
│ │
└────────────────────────────────────────────┬─────────────────────┘
│ signed, sealed frames
┌───────────────────────┴───────────┐
▼ ▼
oracle-client (Rust SDK) oracle-proxy
verify each frame, the SDK as one listed client,
median over the quorum plain JSON on /v1/ws
│ │
▼ ▼
App TLS reverse proxy ──▶ browser
Registry canister on the Internet Computer: the signed lists of nodes and clients.
Each node, the SDK and the proxy fetch it and check its IC certificate.A price takes these steps:
oracle-ingestreceives a venue message, decodes it and stamps it with the time of receipt on the node clock.oracle-aggturns the latest quotes of all venues of a feed into one price, oneconfand the status flags.- The node signs the update with its secp256k1 key.
oracle-serverseals the signed frame once with the current broadcast key and sends the same bytes to every subscribed client.- The SDK opens the frame, verifies the signature against the registry, checks replay and staleness, and takes the median across the nodes.
- The app reads the resolved price, its
confand its status.
The web proxy is one more consumer. It runs the SDK as one listed client and serves the result to browsers as JSON. A reverse proxy in front of it terminates TLS.
Components
oracle-types
Pure data, with no I/O, no clock and no cryptography. It holds Fixed (an i128 fixed-point number at 10^-12), Symbol and FeedId, the venue numbering, the status Flags, Observation and PriceUpdate. A Symbol cannot exist in a non-canonical form. See Feeds and symbols.
oracle-ingest
The venue connections. Each venue sits behind one adapter, so the aggregator never sees anything venue-specific.
- Staleness uses the node clock, never the venue clock.
- A reconnect invalidates the state of that venue. The session emits a disconnect event before any quote from the new connection, and the aggregator drops the old book.
- Reconnects use exponential backoff from 100 ms to 30 s, with random jitter. The backoff resets after 60 s of a stable connection.
- After 1 s with no frame, the session sends a WebSocket ping. The pong keeps the values of a quiet market current. A connection with no frame for 10 s is dead, and the session reconnects.
- When one instrument's book, mark or index sends nothing for 15 minutes while its connection lives, the venue has stopped that stream. The session reconnects, which subscribes to it again, at most once per 15 minutes.
oracle-agg
The aggregation. It has no clock, no socket and no tasks. Time arrives as an argument. For this reason a recorded set of observations replays to the same price, bit for bit, on another machine. See Aggregation.
oracle-wire
The byte layouts and the cryptography. It holds the 96-byte price payload, the secp256k1 recoverable signature over a keccak256 digest with EIP-712-shaped domain separation, the Registry and the ClientList. The types force the order of checks: a frame becomes a VerifiedUpdate only after a check against a VerifiedRegistry. The registry canister compiles this crate to wasm32, so both sides use the same encoding. See Signing.
oracle-transport
The rules of the encrypted transport, with no I/O. The client signs a handshake transcript with its API key. The node answers through HPKE and signs its reply. Each node has one broadcast key for each 60 s epoch. The node seals each price frame once, and every subscriber gets the same bytes. See WebSocket.
oracle-server
The part of the node that faces subscribers.
/v2/wsserves prices, venue reports and books to listed clients only, encrypted./v2/feeds,/v2/registryand/v2/healthare open and carry no price.- The fanout is conflating. Each subscriber has one slot for each feed, and a newer update replaces an older one that waits. A consumer therefore sees state, not every transition.
/v2/registryis a hint, never a trust root.
oracle-node
The node binary. One config file describes the whole node. The node derives its venue subscriptions from the feed definitions, so no second list can drift. One task owns the pipeline from ingest to signing, so nothing reorders a disconnect and the quotes after it.
The node has one secret seed, encrypted at rest with age. Two keys derive from it: the secp256k1 key, whose address is the node id, and an ed25519 key for venues that verify ed25519. At startup the node refuses to start if the registry does not list its address. See Run a node.
oracle-client
The consumer SDK. It has two pure parts and a thin async layer:
Verifierchecks one update: the signature, the registry membership, the sequence number, the staleness and the timestamps.FeedViewresolves one feed across the nodes: the median price, and aconfthat is never narrower than half the spread between the nodes.
The SDK refuses a quorum below three unless you call i_understand_this_provides_no_protection_against_a_bad_node(). See Rust SDK.
oracle-proxy
Verified data for web clients. A browser cannot keep a client API key, so it cannot use /v2/ws. The proxy is one listed client. It runs the SDK, with the signature checks and the quorum, and serves JSON on one plain WebSocket, /v1/ws. It also serves GET /v1/catalog and GET /health. The node signatures stop at the proxy. It has its own repository, markoutlabs/oracle-proxy. See Web proxy.
oracle-replay
The replay harness. oracle-node start --record FILE writes every raw venue frame with its receipt time. oracle-replay plays the file back through the same decoders and the same aggregator. Two replays of one recording print the same digest on any machine. The adversarial suite uses it to attack one venue in a clean recording.
The registry canister
The trust root for membership. The registry is a canister on the Internet Computer. It lists which addresses are nodes and clients now, and which addresses were nodes before.
- The canister signs each version with a threshold ECDSA key on secp256k1. No governance private key exists anywhere.
- The consumer pins the address of that key, fetches
/registry.jsonover HTTPS, checks the IC certificate, and verifies the signature. - Governance is the controller set of the canister and the only caller of its update methods. Nodes never call the canister.
See Registry.
Why nodes do not talk to each other
Each node is an independent attestor. There is no consensus, no leader and no distributed key generation. This is the most important design decision. The table compares it with a leader-based consensus design:
| Property | Independent signers | Leader or OCR consensus |
|---|---|---|
| A node fails | The consumer sees N−1 signers and continues. | The round stalls until a view change. |
| A node is added | Governance approves the entry in the registry canister. | A reconfiguration protocol runs. |
| The network between nodes partitions | No effect. The nodes never communicate. | Loss of liveness. |
| Trust | The consumer chooses its own quorum. | The consumer must trust the aggregate that the network computed. |
| Code to write | Almost no distributed-systems code. | A large amount, with bugs. |
The cost is small. The consumer verifies M signatures and takes a median. The bandwidth is M times redundant. For an off-chain feed this is the correct trade.