Skip to content

Latest commit

 

History

3,056 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Dingo

Dingo Logo
GitHub Go Report Card Go Reference Discord

⚠️ WARNING: Dingo is under heavy active development and is not yet ready for production use. It should only be used on testnets (preview, preprod) and devnets. Do not use Dingo on mainnet with real funds.

A high-performance Cardano blockchain node implementation in Go by Blink Labs. Dingo provides:

  • Full chain synchronization and validation via Ouroboros consensus protocol
  • UTxO tracking with 41 UTXO validation rules and Plutus V1/V2/V3 smart contract execution
  • Block production with VRF leader election and stake snapshots
  • Multi-peer chain selection with density comparison and VRF tie-breaking
  • Client connectivity for wallets and applications
  • Pluggable storage backends (Badger, SQLite, GCS, S3, PostgreSQL, MySQL)
  • Tiered storage modes ("core" for consensus, "api" for full indexing)
  • Peer governance with dynamic peer selection, ledger peers, and topology support
  • Chain rollback support for handling forks with automatic state restoration
  • Fast bootstrapping via built-in Mithril client
  • Optional Midnight event indexing and MidnightState gRPC service
  • Multiple external interfaces: general-purpose APIs (UTxO RPC, Blockfrost-compatible REST, Mesh/Rosetta) plus Bark for Dingo-to-Dingo C2 and archive services

Note: On Windows systems, named pipes are used instead of Unix sockets for node-to-client communication.

dingo screenshot

Documentation

Start with the Dingo documentation index. It maps the versioned documentation in this repository, the package comments exposed by go doc, and the public operator guides at docs.blinklabs.io.

For code-level questions, use the Go code reference to find package doc.go files and commands that render documentation from the exact revision checked out. Automated tools can also use the public LLM documentation index or the focused Cardano nodes and operations set.

Running

Dingo supports configuration via a YAML config file (dingo.yaml), environment variables, and command-line flags. Priority: CLI flags > environment variables > YAML config > defaults.

A sample configuration file is provided at dingo.yaml.example. You can copy and edit this file to configure Dingo for your local or production environment.

Environment Variables

The following environment variables modify Dingo's behavior:

  • CARDANO_BIND_ADDR
    • IP address to bind for listening (default: 0.0.0.0)
  • CARDANO_CONFIG
    • Full path to the Cardano node configuration (default: ./config/cardano/preview/config.json)
    • Use your own configuration files for different networks
    • Genesis configuration files are read from the same directory by default
  • CARDANO_DATABASE_PATH
    • A directory which contains the ledger database files (default: .dingo)
    • This is the location for persistent data storage for the ledger
  • CARDANO_INTERSECT_TIP
    • Ignore prior chain history and start from current position (default: false)
    • This is experimental and will likely break... use with caution
  • CARDANO_METRICS_PORT
    • TCP port to bind for listening for Prometheus metrics (default: 12798)
  • CARDANO_NETWORK
    • Named Cardano network (default: preview)
  • CARDANO_PRIVATE_BIND_ADDR
    • IP address to bind for listening for Ouroboros NtC (default: 127.0.0.1)
  • CARDANO_PRIVATE_PORT
    • TCP port to bind for listening for Ouroboros NtC (default: 3002)
  • CARDANO_RELAY_PORT
    • TCP port to bind for listening for Ouroboros NtN (default: 3001)
  • CARDANO_SOCKET_PATH
    • UNIX socket path for listening (default: dingo.socket)
    • This socket speaks Ouroboros NtC and is used by client software
  • CARDANO_TOPOLOGY
    • Full path to the Cardano node topology (default: "")
  • DINGO_PLUGINS_API_UTXORPC_CONFIG_PORT
    • TCP port to bind for listening for UTxO RPC (default: 9090)
    • Compatibility alias: DINGO_UTXORPC_PORT
  • DINGO_PLUGINS_API_BLOCKFROST_CONFIG_PORT
    • TCP port for the Blockfrost-compatible REST API (default: 3000)
    • Compatibility alias: DINGO_BLOCKFROST_PORT
  • DINGO_PLUGINS_API_MESH_CONFIG_PORT
    • TCP port for the Mesh (Coinbase Rosetta) API (default: 8080)
    • Compatibility alias: DINGO_MESH_PORT
  • DINGO_BARK_PORT
    • TCP port for the Bark block archive API (default: 0, disabled)
  • DINGO_BARK_BASE_URL
    • Base URL of a remote Bark archive node used for archive fallback (default: empty, disabled)
  • DINGO_BARK_BLOCK_DOWNLOAD_HOSTS
    • Comma-separated HTTPS hostnames additionally allowed for Bark-supplied block download URLs. The allowlist always includes the DINGO_BARK_BASE_URL hostname.
  • DINGO_BARK_CLIENT_CA_FILE_PATH
    • PEM CA bundle used to authenticate every Bark DatabaseService caller.
  • DINGO_BARK_OPERATOR_CERTIFICATE_FINGERPRINTS
    • Comma-separated SHA-256 client certificate fingerprints authorized for destructive Bark DatabaseService RPCs.
  • DINGO_HEALTH_PORT
    • TCP port for the liveness/readiness probe listener (default: 12799, 0 disables). Binds bindAddr, like the relay and metrics listeners.
  • DINGO_HEALTH_READY_GAP_SLOTS
    • Slots the chain tip may trail the wall-clock slot while /readyz still reports ready (default: 1000)
  • DINGO_DEBUG_BIND_ADDR
    • IP address to bind for unauthenticated pprof endpoints (default: 127.0.0.1)
    • This is independent of the public and private node bind addresses; set a wildcard address only when an external network control protects it
  • DINGO_DEBUG_PORT
    • TCP port for pprof endpoints (default: 0, disabled)
  • DINGO_HISTORY_EXPIRY_ENABLED
    • Enable local expiry of immutable block CBOR older than the ledger stability window (default: false)
  • DINGO_HISTORY_EXPIRY_FREQUENCY
    • How often a history-expiry node scans for old local blocks (default: 1h)
  • DINGO_STORAGE_MODE
    • Storage mode: core (default) or api
    • core stores only consensus data (UTxOs, certs, pools, protocol params)
    • api additionally stores witnesses, scripts, datums, redeemers, and tx metadata
    • API servers (Blockfrost, UTxO RPC, Mesh) require api mode
  • DINGO_RUN_MODE
    • Application-wide operational mode for a bare dingo invocation: serve (default), load, dev, or leios
    • Explicit subcommands select their own effective operation; for example, dingo sync runs in sync mode regardless of the configured value
    • Relay, producer, storage, API, and validation settings do not select a run mode
  • DINGO_START_ERA
    • Experimental startup era override. Set to dijkstra only for Dijkstra/Leios test networks; leave empty to follow genesis protocol version.
  • DINGO_LOGGING_FORMAT
    • Log output format: text (default, human-readable) or json (machine-parseable, for ELK/Loki ingestion)
  • DINGO_LOGGING_LEVEL
    • Minimum log level: debug, info (default), warn, or error (the --debug flag overrides this to debug)
  • DINGO_MIDNIGHT_ENABLED
    • Enable Midnight indexing in API storage mode (default: false)
  • DINGO_MIDNIGHT_SERVER_ENABLED
    • Independently enable the Midnight gRPC server in API storage mode (default: false)
  • DINGO_MIDNIGHT_REFLECTION_ENABLED
    • Enable gRPC reflection on the Midnight server; requires the server (default: false)
  • DINGO_MIDNIGHT_HOST
    • Midnight gRPC listen address (default: 127.0.0.1)
  • DINGO_MIDNIGHT_PORT
    • Midnight gRPC listen port; must be non-zero when the server is enabled (default: 50051)
  • TLS_CERT_FILE_PATH - TLS certificate used directly by an enabled Midnight gRPC listener and as the built-in UTxO RPC compatibility default; requires TLS_KEY_FILE_PATH (default: empty)
  • TLS_KEY_FILE_PATH - matching TLS private key for those listeners (default: empty)

