Setup Guide
This guide walks you through running Hydra App and connecting to it from a client. The app is the gRPC server that exposes every API documented in this section.
Prerequisites
- Docker & Docker Compose
- ~2 GB free disk for the data directory
- A funded testnet wallet for whatever networks you enable (Bitcoin Signet, Ethereum Sepolia, Arbitrum Sepolia)
Building a bot or trading agent? After completing the setup below, jump to the Bot Quickstart for a minimum runnable template.
Architecture
Your Application (gRPC client)
↓
gRPC over HTTP/2 (or gRPC-Web)
↓
Hydra App (your machine, default :5003)
↓
├─ authentication-service (auth tokens)
├─ liquidity-service (channel leases — see Lease API)
├─ orderbook (markets & matching)
├─ hydra-proxy (Electrum / Esplora / Web3 / Subgraph proxy)
└─ Local data store (fjall key-value DB on disk)
Hydra App brokers everything. You never talk to the orderbook, liquidity service, or chain RPCs directly — the app does it for you and exposes a single gRPC surface.
Authentication is handled by Hydra App, not by your client. The app obtains and refreshes tokens with the authentication service automatically, using the seed/
MNEMONICyou set up below. Clients connect to the local app over plain gRPC with no API keys, no headers, no auth handshake. Theauth_grpc_url/auth_ws_urlandproxy_authfields inconfig.yamlconfigure Hydra App's upstream connection — not your client. Your client never sees those URLs.
Step 1: Configuration files
Create a working directory and two files: config.yaml and .env.
mkdir hydra-app && cd hydra-app
config.yaml
Endpoints redacted. The staging endpoints, subgraph URLs, and Lithium contract addresses below are placeholders. The real values are distributed privately to approved testers — open a ticket in our Discord to request access and do not redistribute. The structure of this file (every field name, every nesting level) is accurate; only the access-controlled values are masked.
The block below is the staging template. Copy it, fill in the placeholders (anywhere you see <...>) with the values you receive, and adjust ports / tokens to taste.
settings:
data_path_name: hydra-app-3
critical_db_option: fjall
db_option: fjall
compression:
type: zstd
preset: balanced
server_port: 5003
metrics_port: 9090
authentication_config:
auth_grpc_url: <auth-grpc-url> # provided by the team
auth_ws_url: <auth-ws-url> # provided by the team
proxy_url: <hydra-proxy-url> # provided by the team
# backup_config:
# Uncomment if you want server-side wallet backups. NOT recommended for
# liquidity providers — adds latency on high-volume nodes.
# grpc_url: <backup-service-url>
liquidity_config:
liquidity_url: <liquidity-service-url> # provided by the team
orderbook_config:
orderbook_url: <orderbook-url> # provided by the team
networks:
- protocol: bitcoin
network: bitcoin-signet
blockchain:
type: electrum
urls:
- <electrum-ws-url> # provided by the team
esplora_url: <esplora-url> # provided by the team
waterfalls_url: <waterfalls-url> # provided by the team
proxy_auth: true
lightning_tcp_port: 19735
lightning_ws_port: 19736
gossip_sync:
type: rgs
rgs_server_url: https://rapidsync.lightningdevkit.org
- protocol: evm
network: ethereum-sepolia
web3_provider:
url: <ethereum-sepolia-web3-ws-url> # provided by the team
proxy_auth: true
lithium:
subgraph:
http_url: <ethereum-subgraph-url> # provided by the team
contract_address: <lithium-eth-contract> # provided by the team
tcp_quic_port: 29980
ws_port: 29981
tokens:
- "ERC20:0x8cd0dA3d001b013336918b8Bc4e56D9DDa1347E0" # USDC on Sepolia (Circle public faucet)
- protocol: evm
network: arbitrum-sepolia
web3_provider:
url: <arbitrum-sepolia-web3-ws-url> # provided by the team
proxy_auth: true
lithium:
subgraph:
http_url: <arbitrum-subgraph-url> # provided by the team
contract_address: <lithium-arb-contract> # provided by the team
tcp_quic_port: 29982
ws_port: 29983
tokens:
# Add the asset contracts you want active on this network.
# The team provides a list alongside the endpoint values.
- "ERC20:<arbitrum-asset-1>"
- "ERC20:<arbitrum-asset-2>"
Why redacted? During the alpha, the staging endpoints are not public. Every URL marked
<...>and every<lithium-*-contract>is access-controlled. Public values (chain IDs, RGS gossip server, well-known testnet token contracts) are kept inline because they're already publicly known.
What each block does
| Block | Purpose |
|---|---|
settings.server_port | The gRPC port your client connects to (here 5003). Map this in Docker. |
settings.metrics_port | Prometheus-style metrics. Optional — expose if you want monitoring. |
settings.data_path_name | On-disk directory name for the wallet/DB. Bump this (e.g. hydra-app-3 → hydra-app-4) to start fresh. |
settings.db_option / critical_db_option | Storage backend. fjall is the current default. |
settings.compression | On-disk compression. zstd + balanced is sensible. |
authentication_config | Hydranet auth service. Hydra App handles tokens for you. |
proxy_url | Single endpoint for the proxy that fronts Electrum, Esplora, Web3, and Subgraph requests. |
liquidity_config | Provider for service-backed channel liquidity (used by the Lease API). |
orderbook_config | The matching engine. |
networks[] | Per-network blockchain config. Each entry is a network the app will activate. |
Per-network blocks
For Bitcoin networks:
| Field | Description |
|---|---|
network | One of bitcoin-mainnet, bitcoin-signet, bitcoin-testnet, bitcoin-regtest |
blockchain.type | electrum is currently the only supported type |
blockchain.urls | Electrum WebSocket endpoints |
blockchain.esplora_url | Esplora HTTP endpoint for richer queries |
blockchain.waterfalls_url | Waterfalls (block explorer) endpoint |
blockchain.proxy_auth | If true, Hydra App authenticates with the proxy automatically |
lightning_tcp_port | Lightning peer TCP port |
lightning_ws_port | Lightning peer WebSocket port (for browser clients) |
gossip_sync | Lightning gossip source. rgs (Rapid Gossip Sync) is the default. |
For EVM networks:
| Field | Description |
|---|---|
network | e.g. ethereum-mainnet, ethereum-sepolia, arbitrum-one, arbitrum-sepolia |
web3_provider.url | WebSocket Web3 RPC endpoint |
web3_provider.proxy_auth | Same as Bitcoin — proxy auth handshake |
lithium.subgraph.http_url | Subgraph endpoint Hydra App uses to index Lithium events |
lithium.contract_address | Lithium contract on the network |
lithium.tcp_quic_port / ws_port | Lithium peer ports |
tokens | List of ERC20:<contract> strings for tokens you want active |
The token list determines which assets you can hold balances of and trade. To add a token after first run, add it here, restart the app, then call
asset.AddTokenfor it.
.env
RUST_LOG=info,hydra=debug
MNEMONIC=
PASSWORD=
GRPC_PORT=5003
DATA_PATH_NAME=hydra-app-3
| Variable | Purpose |
|---|---|
RUST_LOG | Log filter. info is fine for daily use; debug only for troubleshooting. |
MNEMONIC | Optional seed phrase. Empty = interactive mode (CLI prompt). Set = daemon mode. See security note below. |
PASSWORD | Encryption password for the on-disk wallet. Required in daemon mode. |
GRPC_PORT | Must match settings.server_port in config.yaml. |
DATA_PATH_NAME | Must match settings.data_path_name. |
⚠️ Mnemonic & password securityPutting
MNEMONICandPASSWORDin plain.envis convenient for testnet but never do this on mainnet without protections. At minimum:
- Mount the
.envfrom a secret manager (Docker secrets, Kubernetes secrets, HashiCorp Vault, AWS Secrets Manager).- Restrict file permissions (
chmod 600 .env).- Never commit
.env— add it to.gitignore.- Prefer interactive mode for development; reserve daemon mode for hardened production hosts.
Step 2: Choose a mode
| Mode | When to use | How |
|---|---|---|
| Interactive | Local dev, exploration, first run | Leave MNEMONIC="". The app prompts you on stdin to create or unlock a wallet. Requires tty: true and stdin_open: true in compose. |
| Daemon | Bots, servers, CI | Set MNEMONIC and PASSWORD. The app boots the wallet automatically, no terminal needed. |
For a trading bot, you almost always want daemon mode. For your first run on a new machine, use interactive mode once to confirm the wallet generates and the networks initialize cleanly.
Step 3: docker-compose.yml
services:
hydra-app:
image: ghcr.io/offchain-dex/hydra-app-tester:latest
volumes:
- ./config.yaml:/app/config.yaml
- hydra-data:/app/data
env_file:
- .env
environment:
- GRPC_PORT=${GRPC_PORT}
- DATA_PATH_NAME=${DATA_PATH_NAME}
ports:
- "5003:5003" # gRPC — match server_port
- "9090:9090" # metrics — optional, drop if you don't need it
- "19735:19735" # Bitcoin Lightning TCP
- "19736:19736" # Bitcoin Lightning WS
- "29980:29980" # Ethereum Lithium TCP/QUIC
- "29981:29981" # Ethereum Lithium WS
- "29982:29982" # Arbitrum Lithium TCP/QUIC
- "29983:29983" # Arbitrum Lithium WS
tty: true
stdin_open: true
restart: unless-stopped
volumes:
hydra-data:
The Lightning and Lithium peer ports are how other nodes connect to you. If you only want to make outgoing channels, you can drop them. If you want to receive channel openings or run as a liquidity provider, expose them publicly.
Step 4: Start the app
docker compose up -d
docker compose logs -f hydra-app
You're ready when you see something like:
INFO hydra_app::server: gRPC server listening on 0.0.0.0:5003
INFO hydra_app::network: bitcoin-signet activated
INFO hydra_app::network: ethereum-sepolia activated
INFO hydra_app::network: arbitrum-sepolia activated
If you ran in interactive mode without MNEMONIC set, you'll be prompted at this point — docker attach to interact with the prompt:
docker attach hydra-app
Use Ctrl-P Ctrl-Q to detach without stopping the container.
Step 5: Generate a client from the proto files
Hydra App speaks raw gRPC. To call it from your language, you need generated client stubs from the .proto schema files. The schema lives on this docs site as a downloadable bundle, plus individually browsable .proto files.
Or grab a single file:
curl -O https://docs.hydranet.ai/proto/hydra-protos.zip && unzip hydra-protos.zip -d hydra-protos
# → hydra-protos/{wallet,liquidity,orderbook,...}.proto
Each individual
.protois also athttps://docs.hydranet.ai/proto/<name>.proto— handy forimportpaths or quick browsing.
Generate client stubs
Pick your language. All examples assume the protos are unpacked at ./hydra-protos/.
# Buf is the modern polyglot codegen tool. Install: https://buf.build/docs/installation
# Add a buf.gen.yaml describing your target languages, then:
buf generate hydra-protos
# → produces stubs for every plugin you configured
After running the relevant command, you'll have generated source files (one per .proto) you can import in your project. The exact import path depends on your language — see your generator's output.
Already have a Python client? If you've built one for your own bot, drop it in your repo and skip codegen. Want to share it back? Open a Discord ticket — we may be interested in publishing community SDKs.
Future: post-launch the protos will move to Buf Schema Registry at
buf.build/offchain-dex/hydraand codegen will collapse to a one-liner:buf generate buf.build/offchain-dex/hydra. Until then, the static download above is the canonical source.
Step 6: Sanity-check from the client side
Once the app is running and you have generated stubs, hit it from your language. Examples below assume your generated code lives where the imports show.
import { AppServiceClient } from './proto/AppServiceClientPb'
import { GetNetworksRequest } from './proto/app_pb'
const client = new AppServiceClient('http://localhost:5003')
const response = await client.getNetworks(new GetNetworksRequest(), {})
for (const n of response.getNetworksList()) {
console.log(`Network: protocol=${n.getProtocol()} id=${n.getId()}`)
}
You should see one entry per network you enabled in config.yaml.
Network identifiers (config string ↔ proto fields)
config.yaml uses human-readable network names. The gRPC API uses { protocol, id } integers and hex strings. Map between them:
Bitcoin (protocol: 1 / PROTOCOL_BITCOIN)
config.yaml network | proto id (hex magic bytes) |
|---|---|
bitcoin-mainnet | f9beb4d9 |
bitcoin-testnet | 0b110907 |
bitcoin-signet | 0a03cf40 |
bitcoin-regtest | fabfb5da |
EVM (protocol: 2 / PROTOCOL_EVM)
config.yaml network | proto id (decimal chain ID) |
|---|---|
ethereum-mainnet | 1 |
ethereum-sepolia | 11155111 |
arbitrum-one | 42161 |
arbitrum-sepolia | 421614 |
polygon-mainnet | 137 |
optimism-mainnet | 10 |
When passing a
Networkto any RPC, use{ protocol, id }. Thenameandchain_idfields you may see in older client code no longer exist.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
gRPC server listening never appears | Config parse error | Check docker compose logs for the YAML parse line |
failed to connect to electrum | Proxy auth failing | Confirm your auth credentials and network connectivity to proxy_url |
| Wallet prompts loop in daemon mode | MNEMONIC set but PASSWORD blank | Set both, or unset both for interactive |
| Containers restart-loop on first run | Port collision | Check lsof -i :5003 and the Lightning/Lithium ports |
bitcoin-signet activated but no balance | Wallet is brand new | Send testnet sats to wallet.GetDepositAddress output |
Next steps
- Bot Quickstart — minimum runnable trading-bot template
- Getting Started — first API calls explained
- Common Patterns — fee structures, amounts, error handling
- API Reference — complete proto reference