Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

EchoForge OS

A declarative NixOS distribution for the full Cardano stack — one base, four profiles.

NixOS Cardano Platforms License flake check


EchoForge OS (EFOS) is a NixOS-based operating system for everyone who touches Cardano — stakers, dApp developers, stake-pool operators and DePIN fleet runners. Instead of four separate distros, it ships one shared, hardened base and four build targets that differ only in which modules get wired in. Everything is a flake output: rebuild it anywhere, bit-for-bit, from this repository.

Architecture — one shared base, the echoforge.* option namespace, and four profile cards showing which modules each wires in

The four profiles

Profile Audience Form In one line
echoforge-desktop Hobbyist · staking & browsing GUI light desktop Read-only desktop with Lace + Ledger; the node is entirely absent — zero overhead
echoforge-dev Smart-contract developer GUI/CLI sandbox On-demand local devnet (~200 MB, second-level blocks) + Aiken / GHC / Ogmios / Kupo + Zed
echoforge-spo Power user · protocol & full node GUI/TUI hybrid Mithril snapshot sync for preview / preprod / mainnet, TUI monitoring, optional block producer
echoforge-depin DePIN edge node Headless · x86_64 mini-PC (RPi4 variant) tmpfs root, ZRAM, hardware watchdog — survives power loss unattended

Quick start

git clone https://github.com/EchoForge-Dev/EchoForge-OS.git
cd EchoForge-OS

# 1. Build any profile closure (on NixOS or a Linux builder)
nix build .#nixosConfigurations.echoforge-dev.config.system.build.toplevel

# 2. Activate it on the target machine
sudo nixos-rebuild switch --flake .#echoforge-dev

# 3. depin on a Raspberry Pi 4: flashable SD image
#    (.img.zst — root partition auto-expands on first boot)
nix build .#packages.aarch64-linux.depin-sd-image

Disk conventions — partition by label at install time, no code changes needed: echoforge-root (ext4) + EFOS-BOOT (vfat) for the desktop-class profiles; the depin SSD deployment (echoforge-depin, echoforge-depin-rpi4) expects echoforge-nix, echoforge-data, optional echoforge-swap and FIRMWARE; the SD-image form (echoforge-depin-rpi4-sd) needs no manual partitioning at all.