Block Production (SPO Mode)

To run Dingo as a stake pool operator producing blocks:

  • CARDANO_BLOCK_PRODUCER - Enable block production (default: false)
  • CARDANO_SHELLEY_VRF_KEY - Path to VRF signing key file
  • CARDANO_SHELLEY_KES_KEY - Path to KES signing key file
  • CARDANO_SHELLEY_OPERATIONAL_CERTIFICATE - Path to operational certificate file

Dingo block production is exercised by the all-Dingo DevNet and has produced blocks on preview and preprod. Current releases do not support mainnet operation.

Quick Start

# Preview network (default)
./dingo

# Preprod
CARDANO_NETWORK=preprod ./dingo

# Or with explicit config path
CARDANO_NETWORK=preprod CARDANO_CONFIG=path/to/preprod/config.json ./dingo

Dingo creates a dingo.socket file that speaks Ouroboros node-to-client and is compatible with cardano-cli, adder, kupo, and other Cardano client tools.

Cardano configuration files are bundled in the Docker image. For local builds, you can find them at docker-cardano-configs.

Runtime resource requirements

Dingo's resource needs depend on the network, storage mode, and whether it is bootstrapping or serving near the chain tip. The following measurements were captured on September 4, 2026 from nine process samples at 30-second intervals (about four minutes) on otherwise shared 32 GiB hosts. CPU values below are derived from the recorded process percentages (%CPU / 100) and represent core-equivalents, not a new sampling run. They are observations, not capacity guarantees; the mainnet sample was on released v0.70.5, while the Preprod and Preview samples used an origin/main build.

Network and mode Average CPU cores Peak CPU cores Average RSS Peak RSS
Mainnet, core 0.863 0.864 4.3 GiB 4.5 GiB
Preprod, core 0.545 0.547 1.5 GiB 1.5 GiB
Preview, core 0.095 0.096 1.3 GiB 1.4 GiB

For a practical starting point, provision at least 2 vCPUs and 8 GiB RAM for a core node. This leaves CPU and memory headroom above the observed mainnet serve sample and allows for variation from peer activity, compaction, upgrades, and catch-up. A node that serves APIs or uses storageMode: api should use at least 4 vCPUs and 16 GiB RAM until longer production-like measurements are available. Disk capacity must also cover the selected storage mode and its temporary bootstrap peak.

API-mode Mithril bootstrap is a separate workload and needs more resources than near-tip serving. An in-progress Preview sample averaged 0.932 CPU cores (0.972 peak, derived from the recorded percentages) and grew from 0.3 GiB to 3.0 GiB RSS in about four minutes while importing a snapshot; the run had reached 1.8% ledger/UTxO import, so its eventual peak was not measured. The same run used about 17.9 GB of host /data at the time of sampling. Treat these API bootstrap values as preliminary and leave substantial additional CPU, RAM, and disk headroom until a complete bootstrap measurement is available.

API-mode bootstrap measurements

The following complete Mithril API-mode runs used Dingo origin/main at commit 034c12e6. The Preprod backfill was followed by a deferred critical-index rebuild; the overall duration includes both phases. These are environment-specific planning baselines, not capacity guarantees.

Network Dingo ref Storage mode Backfill Deferred index rebuild Overall Blocks / transactions
Preprod origin/main (034c12e6) api 6h09m06s 12m30.607s ~6h21m37.7s 5,169,107 / 6,795,854
Preview origin/main (034c12e6) api 7h38m34s not separately recorded 7h38m34s 4,658,378 / 6,890,796

The API-mode runs reached approximately 8.1 GiB RSS on Preprod and 9.6 GiB RSS on Preview at their highest observed checkpoints. Peak CPU was about 1.29 core-equivalents on both runs; swap reached about 145 MiB on Preprod and 125 MiB on Preview. The observed data footprints were approximately 60 GiB and 49 GiB respectively. These resource readings were captured during active bootstrap and should not be used as steady-state serving requirements; leave additional headroom for host I/O, memory pressure, and future network growth.

Docker

# Run on preview (default)
docker run -p 3001:3001 ghcr.io/blinklabs-io/dingo

# Run on preprod with persistent storage
docker run -p 3001:3001 \
  -e CARDANO_NETWORK=preprod \
  -v dingo-data:/data/db \
  -v dingo-ipc:/ipc \
  ghcr.io/blinklabs-io/dingo

