Skip to main content

External calls

Real apps often need data from outside the validator process: a price feed, a weather API, a model provider, or a webhook. Gora apps reach external endpoints through a sandboxed fetch-like host function. The committee then has to agree on what came back. This page explains how that works and how to design your app so consensus succeeds.

The host function

Inside your app you call:
gora.fetch is provided by the Gora runtime, not by your language’s built-in fetch. It enforces: Apps declare their allowlist:
A call to a host not in the allowlist fails fast. The runtime never silently routes around the policy.

How the committee agrees

Validators run your app independently. If they each made their own live HTTP call, they would get slightly different responses (caches, timing, server clocks, rate limits) and never agree. Two strategies keep responses consistent.

Strategy A — Proposer fetches, committee verifies

This is the default for deterministic apps.
The committee never re-calls the upstream. It only checks that, given the proposer’s transcript, the app produces the same output. This makes the round fast and bandwidth-light. The trust assumption is the same as for any deterministic output: a dishonest proposer is caught by the committee’s re-execution, because the re-execution uses the same transcript and so produces the same answer only if the proposer’s app code is honest about what it did with the data.

Strategy B — Quorum fetch (consensus-grade data)

For requests where the upstream value is itself the thing being attested (a price oracle, an event finality check), the app sets consensus: "quorum" on the fetch:
Every committee member fetches independently. The runtime collects the values, picks the median (or strict-majority match), and exposes that value to the app. The transcript records every validator’s raw response for audit. The round only commits if at least two-thirds of the committee got a usable response. Use quorum mode sparingly: it multiplies upstream load by the committee size and is slower.

Time and the wall clock

Every fetch in proposer-fetch mode captures the proposer’s clock as part of the transcript. Verifiers see the proposer’s fetch_started_at instead of their own Date.now() so re-execution stays deterministic. If your app needs wall-clock time between fetches, read it from req.now_ms, which is the round’s pinned timestamp — not from Date.now().

What to put in the response, what to keep out

A fetch transcript is part of the attestation. Keep it minimal. The runtime automatically strips known-sensitive headers (Authorization, Cookie, X-Api-Key) from the transcript. Anything else you want stripped, set redact_request_headers: [...] on the fetch call.

Errors and round-fail

Network failures during proposer fetch behave like any other proposer execution error: the proposer broadcasts a round-fail with the reason, and the network retries with a new sequence. In quorum mode, a fetch that does not reach two-thirds usable responses also round-fails. Both cases are recorded in the round log so you can see why a request didn’t land.

Combining VRF with external data

A common pattern: pick a winner from an externally-fetched list, using Gora VRF as the source of randomness.
Determinism is preserved because both inputs (the fetched body via the proposer’s transcript, the VRF via the round context) are agreed before voting starts.

What’s intentionally not supported

For workloads that genuinely need long-running off-chain work (multi-minute agents, batch jobs), see the long-running execution surface from MVP 08.5; that path runs outside a single round.