Skip to main content

The Edge model

Most blockchain infrastructure providers hold your keys. That is what makes their API a single call — and it is also what makes them able to move your funds, and what makes a compromise of theirs a loss of yours.

ChainOS splits the problem instead. The consequence is a container you have to run. This page is about why that trade is worth it and what it costs you.

Two components

chainos-edge is a single static Go binary, in a scratch image, running in your network. It is the only component that ever sees key material. It reads your BIP-39 mnemonic from a mounted secret, derives your extended public keys, registers them with Cloud over a signed channel, terminates the full ChainOS API on port 8787, signs every transaction, and relays webhooks to your endpoint.

chainos-server is one Java artifact that runs in five roles, operated by Ziklag. It derives receive addresses from your extended public keys, watches the chains for activity on those addresses, tracks confirmations, constructs unsigned transactions, broadcasts what your Edge has signed, and delivers events.

What each side holds

Your EdgeCloud
BIP-39 mnemonic✅ mlocked, zeroed after use❌ never
Child private keys✅ derived on demand, never persisted❌ never
Edge identity private key✅ derived at m/83696968'/0'/0'❌ never
Extended public keys✅ encrypted at rest
Edge identity public key✅ pinned on first sync
Address ledger, transactionsmirrored✅ authoritative
Chain RPC credentials

That table is the whole architecture. Everything below is a consequence of it.

The request flows

Issuing an address needs only public data, so it does not need your Edge to be reachable at that instant — but it does need Cloud to have your extended public keys, which means the Edge must have synced at least once.

Your app calls the Edge, which forwards to Cloud; Cloud derives the next address from your stored extended public key and returns it back through the Edge.

Issuing an address touches no private key at all.

Solana and Stellar are the exceptions and cannot work this way at all. Both are Ed25519, which has no public-key-only child derivation, so no extended public key exists to store and their addresses come from a pool the Edge fills. See HD derivation.

A chain event stream feeds the ingest role, which normalises onto the event bus; processor matches the address, writes the ledger and tracks confirmations; dispatcher delivers the webhook through your Edge to your endpoint. A reconciliation sweep re-scans monitored addresses in parallel.

Watching for deposits is entirely Cloud-side. Chain event streams push into the ingest role, which normalises them onto the event bus; processor matches them against the address registry, writes the ledger and tracks confirmations; dispatcher delivers the webhooks. Your Edge is not on this path except as the last relay hop.

Sending funds is the one flow that requires all three parties, in order:

Your app posts a transaction to the Edge, which forwards to Cloud; Cloud runs pre-flight checks and builds an unsigned transaction; the Edge signs it; Cloud broadcasts it.

Cloud can propose a transaction. Only your Edge can sign one. A compromised Cloud can therefore ask your Edge to send funds somewhere, which is why autonomous spend authority is capped Edge-side and defaults to zero — see Non-custodial.

Connection direction is an invariant

Every connection is dialled outbound by the Edge: the sync, the heartbeat, the gRPC stream, the lease calls. Cloud never initiates a connection to your infrastructure.

That means the Edge needs no inbound firewall rule, no public ingress, no load balancer and no DNS record. In a bank, those four things are each a change request with a different owner, and avoiding all of them is usually what gets this product approved.

The gRPC channel is bidirectional once open, so Cloud can ask the Edge for something — more pool addresses for Solana or Stellar, a signature — but only over a stream the Edge itself opened.

One artifact, five roles

chainos-server selects its behaviour at run time from CHAINOS_ROLES:

RoleResponsibility
apiREST, the console, the docs, the OpenAPI document
gatewayEdge gRPC streams, leader leases
ingestChain event stream receiver
processorEvent-bus consumers, the ledger, confirmation tracking
dispatcherWebhook delivery and the retry ladder

One chainos-server image fans out into five roles — api, gateway, ingest, processor and dispatcher — each with its own responsibility and its own scaling pressure.

A module is a compile-time boundary. A role is a run-time selection. They are different axes.

Production runs five role-specialised replica sets that scale independently, from the same image — ingest scales for provider bursts, processor for chain volume, api for console traffic, and those three genuinely move apart. A development machine runs all five in one process.

The reason it is one artifact rather than five services: one artifact has one version, one migration history and one thing to roll back. That is also why the console is a static bundle inside the JAR rather than an app needing a Node runtime, and why there is no Redis.

Internally the code is fourteen modules with boundaries verified in CI, which is a different axis from roles: a module is a compile-time boundary, a role is a runtime selection.

What it costs you

Stating the trade honestly, because the alternative genuinely is simpler:

  • You run a container. One, with two required environment variables, but it is yours to deploy, monitor and upgrade.
  • You own the mnemonic. Nobody can recover it. This is the real cost and it is a process cost, not a technical one — see Mnemonic management.
  • Some operations need the Edge live. Signing, obviously. Solana and Stellar address allocation, because both pools come from the Edge. Everything else — address issuance on the other seven chains, balances, deposit monitoring, webhook delivery — survives an offline Edge, with webhooks arriving late rather than being lost.
  • An offline Edge is visible, not silent. Liveness is polled and edge_offline is itself a webhook event.

What you get for it

  • Ziklag cannot move your funds. Not by policy — by what it holds.
  • A compromise of Ziklag is not a loss of your funds.
  • Your application holds no Ziklag credential.
  • No inbound network exposure.
  • The derivationPath on every address is absolute, so you can recover funds with any standard BIP-32 tool and no ChainOS at all.

Further reading