The image is based on Debian bookworm-slim and includes cardano-cli, nview, and txtop. Mithril snapshot support is built into dingo natively (dingo mithril sync). The Dockerfile sets CARDANO_DATABASE_PATH=/data/db and CARDANO_SOCKET_PATH=/ipc/dingo.socket, overriding the local defaults of .dingo and dingo.socket — the volume mounts above map to these container paths.

Port Service Default
3001 Ouroboros NtN (node-to-node) Enabled
3002 Ouroboros NtC over TCP Enabled
12798 Prometheus metrics Enabled
12799 Health probes (/health, /healthz, /readyz) Enabled
3000 Blockfrost REST API Disabled
8080 Mesh (Rosetta) REST API Disabled
9090 UTxO RPC (gRPC) Disabled
50051 Midnight state (gRPC) Disabled
Bark archive (gRPC) Disabled (example when enabled: 9091)
pprof debug endpoints Disabled (DINGO_DEBUG_PORT=0; loopback when enabled)

Health Probes

Dingo serves liveness and readiness on a listener of its own (healthPort, default 12799), separate from Prometheus metrics, pprof, and every API listener. It starts in both storage modes and whether or not the Blockfrost, Mesh, and UTxO RPC APIs are enabled, so the probe is available in the default core relay configuration that the shipped docker-compose.yml runs.

dingo mithril sync serves it too. A Mithril bootstrap runs as its own process before the node starts, takes hours on mainnet, and reports live and not-ready throughout, so a container HEALTHCHECK pointed at healthPort keeps passing while the snapshot downloads instead of replacing the container partway through it.

Path Meaning 200 when Non-200 when
/healthz (and /health) Liveness The process is up and the listener is serving Never, while the process can answer
/readyz Readiness The chain tip is within healthReadyGapSlots (default 1000) of the wall-clock slot Starting up, bootstrapping from Mithril, catching up, or the tip has frozen

Both return JSON carrying the readiness verdict, a reason, and the observed tip gap in slots:

{"live":true,"ready":false,"reason":"tip gap 4211 slots exceeds tolerance of 1000 slots","tipGapSlots":4211,"status":"unhealthy"}

The split is deliberate, because an orchestrator acts on the two differently. Docker, Swarm, and ECS replace an unhealthy container, and Kubernetes restarts a container whose livenessProbe fails; a node doing an initial sync is legitimately not useful for hours or days, and none of the conditions that freeze a tip are repaired by a restart loop. So liveness stays independent of sync state and is what the image's HEALTHCHECK probes; that check follows DINGO_HEALTH_PORT and reports healthy without probing when it is 0. Readiness is the signal that catches a frozen tip, and failing it removes a pod from a Service or a target from a load balancer without killing the node.

Kubernetes:

livenessProbe:
  httpGet: { path: /healthz, port: 12799 }
  periodSeconds: 30
readinessProbe:
  httpGet: { path: /readyz, port: 12799 }
  periodSeconds: 15

The readiness tolerance matches forgeStaleGapThresholdSlots: both answer "has this node stopped following the chain?", and a probe that flapped more readily than the forger's own staleness gate would evict a node the forger still considers current. Raise healthReadyGapSlots on a network with a lower active slot coefficient, or lower it to detect a stall sooner at the cost of flapping on quiet stretches of chain.

The tip gap is read from the ledger's slot clock — the same value the dingo_tip_gap_slots gauge exports — so readiness does not depend on the Prometheus listener.

Storage Modes

Dingo has two storage modes commonly used in three node configurations:

Node configuration Settings Current behavior
Relay storageMode: core, blockProducer: false Validates and follows the chain, participates in NtN/NtC, relays blocks and transactions, and stores consensus state without API history
Block producer storageMode: core, blockProducer: true plus VRF/KES/opcert paths Includes the relay behavior, leader election, block forging, forged-block self-validation, and block diffusion
Data/API node storageMode: api, blockProducer: false Stores consensus state plus transaction, witness, script, datum, redeemer, governance, and metadata history; starts configured Blockfrost, Mesh, and UTxO RPC providers

core is the default and smallest storage/runtime surface. The producer profile adds forging and key operations to it. API mode adds historical indexing and query services; it is not a separate consensus implementation.

# Relay or block producer (default)
./dingo

# API node
DINGO_STORAGE_MODE=api ./dingo

Or in dingo.yaml:

storageMode: "api"

API Servers and Bark

Dingo includes three general-purpose external APIs, an Acropolis-compatible Midnight state service, and Bark. UTxO RPC, Blockfrost, and Mesh are client-facing APIs and require storageMode: "api". Their built-in providers are registered with the instance-owned plugin host, start on their provider defaults in API mode, and can be configured independently under plugins.api. Set an individual port to 0 to disable that interface.

Midnight indexing and serving are separate opt-ins. midnight.enabled starts the indexer, while midnight.serverEnabled starts the gRPC listener for rows already present in the Midnight tables; either may be enabled independently in API storage mode. The listener defaults to 127.0.0.1:50051. Reflection is available only with midnight.reflectionEnabled. Configuring both tlsCertFilePath and tlsKeyFilePath enables TLS; plaintext is also supported. Dingo does not add authentication to this Acropolis-compatible service.

Bark is Dingo's own Dingo-to-Dingo archive protocol rather than an application API. It is configured separately with barkPort and barkBaseUrl.

For public client access, the API listeners may be exposed directly or placed behind a reverse proxy or API gateway. UTxO RPC, Blockfrost, and Mesh support optional in-process TLS and accept requests without credentials.

The shorter DINGO_UTXORPC_PORT, DINGO_BLOCKFROST_PORT, and DINGO_MESH_PORT names remain supported for compatibility. If both a compatibility name and its plugin-form name are set, the plugin-form value takes precedence.