To bump an upstream binary, edit the version in pkgs/cardano/*.nix and run ./scripts/prefetch-hashes.sh to re-verify and fill in the SRI hashes.

ef-cli — the dynamic node engine

The system never boots a heavyweight node. No node unit declares wantedBy, so nothing enters the boot dependency tree — ef-cli is the only way up, and stop is a full release:

ef-cli node start --mode devnet                      # local private chain (~200 MB, second-level blocks)
ef-cli node start --mode mithril --network preview   # Mithril snapshot → preview/preprod/mainnet
ef-cli node stop                                     # release all node memory & CPU
ef-cli node status [--waybar]                        # status query / status-bar JSON
ef-cli pool status [--network N] [--json]            # producer / KES-period status
ef-cli pool rotate-kes [--network N]                 # new KES pair + offline re-signing steps
ef-cli version                                       # version / profile / build revision
ef-cli profile switch <desktop|dev|spo|depin>        # nixos-rebuild into another profile

The monochrome breathing light on the Waybar tracks it live: gray = OFF, bright white = running. One socket for everything: /run/echoforge/node.socket; Ogmios at 127.0.0.1:1337, Kupo at 127.0.0.1:1442.

Network configuration (config.json, genesis files, public topology) is taken from the share/cardano/ directory shipped inside the pinned cardano-node release, not fetched at runtime from book.world.dev.cardano.org — the published files always track the newest node, so a pinned binary eventually fails to parse them. Bundled config keeps the two in lockstep and lets a node start with no network at all. The devnet derives its own config from the preview template: genesis hashes replaced, checkpoints dropped, every hard fork at epoch 0 (Conway from block one) and k=36 / f=0.5 / epochLength=720 — ~2 s blocks, 12-minute epochs.

Resource footprint — 0 MB / 0% CPU node usage after boot on all four profiles, declared memory ceilings, and per-profile quota comparison

Security model

  • Immutable systemusers.mutableUsers = false; every change goes through flake.nix + nixos-rebuild. No runtime apt/pacman, no hand-editing /etc.
  • Zero secret leakage — sensitive material exists only as sops-nix Age ciphertext, decrypted to the in-memory mount /run/secrets/ (see secrets/README.md).
  • Network isolation — the RPC surface (Ogmios, Kupo, and the devnet node) binds to 127.0.0.1, guarded by a build-time assertion. The full node's P2P socket binds 0.0.0.0 because outbound peering requires it, and reachability is decided by the firewall — which denies all inbound by default, opening SSH (key-only) on spo / depin and the P2P port only when a relay explicitly asks for it.

Security posture matrix — five hard rules enforced on every profile, open-port counts per profile, and the per-safeguard comparison table

Preinstalled on the desktop profiles (desktop / dev / spo)

App Integration
Lace Wallet Forced install via Chrome enterprise policy ExtensionInstallForcelist — works out of the box
Ledger hardware.ledger.enable = true + udev rules — plug and use
Zed Editor Preinstalled with the EFDS dark high-contrast theme + Aiken / Haskell / Nix extensions
Google Chrome Homepage & new-tab policy locked to echoforgellc.tech / stickmancharles.com

Capability matrix — desktop and wallet apps, node subsystems and ops tooling across the four profiles

SPO block production

Out of the box the spo profile is an observer: it peers outbound over the public topology and syncs, but no inbound port is opened and the RPC surface stays on loopback. Two commented role blocks in profiles/spo.nix turn it into a real pool — pick exactly one per machine.

Relay — reachable, public port open, localRoots pointing at your own producer and sibling relays:

mithril.openFirewall = true;
mithril.topology.localRoots = [ { address = "10.0.0.10"; port = 3001; } ];
mithril.topology.bootstrapPeers = [ { address = "backbone.cardano.iog.io"; port = 3001; } ];

Block producer — stays out of allowedTCPPorts, so nothing reaches it from outside; localRoots lists only your own relays, and the generated topology pins useLedgerAfterSlot = -1 with bootstrapPeers = null, so the producer never touches public peer discovery:

mithril.blockProducer.enable = true;
mithril.topology.localRoots = [ { address = "relay-1.example.com"; port = 3001; } ];

With blockProducer.enable, the ef-node@ unit appends --shelley-kes-key / --shelley-vrf-key / --shelley-operational-certificate, sourced from sops (secrets/secrets.yaml/run/secrets/pool/). Missing secrets fail the build — you cannot ship a producer without its keys. Build-time guards also catch openFirewall with a loopback p2pAddr (assertion), and warn when a producer runs public topology, opens its port, or sets bootstrap peers.

Two addresses, and mixing them up is expensive. node.mithril.p2pAddr (default 0.0.0.0) is what the node binds for peer-to-peer; node.hostAddr (default 127.0.0.1) is where the indexer layer and RPC listen. p2pAddr defaulting to 0.0.0.0 does not expose anything — inbound reachability is the firewall's job — but --host-addr also fixes the source address of outbound connections, so binding it to loopback makes every outbound connect return EINVAL. The node then sits at zero peers, frozen at the block the snapshot ended on, while syncProgress still reads 99%+ and everything looks healthy.

Day-to-day operation runs through ef-cli pool. cardano-cli is on PATH on any node-enabled profile, with CARDANO_NODE_SOCKET_PATH pre-pointed at /run/echoforge/node.socket (the operator account is in the cardano group; the socket is 0770, the KES/VRF keys stay 0400 and out of reach):

ef-cli pool status          # KES period, certificate validity window, on-chain vs local counter
ef-cli pool rotate-kes      # new KES pair in tmpfs + the exact offline issue-op-cert command

rotate-kes generates the KES pair into $XDG_RUNTIME_DIR (memory-backed, never on disk) and computes the current KES period from chain tip and Shelley genesis. It stops there by design: the cold key lives on your offline signer, so re-issuing the operational certificate and folding both artifacts back into sops stay manual steps.

Not covered: on-chain pool onboarding — cold/stake key generation, pool registration and delegation certificates, metadata hosting, and the 500 ADA deposit transaction — is plain cardano-cli work against the running node.

SPO workflow — ef-cli triggers the Mithril snapshot sync unit, then the full-node unit; block-production keys are injected via sops; relay vs producer comparison

Handing the machine to an AI agent

An immutable, declarative system is an unusual place for an agent trained on Ubuntu habits: installing a package, editing /etc, enabling a service — the reflexes are all wrong here, and they fail quietly rather than loudly. AGENTS.md states the rules that actually apply, the ef-cli surface, and a diagnosis section built from failures observed on hardware — including several where the surface reading (syncProgress at 99%, a unit reporting active) stayed reassuring while the node was doing nothing at all. It also marks the line an agent must not cross alone: anything touching keys, funds, or a producing node.

Claude Code, Cursor and Codex read AGENTS.md automatically.

Repository layout

AGENTS.md                  # operating rules for an AI agent running this machine
flake.nix                  # four profile build targets + package outputs
├── profiles/              # desktop / dev / spo / depin (+ depin hardware & disk layers)
├── modules/
│   ├── options.nix        # the echoforge.* option namespace
│   ├── common/            # immutable base · security policy · sops-nix secrets
│   ├── cardano/           # dynamic node engine (devnet / mithril / ogmios+kupo units)
│   └── desktop/           # Hyprland + Waybar GUI, Lace / Ledger / Zed / Chrome integration
├── home/                  # EFDS visual layer (Waybar breathing light, Zed theme)
├── pkgs/                  # ef-cli + static Cardano binary packaging
├── scripts/               # node runners + hash-prefetch tooling
├── secrets/               # sops-nix Age templates (decrypt to /run/secrets)
└── docs/                  # engineering figures + brand assets

Known limitations

  1. aarch64 node trust chain — the cardano-node-bin aarch64 static binaries come from the community Armada Alliance builds (musl static, version-aligned with the official x86_64 11.0.1 binaries). If you require same-origin builds, compile from source via haskell.nix and override cardano-node-bin in the overlay.
  2. Real-hardware validationdev and spo have been run on an x86_64 VPS (devnet and Mithril-synced preview, Ogmios/Kupo on top). The echoforge-depin-rpi4-sd image (sd-image-aarch64 + nixos-hardware RPi4) has not yet been flashed and verified on a physical RPi4.
  3. RAM is the binding constraint for a full node — measured at the tip: ~3.1 GB resident, ~3.3 GB peak during ledger replay, and replay is memory-bound rather than CPU-bound. A 4 GB board cannot hold a node; 8 GB covers preview. depin therefore targets an x86_64 mini-PC by default, with the RPi4 as a hardware overlay.
  4. Devnet chain is disposable — the devnet genesis is generated by the pinned cardano-cli, and its parameter set moves with the binary. Bumping cardano-node therefore regenerates the genesis and discards the local chain and its Kupo index (announced on the next ef-cli node start --mode devnet). Nothing you need to keep should live on the devnet.

License

Code is licensed under Apache-2.0. The EchoForge name, wordmarks and logo assets under docs/brand/ are not open source — see LICENSE-NOTICE.md.

Design language: EchoForge Design System — pure black / white / gray, four status colors, IBM Plex Mono.

About

A declarative NixOS distribution for the full Cardano stack — one base, four profiles.

Topics

Resources

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages