The magical gateway between Ogmios and Exura for Cardano blockchain synchronization
Hecate is an independent data relay service that connects through Ogmios and efficiently fetches both historical and real-time on-chain data.
Named after the Greek goddess of magic, crossroads, and keeper of keys, Hecate serves as a bridge between the chain and downstream processing systems via standardized interfaces, focusing exclusively on reliable data acquisition and transmission through well-defined API boundaries, enabling integration with any system that needs to track on-chain data.
What it relays lands in a sink, and the sink decides what downstream sees: Hecate ships one that prints to the command line (CLI) for debugging or testing, and Redis sinks that write streams or a list; a message queue or a database would be a sink of its own.
NOTE: This project is in early development and is not yet ready for production use. Please use at your own risk.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ HECATE โ
โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โ
โ โ โ โ โ โ โ โ
โ โ Async โ โ Core Processing โ โ Data Sinks โ โ
โ โ Client โโโโโโบโ โโโโโโบโ โ โ
โ โ โ โ โโโโโโโโโโโโ โโโโโโโโโโโ โ โ โโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโ โ โ Backfill โ โ Follow โ โ โ โRedis โ โ โ
โ โฒ โ โ(historic)โ โ (tip) โ โ โ โSinks โ โ โ
โ โ โ โโโโโโโโโโโโ โโโโโโโโโโโ โ โ โโโโโโโโโโ โ โ
โ โ โ โ โ โโโโโโโโโโ โ โ
โ โ โ โฒ โ โ โCLI โ โ โ
โ โ โ โ โ โ โSink โ โ โ
โ โ โ โโโโโโดโโโโโ โ โ โโโโโโโโโโ โ โ
โ โ โ โ CLI โ โ โ โ โ
โ โ โโโโโโโโโโดโโโโโโโโโโดโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โ
โ โ โ โ
โโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโ
โ โ
โผ โผ
โโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ
โ โ โ โ
โ Cardano โ โ Downstream โ
โ Node + โ โ Applications โ
โ Ogmios โ โ (e.g. Exura) โ
โโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ
Hecate consists of:
- Ogmios Client - Asynchronous client for the Ogmios WebSocket API
- Data Relay - Efficiently forward blockchain data with minimal transformation
- Backfill Core - A plain
asynciocoroutine that fetches epochs concurrently, driven by a thin CLI - Live Follow - A plain
asynciocoroutine that stays at the chain tip, holding the newest blocks back so shallow rollbacks never reach its sink, driven by the same CLI - Sinks - Where relayed blocks land: the command line, per-epoch Redis streams or a Redis list for the backfill, and the Redis live stream for the live follow
- โก Parallel Historical Fetching - Efficiently fetch the entire blockchain history in batches
- ๐ Real-time Data Relay - Follow the chain tip block by block, rollbacks included
- ๐งฉ Pluggable Sinks - The backfill relays into anything that implements
send_batch; CLI and Redis sinks ship with Hecate - ๐ก๏ธ Reorg Detection - Catch chain reorganizations early and handle them gracefully
- ๐ชถ No Orchestrator - Just
asyncioand a CLI; progress lives in the sink, not an engine's database - ๐ Resumable - Rerun after a failure and it continues from the last completed epoch
- ๐งฐ Flexible Deployment - Run as a standalone service with simple configuration
- ๐ Optional Dependencies - Use only what you need - Redis is optional and can be installed separately
Hecate includes a demo script showcasing of some of the async client capabilities:
# Run the demo (assumes Ogmios on localhost)
uv run python -m demo# Backfill from the first Shelley epoch to the chain's last finalized epoch
uv run python -m cli backfill
# A bounded range, six epochs at a time
uv run python -m cli backfill --start-epoch 208 --end-epoch 320 --concurrency 6
# No Redis needed โ print what would be relayed
uv run python -m cli backfill --start-epoch 208 --end-epoch 208 --sink cli
# How far along is the Redis sink?
uv run python -m cli status
# Follow the chain tip into the Redis live stream, starting an empty one at an epoch
uv run python -m cli follow --from-epoch 650 --kupo-url http://localhost:1442Epochs are fetched concurrently โ each in its own process, since block parsing is GIL-bound โ then committed in ascending order so a consumer reading epoch-by-epoch never sees a gap. Progress lives in the sink, so a rerun resumes after the last epoch that completed, and a run whose window would leave a gap in what consumers can read is refused rather than published.
backfill() is also usable directly as a library: give it something that
implements send_batch and it will relay blocks into it, no Redis and no CLI
involved. See the detailed backfill documentation.
follow stays at the tip instead: it relays every block as the node adopts
it, holding the newest few back so that shallow rollbacks never reach its sink
and deeper ones reach it as rollbacks. Its sink is the Redis live stream, one
stream any number of consumers read; the lease that lets a second follower
wait as a warm standby, and the tail a restarted follower resumes from, are
that sink's. See the live follow documentation, and the
Redis live stream's contract for its consumers.
Epoch boundaries and block counts are derived directly from the chain over the
same Ogmios connection used for streaming (see epoch_derivation.py), with an
optional kupo endpoint to accelerate
boundary lookups. The committed data/*.csv are only a frozen bootstrap anchor
index; epochs past it are derived on demand and cached locally.
- Python 3.12+
- uv (Python package manager)
- Redis (Optional)
- Ogmios node access
# Clone the repository
git clone https://github.com/ExuraLabs/hecate.git
cd hecate
# Install dependencies
uv venv -p 3.12
# Install one of the following:
# 1) Base installation (CLI sink only, no Redis support)
uv sync
# 2) With the Redis sink
uv sync --group redis
# 3) Complete installation (Redis + development tools)
uv sync --all-groupsSettings are read from the environment at import time, optionally seeded from a
dotenv file โ .env at the repo root by default, or point HECATE_ENV_FILE at
whichever one this environment should use. See the
backfill docs for the full table; the two that
matter most are OGMIOS_ENDPOINTS and REDIS_URL:
OGMIOS_ENDPOINTS='["ws://your-ogmios-host:1337"]' REDIS_URL='redis://localhost:6379/0' \
uv run python -m cli backfill --end-epoch 210hecate/
โโโ cli # Command line entry point (python -m cli)
โโโ backfill # Historical backfill core โ plain asyncio, no orchestrator
โโโ client/ # Ogmios WebSocket client
โโโ config/ # Environment-backed settings and logging setup
โโโ data/ # Frozen bootstrap epoch-boundary data
โโโ docs/ # Longer-form documentation
โโโ epoch_derivation # Derive epoch boundaries from the chain
โโโ sinks/ # Data sinks
โ โโโ base # Sink protocols: BlockRelay, EpochCoordinator
โ โโโ redis # Redis sink for downstream service(s)
โ โโโ cli # CLI sink for command line output
โโโ constants # Constant values and configurations
โโโ models # Data models and type definitions
โโโ tests/ # Test suite (see Development, below)
# The suite that needs no chain access
uv run pytest
# All of it, including the tests that relay real blocks
HECATE_TEST_OGMIOS=ws://your-server:1337 uv run pytestThe tests exercise the real Redis sink, so they need a Redis they are allowed to empty. Supply one however suits the machine:
# bring your own โ a container, a remote instance, anything
docker run --rm -d -p 6390:6379 redis:8-alpine
HECATE_TEST_REDIS_URL=redis://localhost:6390/0 uv run pytestWithout HECATE_TEST_REDIS_URL the suite starts and owns a private
redis-server on a free port, if that binary happens to be installed. If a
given database is not empty the run stops before touching it, and one the
suite borrowed is handed back empty.
Everything degrades to skips rather than errors: no Redis to be had, or an
install without the redis dependency group, and the suite reports why and
passes. The backfill tests that must see blocks move ask for an Ogmios endpoint
the same way; the rest โ window refusals, purge safety, status โ run against
faked sink state and finish in seconds. The follow tests need no Ogmios at
all: they drive it against an in-process fake (tests/fake_ogmios.py) that
serves a chain each test controls, forks included.
# Run mypy for type checking
uv run mypy
# Run ruff for linting
uv run ruff check
# Run ruff for formatting
uv run ruff format
# Set up pre-commit hooks
uv run pre-commit installThis project builds on the ogmios-python client, which is distributed under GPL-3.0-or-later. As a result, this project is also shares the same license terms - see the LICENSE file for details.
- Ogmios - WebSocket bridge that wraps Ouroboros' mini-protocols
- ogmios-python - Original Python SDK for Ogmios
- kupo - Fast, lightweight chain-index used to accelerate epoch-boundary lookups