Interface Port Env Var Default Protocol Role
UTxO RPC DINGO_PLUGINS_API_UTXORPC_CONFIG_PORT 9090 gRPC General-purpose client API (v1alpha and v1beta)
Blockfrost DINGO_PLUGINS_API_BLOCKFROST_CONFIG_PORT 3000 REST General-purpose client API
Mesh (Rosetta) DINGO_PLUGINS_API_MESH_CONFIG_PORT 8080 REST General-purpose client API
Midnight DINGO_MIDNIGHT_PORT 50051 (server off) gRPC Acropolis-compatible Midnight state API
Bark DINGO_BARK_PORT disabled Connect/gRPC Dingo-to-Dingo C2/archive protocol
# Enable Blockfrost API on port 3100 and UTxO RPC on port 9090
DINGO_STORAGE_MODE=api \
  DINGO_PLUGINS_API_BLOCKFROST_CONFIG_PORT=3100 \
  DINGO_PLUGINS_API_UTXORPC_CONFIG_PORT=9090 \
  ./dingo

Or in dingo.yaml:

storageMode: "api"
plugins:
  api:
    blockfrost: {provider: builtin, config: {port: 3100}}
    utxorpc: {provider: builtin, config: {port: 9090}}

API TLS

api.tls sets a shared default TLS policy for every selected plugins.api.* provider (Blockfrost, Mesh, UTxO RPC). Each field resolves independently: plugins.api.<name>.config.tls overrides a field for that provider only, otherwise the shared value is used and an unset policy remains plaintext. With mode: server, both certificate and key paths are required before the listener binds; TLS remains optional. Explicit mode: disabled keeps a provider plaintext even when a shared TLS policy is configured. CLI and environment values for the shared TLS policy retain the normal CLI > environment > YAML > default precedence.

api:
  tls:
    mode: server
    certFilePath: /run/secrets/api.crt
    keyFilePath: /run/secrets/api.key
plugins:
  api:
    # Inherits TLS from api.tls above.
    utxorpc:
      provider: builtin
      config:
        port: 9090
    mesh:
      provider: builtin
      config:
        port: 8080
    # Overrides just the certificate/key for this provider; the inherited
    # api.tls.mode ("server") still applies.
    blockfrost:
      provider: builtin
      config:
        port: 3000
        tls:
          certFilePath: /run/secrets/blockfrost.crt
          keyFilePath: /run/secrets/blockfrost.key

API routes require no credentials. TLS certificate/key file contents are never written to logs, error messages, or effective-config output.

The pre-existing root tlsCertFilePath/tlsKeyFilePath fields remain supported compatibility inputs: Midnight uses the pair directly when its listener is enabled, and UTxO RPC merges it as the lowest-priority policy, field by field, alongside the shared api.tls default and its own tls config — not as an all-or-nothing fallback that applies only when both newer scopes are completely unset. For example, if shared api.tls sets only mode: server with no certFilePath/keyFilePath, UTxO RPC still inherits the two paths from the legacy root settings. The root pair is not promoted onto Blockfrost or Mesh, since doing so would silently switch a previously plaintext listener to TLS on upgrade. bindAddr and corsAllowedOrigins are unrelated to this policy and remain root-level settings shared by all listeners (bindAddr is also used by the relay/NtN listener, not just the APIs). API listeners use bindAddr, whose default is 0.0.0.0; CORS remains operator-chosen through corsAllowedOrigins.

Archive And History Expiry Nodes

Dingo can expire immutable block CBOR from a local blob store once blocks are older than the ledger-derived stability window. This History Expiry mode is a valid standalone operational mode: without an archive fallback, reads for expired blocks return a clear history-expired error. When paired with Bark, expired or missing historical block reads can be transparently served from a remote archive node.

An archive node uses a signed-URL-capable blob plugin (s3 or gcs) and enables Bark with barkPort. Bark answers Dingo-to-Dingo archive requests by returning a signed object-storage URL plus block metadata. Badger is valid for a normal local blob store, but it does not provide signed URLs and should not be used as the Bark archive backend.

For local source builds, the s3 and gcs blob plugins require -tags dingo_extra_plugins or make build. Official release binaries include the extra plugin tag.

storageMode: "core"
plugins:
  storage:
    blob:
      provider: s3
      config:
        bucket: "dingo-archive"
        region: "us-east-1"
        prefix: "preview"
barkPort: 9091

A history-expiry node keeps its normal local blob store and enables historyExpiry. Dingo expires blocks older than the ledger-derived stability window while keeping local indexes and metadata, so reads fail explicitly as expired history unless an archive wrapper can serve them.

storageMode: "core"
plugins:
  storage:
    blob:
      provider: badger
      config: {}
historyExpiry:
  enabled: true
  frequency: 1h

Add barkBaseUrl when expired historical reads should fall back to a Bark archive:

barkBaseUrl: "http://archive.example.internal:9091"
barkBlockDownloadHosts:
  - "dingo-archive.s3.us-east-1.amazonaws.com"

Bark archive RPC may use the configured barkBaseUrl, but the block download URLs returned by that service must be HTTPS, must not contain credentials, and must match either the barkBaseUrl hostname or barkBlockDownloadHosts.

The runnable demonstration in internal/test/archive-demo/ brings up an S3 compatible Minio archive node, a local Badger history-expiry node, and an end-to-end BlockFetch check through Bark.

Deployment Patterns

Relay node (consensus only, no APIs):

./dingo

API / data node (full indexing, one or more APIs):

DINGO_STORAGE_MODE=api DINGO_PLUGINS_API_BLOCKFROST_CONFIG_PORT=3100 ./dingo

Archive node (cloud object storage plus Bark archive service):

DINGO_PLUGINS_STORAGE_BLOB_PROVIDER=s3 DINGO_BARK_PORT=9091 ./dingo

History-expiry node (local storage plus a remote Bark archive):

DINGO_HISTORY_EXPIRY_ENABLED=true \
DINGO_BARK_BASE_URL=http://archive.example.internal:9091 ./dingo

Block producer (consensus only, with SPO keys):

CARDANO_BLOCK_PRODUCER=true \
  CARDANO_SHELLEY_VRF_KEY=/keys/vrf.skey \
  CARDANO_SHELLEY_KES_KEY=/keys/kes.skey \
  CARDANO_SHELLEY_OPERATIONAL_CERTIFICATE=/keys/opcert.cert \
  ./dingo

