This is a work-in-progress implementation of IBC v1 for Cardano. It implements a Cardano-native realization of IBC protocol semantics for interoperability between Cardano and the Cosmos ecosystem. The bridge implements ICS-02 (clients), ICS-03 (connections), ICS-04 (channels and packets), ICS-20 (fungible token transfer), and the proof/path model of ICS-23 and ICS-24, while adapting Cardano to the IBC client model through the experimental 08-cardano-probabilistic light client. The current light client has explicit observer and settlement-heuristic trust assumptions; it is not equivalent to a Tendermint light client or BFT finality.
The implementation adheres to the inter-blockchain communication protocol standards.
Caution
Disclaimer
Please be aware that this is an incubator project, and it is neither complete nor sufficiently tested at this time. The source code and software artifacts in this repository are subject to your own discretion and risk, and are not suggested as adequate for a production bridge deployment.
While we strive for high functionality and user satisfaction, unforeseen issues may arise due to the experimental nature of this project.
- Status
- Trust Model & Security Considerations
- Overview
- Architecture
- Getting Started
- Testing Against Cosmos Chains
- Demo: Direct Cosmos Routes
- Demo: Cross-chain Token Swap
- Additional Resources
- Contributing
| Area | Status | Notes |
|---|---|---|
| Local devnet stack | Active | caribic provisions five Cardano producers through Yaci DevKit |
| Core IBC semantics | Active | Implements clients, connections, channels, packets, acknowledgements, and timeouts |
| ICS-20 transfer path | Local: v8-classic |
Other local profiles need shared clock integration |
| Historical query backend | Active | Uses Yaci Store + Bridge Projection rather than a generic db-sync query surface |
| Public network integrations | Pre-production | Select paths exist for public testnets and external Cardano services, but the operating model is still evolving |
| Mithril light client and local setup | Deprecated / disabled | Not maintained for new deployments; source is retained only for historical reference and type compatibility |
| Production deployment | Not recommended | This repository should be treated as pre-production software |
There are currently protocol-level constraints that prevent IBC-style state proofs of Cardano, for example UTxO inclusion proofs. A valuable conversation on that topic can be found here: CIP-0165 (Canonical Ledger State).
The Cardano-native approach uses a bespoke Single Token Thread (STT) architecture alongside the 08-cardano-probabilistic light client to attain an analogous IBC state machine in Cardano semantics. The STT architecture over the IBC host state keyspace functions as an authenticated mutex for IBC host state mutation, where each authenticated mutation is tied to a corresponding merkle root of IBC state, while the probabilistic light client authenticates accepted Cardano history through configured settlement heuristics. This model is documented more deeply at Probabilistic Light Client Design.
A Cardano state root is only accepted once enough independent stake has built blocks on top of the block that carries it. The client needs at least 24 descendant blocks, at least 5 qualifying pools, and at least 5.11% of stake from those pools, and pools registered too recently do not count toward the last two.
24 blocks is a minimum, not a guarantee. If most blocks come from a few large pools, or from pools registered too recently to count, depth can reach 24 before enough independent pools and stake have taken part. In this run the root is only accepted at block 30.
The verifier checks the structure and internal consistency of submitted block witnesses, but canonical block history and epoch context currently come from configured observer data. Safety therefore depends on those data sources, tuned acceptance parameters, and an honest observer or relayer surfacing conflicting context; this is an explicit trust assumption of the current pre-production design.
The light client itself never makes a network call. The Gateway gathers everything it needs from named sources, Hermes carries it inside MsgUpdateClient, and the Go module verifies block hashes, pool signatures, VRF proofs, and leader eligibility from the submitted bytes alone. The epoch context (stake distribution and epoch nonce) is the part it takes on trust. The animation shows exactly what the Gateway and Hermes ask each service for, first to build a light-client header and then to build, check, and submit a Cardano transaction.
The older Mithril light client and local Mithril setup are deprecated, disabled, and not maintained. They remain in the repository only for historical design reference and protobuf/type compatibility.
The repository is organized around these main areas:
cardano: Cardano on-chain validators, off-chain deployment code, and the Gateway.cosmos: The standalone async-ICQ host, dormant VesselOracle module, shared Cardano light-client core, ibc-go v8 and v10 adapters, and preserved deprecated Mithril module. The v8 adapter targets Cosmos SDK 0.50; the async-ICQ host, VesselOracle, v10 adapters, and Mithril module target Cosmos SDK 0.53.proto-types: Shared protobuf contracts and generated TypeScript bindings, including the dormant VesselOracle integration contract.relayer: A Hermes fork with a native CardanoChainEndpointimplementation.caribic: The CLI for configuring, starting, stopping, and testing the local bridge stack.chains: Managed Cardano and counterparty-chain runtime configuration, including the pinned ibc-go compatibility profiles.dapps: Optional swap and explorer frontends.packagesandproto-types: Shared application packages and generated protocol bindings.docs,studies, andmanifests: Design documentation, analysis, and tracked deployment artifacts.
Chain history comes from Yaci Store on every network. Yaci Store is an indexer rather than a node: it follows a Cardano node and writes each block into its own Postgres database as rows of blocks, transactions, inputs, UTxOs, pool registrations, and epoch nonces. A sidecar copies the bridge's own rows into bridge_* tables, and the Gateway reads all of it with plain SQL, plus Yaci's REST API for raw block bytes. On public networks Yaci syncs from a recent checkpoint, and Blockfrost supplies older epoch and pool history.
All of the bridge's IBC state lives in one Merkle tree whose root sits in the HostState datum. Cardano validators check every update to that root, the light client carries it to Cosmos in an authenticated header, and each record is then proven against it with an ICS-23 membership proof. No step relies on trusting the Gateway or Hermes.
Additional architecture diagrams:
- Gateway escrow flow:
cardano/gateway/README.md#sendpacket-escrow-flow - Denom trace lifecycle:
docs/denom-trace-mapping.md - Probabilistic light client:
docs/probabilistic-light-client.md - Deprecated Mithril proof flow:
docs/mithril-light-client.md#mithril-proof-flow-for-relaying - Diagram index:
docs/architecture-overview.md
This project uses a fork of the Hermes IBC relayer with native Cardano support. The relayer is integrated as a git submodule pointing to:
Fork Repository: https://github.com/cardano-foundation/hermes-relayer
Branch: main
The Cardano implementation resides in relayer/crates/relayer/src/chain/cardano/ and includes:
ChainEndpointtrait implementation for Cardano- Hermes-specific SLIP-0010 Ed25519 mnemonic derivation using a Cardano-shaped path. This is not wallet-compatible Ed25519-BIP32/CIP-1852 derivation; verify the derived address before funding it.
- Intent-bound Cardano transaction validation and Ed25519 signing using Pallas primitives
- Gateway gRPC client for blockchain interaction
- Cardano-specific IBC types (Header, ClientState, ConsensusState)
- Full async runtime integration with Hermes's message-passing architecture
- Complete protobuf generation for Gateway Query and Msg services
Caution
When configuring Hermes, ensure your ~/.hermes/config.toml has the correct key_store_folder path. Use absolute paths, not tilde (~) notation, as tilde expansion may not work correctly:
[[chains]]
type = 'Cardano'
id = 'cardano-devnet'
bridge_manifest_path = '/absolute/path/to/bridge-manifest.json'
key_store_folder = '/Users/yourusername/.hermes/keys' # Absolute path requiredbridge_manifest_path is required for Cardano signing. It must name the trusted local deployment
manifest that corresponds to the Gateway's BRIDGE_MANIFEST_PATH; never source it from the
Gateway itself. caribic start resolves the active network profile's manifest, snapshots it into
the owner-only ~/.hermes/signing-security directory, and writes that snapshot path into
~/.hermes/config.toml, failing before Hermes starts if the artifact is absent. The Gateway's
deployment-artifact mounts are also read-only. Restart Hermes after changing or redeploying the
manifest.
Hermes also requires signing_utxo_kupo_url and signing_ogmios_url. It resolves every regular
and collateral input against the configured Kupo service and evaluates the exact unsigned CBOR
with the configured Ogmios service before loading the signing key. After signing, Hermes submits
the exact signed envelope directly to that Ogmios endpoint; the Gateway receives only its hash so
it can confirm inclusion and finalize the corresponding pending IBC-tree update. For hosted
Demeter endpoints, caribic start derives these settings from the active Gateway .env profile
and stores any required API-key files with owner-only permissions.
Keep the signing limits in the Cardano chain configuration at values appropriate for the funded
relayer wallet. In particular, max_wallet_lovelace_top_up bounds ADA the wallet may contribute
beyond the exact fee and any explicitly requested outbound lovelace transfer.
The default local Gateway URL uses plaintext on loopback. Hermes rejects plaintext connections to
non-loopback Gateway hosts. Remote deployments must use https://; configure
gateway_tls_ca_file for a private CA and pair gateway_auth_token_file with the Gateway's
GRPC_AUTH_TOKEN_FILE when bearer authentication is enabled. An independently configured
misbehaviour_witness_gateway_url follows the same rules; use its corresponding
misbehaviour_witness_gateway_tls_ca_file and
misbehaviour_witness_gateway_auth_token_file settings when needed.
The Hermes relayer implements Cardano transaction validation and signing using Pallas, a pure Rust library for Cardano primitives. The architecture separates transaction construction from authorization and signing:
- Gateway (NestJS/TypeScript) builds unsigned transactions using Lucid Evolution and handles all Cardano-specific domain logic (UTxO querying, fee calculation, and proof/header preparation)
- Hermes Relayer (Rust) derives the expected effect from the IBC message, decodes exactly one Gateway transaction, and checks it against the operator-pinned bridge manifest and configured fee, collateral, transaction-size, validity-interval, total protocol-output value, network, signer, input, output, mint, and reference-script policy before producing a signature
- Cardano validators remain the final authority for protocol state transitions; Hermes's policy prevents its fee key from authorizing an unrelated or materially broader transaction assembled by a compromised Gateway
This separation provides:
- Clean boundaries between chain-specific logic (Gateway) and generic IBC relaying (Hermes)
- Native integration with Hermes's keyring system following the same pattern as Cosmos SDK chains
- A local authorization boundary between the network-facing transaction builder and the funded signing key
- Easier testing and maintenance of validation and cryptographic signing separate from transaction construction
The Cardano chain implementation in Hermes (relayer/crates/relayer/src/chain/cardano/) follows the same architectural patterns as other supported chains, ensuring consistent behavior across the IBC ecosystem.
The following components are required to run the project:
- Docker
- Aiken
- Node.js
>= v22.13.0 - deno 2.7 or newer, required for the offchain evaluator dependency override
- golang
- Rust & Cargo
To check Docker, Docker Compose, Python, Aiken, Deno, Go, and the Linux-native Hermes build toolchain when applicable:
cd caribic
cargo run checkThe command does not currently validate Node.js or Rust/Cargo; verify those separately with node --version and cargo --version.
This project uses Docker containers that require platform-specific images depending on your operating system and CPU architecture. Some Docker images (such as Kupo) support multiple architectures (AMD64/x86_64 and ARM64), but Docker may not automatically select the correct one.
If you encounter issues with containers crashing immediately or OOM (Out-Of-Memory) errors, you may need to explicitly specify the platform in the Docker Compose configuration:
- ARM64 (Apple Silicon, M1/M2/M3 Macs): Ensure images specify
platform: linux/arm64 - AMD64/x86_64 (Intel/AMD processors): Use
platform: linux/amd64or omit the platform (defaults to AMD64)
Local Cardano images are pinned in chains/cardano/devkit/Dockerfile and chains/cardano/devkit/compose.yaml.
Local Cardano uses Yaci DevKit. See the Caribic guide for migration, startup timing and current limitations.
Warning
Mithril setup is deprecated, disabled, and not maintained. Do not use caribic start --with-mithril or caribic start mithril for new deployments. The Mithril sources and compose files remain only for historical reference and compatibility with old types.
Before using the CLI, build and install caribic locally:
cd caribic
cargo install --path . --force
cd ..To start the managed local Cardano devnet together with the bridge components, run:
caribic start --cleanIf you need to start components separately, use:
caribic start network
caribic start bridgeImportant
Cosmos chains must explicitly support the Cardano light client and allow it via ibc.core.client.v1.Params.allowed_clients (e.g., 08-cardano-probabilistic). If the client type is not registered/allowed on the Cosmos chain, creating the counterparty client will fail and IBC connection/channel handshakes cannot proceed. Also ensure the relayer key on those chains is funded; Cosmos SDK accounts can return NotFound until they receive tokens.
Three reproducible local profiles are available through the cosmos chain
adapter:
| Profile | Chain ID | Semantics | Current compatibility testing |
|---|---|---|---|
v8-classic |
v8-classic-1 |
IBC Classic | Enabled |
v10-classic |
v10-classic-1 |
IBC Classic | Waiting for local clock integration |
v10-v2 |
v10-v2-1 |
IBC v2 | Deferred |
Here, Classic identifies the IBC v1 and ICS-20 v1 workflow, not identical packet bytes or ibc-go integration APIs. The v8 and v10 Classic profiles exercise the same protocol flow while exposing version-specific APIs and JSON packet encoding. See Classic compatibility across v8 and v10 for the relevant JSON ordering and protobuf namespace differences.
Select any lifecycle profile explicitly:
caribic chain start --chain cosmos --network v8-classic
caribic chain health --chain cosmos --network v8-classic
caribic chain stop --chain cosmos --network v8-classicSee Local Cosmos compatibility profiles for pinned commits, endpoints, deterministic accounts, and direct Docker commands.
To stop the services:
caribic stopLocal and public-testnet flows use direct Cardano-to-target routes.
Direct routes require explicit target-chain support:
- The target Cosmos chain must compile and register the Cardano light client module, and allow the relevant client type in IBC client parameters.
- Cardano must support the target chain client type, usually Tendermint/CometBFT for Cosmos SDK chains.
- Operators must create direct Cardano clients, connections, and channels for each target chain.
- ICQ flows require the target chain to enable the relevant ICQ host/query module.
The migrated local network currently pairs with Cosmos v8-classic:
caribic setup route --from cardano --to cosmos --to-network v8-classicLocal demos currently use Cosmos v8-classic.
caribic start
caribic chain start --chain cosmos --network v8-classic
caribic setup route --from cardano --to cosmos --to-network v8-classic
caribic demo token-swap --chain cosmos --network v8-classicConfigure funding addresses in cardano.bootstrap_addresses.
For current end-to-end timeout and packet lifecycle validation, use the maintained integration suite:
caribic start --clean
caribic --verbose 5 testIf you encounter an error like DiffusionError Network.Socket.bind: permission denied (Operation not permitted) when starting the Cardano node, see the Cardano Forum thread on this issue.
If this doesn't resolve the issue, this is typically related to Docker runtime configuration. If using Colima on macOS, ensure you're using VirtioFS mount type by recreating Colima with colima delete followed by colima start --vm-type=vz --mount-type=virtiofs --network-address, and verify the cardano-node is configured to bind to 0.0.0.0 rather than a specific IP address.
This project stands on the shoulders of some incredible frameworks and tools developed by the Cardano community. Huge thanks to the developers behind these services—projects like this wouldn’t be possible without their hard work and innovation:
All contributions are welcome! Please feel free to open a new thread on the issue tracker or submit a new pull request.
Please read Contributing in advance. Thank you for contributing!
This project is licensed under Apache 2.0.