When storageMode=core, the Badger blob store defaults to mmap-only settings: block-cache-size=0, index-cache-size=0, and compression=false. When storageMode=api, the default Badger profile is block-cache-size=268435456, index-cache-size=0, and compression=true. The plugins.storage.blob.config Badger settings (YAML or the matching DINGO_PLUGINS_STORAGE_BLOB_CONFIG_* environment variables) override those defaults only when explicitly set.

See dingo.yaml.example for the full set of configuration options.

Fast Bootstrapping with Mithril

Instead of syncing from genesis (which can take days on mainnet), you can bootstrap Dingo using a Mithril snapshot. Dingo has a built-in Mithril client that handles download, extraction, and import automatically. This is the fastest way to get a node running.

# Bootstrap from Mithril and start syncing
./dingo -n preview sync --mithril

# Then start the node
./dingo -n preview serve

Or use the subcommand form for more control:

# List available snapshots
./dingo -n preview mithril list

# Show snapshot details
./dingo -n preview mithril show <hash>

# Download and import
./dingo -n preview mithril sync

For reproducible fresh-bootstrap comparisons, pin an exact artifact with --mithril-pinned-digest <identity> or DINGO_MITHRIL_PINNED_DIGEST. The identity is a snapshot digest for the v1 backend and a Cardano database artifact hash for v2. The pin is rejected for catch-up runs and cannot override the artifact recorded by an interrupted import.

The Docker entrypoint manages both a first-run or resumed Mithril sync and the subsequent serve process as direct children. It forwards SIGINT and SIGTERM to whichever child is active, waits for that child to finish, and returns the child's exit status instead of masking an interrupted bootstrap as success.

The default v2 backend restores incremental per-immutable-file archives only after checking the genesis-rooted certificate chain, certified Merkle root, and each immutable-file digest. It also requires the ancillary archive: its ledger-state and in-progress immutable files are checked against the manifest separately signed by the ancillary key. That signature authenticates that payload; it is not a stake certificate and does not validate the volatile blocks after the certified immutable point.

The legacy v1 full-snapshot backend is available for inspection and unverified library workflows, but it cannot be used for a verified fast bootstrap because it has no signed ancillary-state boundary. The mithril list and mithril show subcommands follow the configured backend.

This imports:

  • All blocks from genesis (stored in blob store for serving peers)
  • Current UTxO set, stake accounts, pool registrations, DRep registrations
  • Stake snapshots (mark/set/go) for leader election
  • Protocol parameters, governance state, treasury/reserves
  • Complete epoch history for slot-to-time calculations

Individual transaction records, certificate history, witness/script/datum storage, and governance vote records for blocks before the snapshot are not stored by the snapshot itself. In core mode these are not needed — consensus, block production, and serving blocks to peers work without them, and new blocks processed after bootstrap will have full metadata. In api mode, dingo mithril sync automatically runs a backfill step after loading the snapshot to populate this historical data, so API servers (Blockfrost, UTxO RPC, Mesh) have complete records from genesis.

Replay and bootstrap behavior

Dingo supports two working startup paths:

  • A normal chain sync builds ledger and database state from downloaded blocks. The default configuration validates historical blocks from origin and fails closed when required ledger/UTxO state is missing. Operators intentionally using a non-genesis intersection without complete pre-intersect state must explicitly set validateHistorical: false and strictUtxoValidation: false.
  • Mithril sync verifies the certificate chain and snapshot artifact, imports the separately ancillary-key-signed ledger state, stores certified immutable blocks, and strictly processes the gap between the imported state and immutable tip. Normal strict validation resumes at the imported point for the gap and all subsequently received network data. In API mode it then backfills historical query records before the APIs are used.

Performance (preview network, ~4M blocks):

Phase core mode api mode
Download snapshot (~2.6 GB) ~1-2 min ~1-2 min
Extract + download ancillary ~1 min ~1 min
Import ledger state (UTxOs, accounts, pools, DReps, epochs) ~12 min ~12 min
Load blocks into blob store ~36 min ~36 min
Backfill historical metadata ~varies
Total ~50 min ~50 min + backfill

Observed Mithril Bootstrap Timings

The following timings were measured during profiled core-mode validation runs on 2026-08-26 and 2026-08-28, from bootstrap start through completion:

Network Snapshot ready Bootstrap complete
mainnet 41m 51s 9h 10m 56s
preprod 4m 07s 37m 56s
preview 12m 11s 46m 22s

Mainnet's total includes its index rebuild; subsequent restarts reused the completed database rather than repeating the bootstrap.

A profiled Preview api-mode run completed snapshot bootstrap and historical metadata backfill with these timings:

Phase Duration
Mithril snapshot bootstrap 39m 13s
Historical metadata backfill 19h 57m 46s
Post-backfill index finalization ~16m 30s
Total through bootstrap completion ~20h 53m 19s

The finalization phase is approximate; the total is the end-to-end measurement and should not be reconstructed by summing the rounded phase durations.

The API-mode measurement was taken on 2026-08-30/31 against approximately 4.6M Preview blocks. The backfill processed 6.86M transactions at roughly 64 blocks per second.

The Preview API path was also measured in a profiled run on 2026-08-31/09-01 using the SQLite bulk-load pragmas and a temporary Mithril artifact cache. It completed in 7h 36m 07s end-to-end, including the historical metadata backfill (24,547s) and deferred index rebuild (16m). The earlier Preview API baseline was approximately 20h 53m, so this run used 63.6% less elapsed time. The approximately 30 GB Mithril cache is temporary and can be removed after the snapshot is imported. Peak bootstrap space was approximately 76 GB while the cache was present (46 GB database plus 30 GB cache); after cleanup, the database requires approximately 46 GB and a fresh bootstrap needs approximately 61 GB for the database plus the 15 GB snapshot.

Disk Space Requirements

Bootstrapping requires temporary disk space for both the downloaded snapshot and the Dingo database:

Network Snapshot Size Dingo DB Total Needed
mainnet ~180 GB ~200+ GB ~400 GB
preprod ~60 GB ~80 GB ~150 GB
preview ~15 GB ~25 GB ~50 GB
preview (API mode) ~15 GB ~46 GB ~61 GB minimum (~76 GB peak during bootstrap)

These are approximate values that grow over time. The snapshot can be deleted after import, but you need sufficient space for both during the load process.

Database Maintenance

dingo database provides offline snapshot, restore, and truncate operations. Each subcommand operates directly against the configured data directory and must not be run while a dingo node process has that directory open. All three honor SIGINT and SIGTERM so an interrupt unwinds cleanly instead of leaving a partial result behind.

# Capture a point-in-time snapshot. --dir must not already exist.
./dingo database snapshot --dir /backups/dingo-preview-2026-08-05

# Restore the configured data directory from a snapshot directory.
./dingo database restore /backups/dingo-preview-2026-08-05

# Rewind to a target point. Pass exactly one of --slot, --hash, --block-number.
./dingo database truncate --slot 12345678

Truncate makes the target block the new chain tip and removes every block and metadata row added after it. Unlike a normal chain rollback it does not reject a target beyond the security parameter, because it exists for disaster-recovery scenarios (see CIP-0135) where the chain must be rewound further than Ouroboros Praos allows. The resulting database is resync-ready from the target point.

The same operations are also exposed remotely through Bark's DatabaseService. Every DatabaseService RPC requires a client certificate verified against barkClientCaFilePath; destructive RPCs also require the certificate's SHA-256 fingerprint in barkOperatorCertificateFingerprints. Bark's read-only ArchiveService remains public on the same listener, so expose the Bark port only to the intended network.

Database Plugins

Dingo supports pluggable storage backends for both blob storage (blocks, transactions) and metadata storage. This allows you to choose the best storage solution for your use case.

Available Plugins

For local source builds, badger, sqlite, the default mempool, and all three built-in API providers are always available. GCS and S3 require -tags dingo_extra_plugins or an official release binary. The same tag adds the operational PostgreSQL and MySQL metadata providers, backed by the shared database/sql store and v1alpha1 schema.

Blob Storage Plugins:

  • badger - BadgerDB local key-value store (default)
  • gcs - Google Cloud Storage blob store
  • s3 - AWS S3 blob store

Metadata Storage Plugins:

  • sqlite - SQLite relational database (default)
  • postgres - PostgreSQL metadata store (requires dingo_extra_plugins)
  • mysql - MySQL metadata store (requires dingo_extra_plugins)

Mempool Plugins:

  • fifo - First-in, first-out transaction pool (default)
  • dag - Dependency-graph transaction pool that makes transaction dependencies explicit. Ledger validation remains the source of truth for both providers; the DAG backend changes ordering and selection, not validation.

API Plugins:

  • blockfrost - Blockfrost-compatible REST API
  • mesh - Mesh (Coinbase Rosetta) REST API
  • utxorpc - UTxO RPC gRPC API (serves both v1alpha and v1beta)

Badger value-log GC runs every five minutes at a 0.5 discard ratio by default. Operators may set gc: false for a controlled bulk load, but should re-enable GC for steady-state operation. GC activity and measurement guidance are documented in docs/badger-gc.md.

Plugin Selection

Plugins can be selected via command-line flags, environment variables, or configuration file:

# Command line
./dingo --blob gcs --metadata sqlite

# Environment variables
DINGO_PLUGINS_STORAGE_BLOB_PROVIDER=gcs
DINGO_PLUGINS_STORAGE_METADATA_PROVIDER=sqlite

# Configuration file (dingo.yaml)
plugins:
  storage:
    blob:
      provider: gcs
      config:
        bucket: my-cardano-blocks
    metadata:
      provider: sqlite
      config: {}

Plugin Configuration

Each capability has exactly one selected provider. Provider configuration is strictly decoded; unknown fields fail startup. Generic environment variables flatten the capability and config path, for example DINGO_PLUGINS_MEMPOOL_CONFIG_CAPACITY and DINGO_PLUGINS_API_UTXORPC_CONFIG_PORT. See dingo.yaml.example.

CARDANO_DATABASE_PATH (or databasePath / --data-dir) remains a shortcut that supplies the data directory to both local storage providers. Set dataDir on either local provider when blob and metadata storage need separate paths; the provider value overrides the shared shortcut.

BadgerDB Options:

  • dataDir - Badger data directory (defaults to the shared database path)
  • blockCacheSize - Block cache size in bytes
  • indexCacheSize - Index cache size in bytes
  • compression - Enable ZSTD compression
  • gc - Enable garbage collection

Leave mode-sensitive Badger settings unset to use storage-mode defaults.

Google Cloud Storage Options:

  • bucket - GCS bucket name

AWS S3 Options:

  • endpoint - Optional custom S3-compatible endpoint
  • bucket - S3 bucket name
  • region - AWS region
  • prefix - Path prefix within bucket
  • timeout - Request timeout

S3 credentials use the standard AWS credential chain.

SQLite Options:

  • dataDir - SQLite data directory (defaults to the shared database path)
  • maxConnections - Maximum connection count

Reserved PostgreSQL Options:

  • host - PostgreSQL server hostname
  • port - PostgreSQL server port
  • user - Database user
  • password - Database password
  • database - Database name
  • sslMode - PostgreSQL SSL mode
  • timeZone - PostgreSQL time zone (default: UTC)
  • dsn - Full PostgreSQL DSN (overrides the individual connection fields)
  • poolMaxOpenConns - Maximum open connections (default: 100)
  • poolMaxIdleConns - Maximum idle connections (default: 10)
  • poolConnMaxLifetime - Maximum connection lifetime (default: 1h)

Reserved MySQL Options:

  • host - MySQL server hostname
  • port - MySQL server port
  • user - Database user
  • password - Database password
  • database - Database name
  • sslMode - MySQL TLS mode (mapped to tls in the DSN)
  • timeZone - MySQL time zone location (default: UTC)
  • dsn - Full MySQL DSN (overrides other options when set)
  • poolMaxOpenConns - Maximum open connections (default: 100)
  • poolMaxIdleConns - Maximum idle connections (default: 10)
  • poolConnMaxLifetime - Maximum connection lifetime (default: 1h)

Migrating From Pre-Plugin Configuration

The plugin platform replaces the earlier per-plugin CLI flags and environment variables for storage, mempool, and API ports with the plugins.* config tree (YAML), the generic DINGO_PLUGINS_* environment scheme, and the provider selector flags. Every removed setting has an equivalent below; values are unchanged, only where they are set has moved.

Removed setting New equivalent
--mempool-capacity, CARDANO_MEMPOOL_CAPACITY plugins.mempool.config.capacity / DINGO_PLUGINS_MEMPOOL_CONFIG_CAPACITY
--eviction-watermark, DINGO_MEMPOOL_EVICTION_WATERMARK plugins.mempool.config.evictionWatermark / DINGO_PLUGINS_MEMPOOL_CONFIG_EVICTION_WATERMARK
--rejection-watermark, DINGO_MEMPOOL_REJECTION_WATERMARK plugins.mempool.config.rejectionWatermark / DINGO_PLUGINS_MEMPOOL_CONFIG_REJECTION_WATERMARK
DINGO_DATABASE_BLOB_PLUGIN --blob, plugins.storage.blob.provider, or DINGO_PLUGINS_STORAGE_BLOB_PROVIDER
DINGO_DATABASE_METADATA_PLUGIN --metadata, plugins.storage.metadata.provider, or DINGO_PLUGINS_STORAGE_METADATA_PROVIDER
--blob-badger-*, DINGO_DATABASE_BLOB_BADGER_* plugins.storage.blob.config.* / DINGO_PLUGINS_STORAGE_BLOB_CONFIG_*
--metadata-sqlite-*, DINGO_DATABASE_METADATA_SQLITE_* plugins.storage.metadata.config.* / DINGO_PLUGINS_STORAGE_METADATA_CONFIG_*
MYSQL_* MySQL connection aliases (-tags dingo_extra_plugins) plugins.storage.metadata.config.* / DINGO_PLUGINS_STORAGE_METADATA_CONFIG_*
--utxorpc-port, --blockfrost-port, --mesh-port plugins.api.<name>.config.port / DINGO_PLUGINS_API_<NAME>_CONFIG_PORT

Provider config fields use lowerCamelCase in YAML; the environment form uppercases them with underscore separators (dataDir becomes ..._CONFIG_DATA_DIR). The pre-plugin API port variables DINGO_UTXORPC_PORT, DINGO_BLOCKFROST_PORT, and DINGO_MESH_PORT still work as compatibility aliases, and setting an API port to 0 disables that server.

Listing Available Plugins

You can see all available plugins and their descriptions:

./dingo list

Plugin Development

For information on developing custom storage plugins, see database/plugin/PLUGIN_DEVELOPMENT.md.

Features

This checklist is a compact map of implemented feature areas. The package tests, conformance suite, DevNet, and public-network evidence provide the detailed validation record.

  • Network
    • UTxO RPC
    • Ouroboros
      • Node-to-node
        • ChainSync
        • BlockFetch
        • TxSubmission2
      • Node-to-client
        • ChainSync
        • LocalTxMonitor
        • LocalTxSubmission
        • LocalStateQuery
      • Peer governor
        • Topology config
        • Peer churn (full PeerChurnEvent with gossip/public root churn, bootstrap events)
        • Ledger peers
        • Peer sharing
        • Denied peers tracking
      • Connection manager
        • Inbound connections
          • Node-to-client over TCP
          • Node-to-client over UNIX socket
          • Node-to-node over TCP
        • Outbound connections
          • Node-to-node over TCP
  • Ledger
    • Blocks
      • Block storage
      • Chain selection (density comparison, VRF tie-breaker, ChainForkEvent)
    • UTxO tracking
    • Protocol parameters
    • Genesis validation
    • Block header validation (VRF/KES/OpCert cryptographic verification)
    • Certificates
      • Pool registration
      • Stake registration/delegation
      • Account registration checks
      • DRep registration
      • Governance
    • Transaction validation
      • Phase 1 validation
        • UTxO rules
        • Fee validation (full fee calculation with script costs)
        • Transaction size and ExUnit budget validation
        • Witnesses
        • Block body
        • Certificates
        • Delegation/pools
        • Governance
      • Phase 2 validation
        • Plutus V1 smart contract execution
        • Plutus V2 smart contract execution
        • Plutus V3 smart contract execution
  • Block production
    • VRF leader election with stake snapshots
    • Block forging with KES/OpCert signing
    • Slot battle detection
  • Mempool
    • Accept transactions from local clients
    • Distribute transactions to other nodes
    • Validation of transaction on add
    • Consumer tracking
    • Transaction purging on chain update
    • Watermark-based eviction and rejection
    • Selectable backend: FIFO (default) or DAG
  • Database Recovery
    • Chain rollback support
    • State restoration on rollback
    • WAL mode for crash recovery
    • Automatic rollback on transaction error
    • Cross-store commit fence with durable blob sync and commit timestamps
    • Partial-commit and blob-only timestamp divergence detection
    • Startup chain/ledger tip reconciliation and orphaned-blob cleanup
    • Recovery ordering ahead of history expiry
  • Database Lifecycle
    • Offline snapshot, restore, and truncate (dingo database)
    • Remote operation through the Bark DatabaseService
  • Stake Snapshots
    • Mark/Set/Go rotation at epoch boundaries
    • Genesis snapshot capture
  • API Servers
    • UTxO RPC (gRPC), serving v1alpha and v1beta
    • WIP Blockfrost-compatible REST API (required endpoint families are implemented; compatibility hardening and reward parity are ongoing)
    • Mesh (Coinbase Rosetta) API
    • Optional Midnight event indexer and MidnightState gRPC service
  • Mithril Bootstrap
    • Built-in Mithril client
    • Ledger state import (UTxOs, accounts, pools, DReps, epochs)
    • Block loading from ImmutableDB

Additional planned features can be found in our issue tracker and project boards.

Catalyst Fund 12 - Go Node (Dingo)
Catalyst Fund 13 - Archive Node

Check the issue tracker for known issues. Due to rapid development, bugs happen especially as there is functionality which has not yet been developed.

Development / Building

This requires Go 1.26 or later. You also need make.

The default target formats and builds. It does not run tests; use make test for those.

# Format and build (default target)
make

# Build only
make build

# Run
./dingo

# Run without building a binary
go run ./cmd/dingo/

make build builds every command under cmd/: the dingo node itself and koios-parity, which compares Dingo ledger state against Koios for a given network and epoch.

Metadata storage uses typed database/sql code generated by sqlc from sqlc.yaml. Regenerate it with make sql after changing a query, and make sql-check fails when the checked-in output is stale.

Testing

make test                                    # All tests with race detection
go test -v -race -run TestName ./package/    # Single test
make bench                                   # Benchmarks
make bench-mempool                           # Compare FIFO and DAG mempools
make docs-parity                             # Docs agree with go.mod, Makefile, compose, Koios matrix
make sql-check                               # Generated sqlc output is current

Conformance profiles

Dingo reports compatibility in separate layers. The ledger corpus is the pinned Cardano Blueprint archive consumed from ouroboros-mock; a green ledger result is not complete node conformance.

Profile Command Current scope
Ledger rules go test ./internal/test/conformance/ Blueprint ledger vectors, Dingo era validation entry points, and real SQLite/backend behavior; reports counts by era and rule family
Deterministic consensus go test ./ouroboros/ -run TestConsensusConformance Five shared scenarios covering origin ingestion, within-k and beyond-k forks, rollback/intersection, tie-breaking, and downstream ChainSync observations
Reference node ./internal/test/devnet/run-tests.sh --conformance Explicit Dingo-versus-cardano-node live compatibility profile; it is not run by either deterministic profile

The release and Linux CI gates run the ledger and deterministic profiles as part of ./...; the verbose profile reports contain the exact corpus and scenario counts. Reference-node compatibility remains an explicit DevNet check and is not represented as passing when that profile was not run.

Profiling

# Load testdata with CPU and memory profiling
make test-load-profile

# Analyze
go tool pprof cpu.prof
go tool pprof mem.prof

# Enable live pprof on loopback for serve or Mithril sync
DINGO_DEBUG_PORT=6060 ./dingo
go tool pprof http://127.0.0.1:6060/debug/pprof/heap

The live pprof server has no authentication or TLS. Its dedicated debugBindAddr defaults to 127.0.0.1 even when bindAddr or privateBindAddr uses a wildcard. External exposure therefore requires an explicit --debug-bind-addr, DINGO_DEBUG_BIND_ADDR, or debugBindAddr override and should be protected by a firewall or equivalent network policy.

DevNet

The default DevNet runs a private all-Dingo Cardano network: three Dingo block producers, one Dingo relay, and txpump. It validates Dingo-to-Dingo consensus, block diffusion, liveness, mempool behavior, and Dingo-only features.

Pass --conformance to run Dingo beside cardano-node for compatibility and reference-conformance testing.

Architecture

The default Docker Compose profile contains:

Container Role Host Port
dingo-1 Dingo block producer (pool 1) 3010
dingo-2 Dingo block producer (pool 2) 3013
dingo-3 Dingo block producer (pool 3) 3014
dingo-relay Dingo relay (no block production) 3015
txpump-dingo Submits transactions into Dingo's mempool

The opt-in conformance profile contains dingo-producer, cardano-producer, cardano-relay, and txpump. A configurator init container generates fresh pool keys and genesis files for either profile.

Prerequisites

  • Docker with the Compose plugin (docker compose)
  • Go 1.26+

Running the Automated Tests

The test suite builds the Dingo Docker image, starts all containers, waits for health checks, and runs Linux-only Go integration tests tagged with //go:build linux && devnet. Conformance-only scenarios additionally require devnet_conformance, while Dingo-only scenarios require !devnet_conformance:

cd internal/test/devnet/

# Run the all-Dingo suite
./run-tests.sh

# Run Dingo beside cardano-node
./run-tests.sh --conformance

# Run a specific test
./run-tests.sh -run TestBasicBlockForging

# Keep containers running after tests pass (for inspection)
./run-tests.sh --keep-up

Override host ports if needed:

DEVNET_DINGO_PORT=4010 DEVNET_CARDANO_PORT=4011 DEVNET_RELAY_PORT=4012 ./run-tests.sh

Running the DevNet Manually

For longer-running manual tests (soak testing, observing behavior over multiple epochs, debugging):

cd internal/test/devnet/

# Start all containers
./start.sh

# Watch logs
docker compose -f docker-compose.yml logs -f

# Watch a specific node
docker compose -f docker-compose.yml logs -f dingo-1

# Stop and clean up
./stop.sh

Containers remain running until you stop them. The DevNet parameters (in testnet.yaml) use 1-second slots and 500-slot epochs (~8 minutes per epoch) with activeSlotsCoeff=0.4 and securityParam (k)=40, so you can observe epoch transitions, leader election, and stake snapshot rotation relatively quickly.

See internal/test/devnet/README.md for full details on the harness, configurator, available test scenarios, and port/address overrides.

Local DevNet (Without Docker)

For quick iteration without Docker, devmode.sh runs Dingo directly against a local devnet genesis. It resets state and updates genesis timestamps on each run:

# Run in devnet mode
./devmode.sh

# With debug logging
DEBUG=true ./devmode.sh

This stores state in .devnet/ and uses genesis configs from config/cardano/devnet/. It runs a single Dingo node (no cardano-node counterpart), which is useful for testing startup, block production, and transaction submission in isolation.

The bundled devnet parameters track Yaci DevKit's default local cluster, so a dApp developer moving between the two sees the same chain shape: 1-second slots with activeSlotsCoeff=1.0, so the single producer forges a block every slot, and a 600-slot (10-minute) epoch. securityParam (k)=100 follows Yaci's derivation, which sizes k so the randomness stabilisation window is a fraction of the epoch rather than a multiple of it. Byron k=60 keeps a Byron epoch (10k slots) at the same 600 slots, with a 1-second Byron slot.

These same files ship in the release image as /opt/cardano/config/devnet (from docker-cardano-configs) and are what downstream tooling copies to generate a single-node devnet.

Releases

Packages

Used by

Contributors

Languages