Skip to content

Commit 2a10fe1

Browse files
author
tazhate
committed
feat(oss): prepare v0.1.0 OSS release
Add standard OSS files, architecture docs, and release automation. Polish README and existing docs to reflect all features built over the past weeks. Context: - Reviewed all ~102 adapters — DefaultResources() and VersionPolicy() already in place from prior sessions - Added OCI v2 client for GAR / ECR Public in previous commits - Spent ~1h writing docs and reviewing adapter internals for accuracy - Ported two finso fixes: BSC StartupProbe (30min) and dash -printtoconsole removal
1 parent efec02c commit 2a10fe1

13 files changed

Lines changed: 455 additions & 284 deletions

File tree

.github/workflows/release.yml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
jobs:
9+
release:
10+
name: Build and Release
11+
runs-on: ubuntu-latest
12+
permissions:
13+
contents: write
14+
packages: write
15+
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0
21+
22+
- name: Setup Go
23+
uses: actions/setup-go@v5
24+
with:
25+
go-version: "1.25"
26+
27+
- name: Sanity check
28+
run: go build ./... && go vet ./...
29+
30+
- name: Login to GitHub Container Registry
31+
uses: docker/login-action@v3
32+
with:
33+
registry: ghcr.io
34+
username: ${{ github.actor }}
35+
password: ${{ secrets.GITHUB_TOKEN }}
36+
37+
- name: Set up QEMU
38+
uses: docker/setup-qemu-action@v3
39+
40+
- name: Set up Docker Buildx
41+
uses: docker/setup-buildx-action@v3
42+
43+
- name: Build and push multi-arch image
44+
uses: docker/build-push-action@v5
45+
with:
46+
context: .
47+
platforms: linux/amd64,linux/arm64
48+
push: true
49+
tags: |
50+
ghcr.io/tazhate/blockchain-node-operator:${{ github.ref_name }}
51+
ghcr.io/tazhate/blockchain-node-operator:latest
52+
53+
- name: Package Helm chart
54+
run: |
55+
VERSION=${GITHUB_REF_NAME#v}
56+
helm package charts/blockchain-node-operator --version ${VERSION}
57+
58+
- name: Create GitHub Release
59+
uses: softprops/action-gh-release@v2
60+
with:
61+
generate_release_notes: true
62+
files: blockchain-node-operator-*.tgz

CHANGELOG.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog 1.1](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
The CRD API is currently `v1alpha1` — breaking changes may occur in any minor
9+
release until the API is promoted to `v1beta1`.
10+
11+
## [Unreleased]
12+
13+
## [0.1.0] - 2026-04-28
14+
15+
Initial OSS release.
16+
17+
### Added
18+
19+
- **102 blockchain adapters** spanning Ethereum L1/archive/beacon, Bitcoin and
20+
UTXO-family, BSC, TRON, Solana, Cosmos ecosystem (Cosmos Hub, Osmosis, Sei,
21+
Evmos, Kava, Axelar, Dymension), Polkadot/Kusama, Substrate parachains
22+
(Moonbeam, Moonriver), 46 EVM L2s (Arbitrum, Optimism, Base, zkSync, Linea,
23+
Scroll, Mantle, Taiko, all OP Stack chains, etc.) and others (Aptos, Sui,
24+
NEAR, TON, Cardano, Stellar, Filecoin, XRP, etc.).
25+
- **`BlockchainNode` CRD** for declarative node lifecycle: chain, network,
26+
client, image, storage, RPC, snapshot bootstrap, health monitoring.
27+
- **`ChainVersionCatalog` CRD** for tracking the latest container image
28+
versions of supported chains via a configurable polling interval.
29+
- **`DefaultResources()` interface** on every adapter — returns recommended
30+
CPU, memory and storage based on official documentation.
31+
- **`VersionPolicy()` interface** on 96/101 adapters — drives auto-tracking of
32+
upstream image releases through `ChainVersionCatalog`.
33+
- **OCI v2 registry client** in `internal/registry/oci.go` — supports Google
34+
Artifact Registry (`us-docker.pkg.dev`) and Amazon ECR Public
35+
(`public.ecr.aws`) alongside the existing Docker Hub and GHCR clients.
36+
- **Auto-upgrade reconciler** with rolling restart and automatic rollback on
37+
`CrashLoopBackOff` (≥3 container restarts).
38+
- **Snapshot bootstrap** through MinIO-backed init containers; supports `full`
39+
and `lite` snapshot variants.
40+
- **Health monitoring** with chain-specific block-lag thresholds, sync stall
41+
detection, peer count tracking, and auto-restart on degraded timeout.
42+
- **Validating admission webhook** with per-chain resource recommendation
43+
warnings; defaulting webhook for common spec fields.
44+
- **Prometheus metrics**: `blockchain_node_block_height`,
45+
`blockchain_node_sync_progress`, `blockchain_node_peers_count`,
46+
`blockchain_node_phase`, `blockchain_node_restarts_total`,
47+
`blockchain_node_degraded_duration_seconds`.
48+
- **Fleet Status dashboard** — embedded HTML/JS UI with real-time node table,
49+
per-node detail, namespace filtering, JSON API and Prometheus metrics.
50+
- **Helm chart** at `charts/blockchain-node-operator` with HA defaults,
51+
webhook + cert-manager integration, optional `ServiceMonitor`.
52+
- **Multi-arch container images** (linux/amd64, linux/arm64) published to
53+
`ghcr.io/tazhate/blockchain-node-operator`.
54+
- **CI workflows** for unit tests, golangci-lint, Helm validation and
55+
tag-driven releases.
56+
57+
[Unreleased]: https://github.com/tazhate/blockchain-node-operator/compare/v0.1.0...HEAD
58+
[0.1.0]: https://github.com/tazhate/blockchain-node-operator/releases/tag/v0.1.0

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Image URL to use all building/pushing image targets
2-
IMG ?= controller:latest
2+
IMG ?= ghcr.io/tazhate/blockchain-node-operator:latest
33

44
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
55
ifeq (,$(shell go env GOBIN))

README.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Supports **102 chains** with built-in health monitoring, snapshot bootstrapping,
66
[![License: Unlicense](https://img.shields.io/badge/license-Unlicense-blue.svg)](https://unlicense.org)
77
[![Go](https://img.shields.io/badge/Go-1.23+-00ADD8.svg)](https://golang.org/)
88
[![Kubernetes](https://img.shields.io/badge/Kubernetes-1.25+-326CE5.svg)](https://kubernetes.io/)
9+
[![Release](https://img.shields.io/github/v/release/tazhate/blockchain-node-operator)](https://github.com/tazhate/blockchain-node-operator/releases)
910

1011
## Overview
1112

@@ -40,6 +41,9 @@ spec:
4041
- **Snapshot bootstrap** — MinIO-based snapshot restore to skip days of initial sync
4142
- **Multi-client Ethereum** — Nethermind, Geth, Reth, and Erigon selectable via a single CRD field
4243
- **Adapter pattern** — each chain is a self-contained Go file providing image, config template, health check, CLI flags, env vars, and probes
44+
- **DefaultResources() on all 102 adapters** — recommended CPU/memory/storage derived from official chain documentation; the validating webhook uses these to warn when resources are below recommended minimums
45+
- **VersionPolicy() on 96/102 adapters** — drives `ChainVersionCatalog` auto-tracking by declaring the registry, repository, and tag pattern for each chain image
46+
- **Multi-registry support** — docker.io, ghcr.io, Google Artifact Registry (`us-docker.pkg.dev`), and Amazon ECR Public (`public.ecr.aws`) via a unified OCI v2 client
4347
- **Webhook validation** — admission webhook enforces minimum storage/memory requirements and immutable chain/network fields
4448
- **Sidecars** — attach consensus-layer clients (e.g. Lighthouse for Ethereum) or any helper process
4549

@@ -442,10 +446,14 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for how to add support for a new chain ad
442446
| Doc | Description |
443447
|-----|-------------|
444448
| [docs/getting-started.md](docs/getting-started.md) | Installation and first steps |
445-
| [docs/adapters.md](docs/adapters.md) | Per-chain adapter details (images, ports, health checks) |
446-
| [docs/adding-new-chain.md](docs/adding-new-chain.md) | Step-by-step guide to adding a new chain |
449+
| [docs/architecture.md](docs/architecture.md) | Component overview and reconciliation flow |
450+
| [docs/configuration.md](docs/configuration.md) | CRD spec fields (full configuration reference) |
451+
| [docs/adapters.md](docs/adapters.md) | All supported chains — images, ports, health checks |
452+
| [docs/adding-new-chain.md](docs/adding-new-chain.md) | Adapter development guide |
453+
| [docs/registry-support.md](docs/registry-support.md) | Supported image registries (docker.io, ghcr.io, GAR, ECR Public) |
454+
| [docs/fleet-dashboard.md](docs/fleet-dashboard.md) | Web UI for fleet-wide node status |
455+
| [docs/release-process.md](docs/release-process.md) | Release workflow (for maintainers) |
447456
| [docs/health-monitoring.md](docs/health-monitoring.md) | Health trigger system deep-dive |
448-
| [docs/configuration.md](docs/configuration.md) | Full CRD configuration reference |
449457
| [docs/chain-verification.md](docs/chain-verification.md) | Adapter verification report |
450458

451459
## License

charts/blockchain-node-operator/values.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ replicaCount: 2
33

44
image:
55
# -- Operator image repository
6-
repository: controller
6+
repository: ghcr.io/tazhate/blockchain-node-operator
77
# -- Image pull policy
88
pullPolicy: IfNotPresent
99
# -- Overrides the image tag (default is the chart appVersion)

docs/adding-new-chain.md

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,43 @@ func (a *myutxoAdapter) HealthCheck(ctx context.Context, rpcURL string) (SyncSta
9595

9696
For non-EVM, non-UTXO chains, implement `HealthCheck` directly by calling the chain's RPC.
9797

98-
## Step 3: Optional Interfaces
98+
## Step 3: Implement DefaultResources()
99+
100+
Every adapter **must** implement `DefaultResources()`. Return a `ResourceDefaults` struct with the recommended CPU request, memory request, and storage size based on the chain's official node documentation. The validating webhook uses these values to warn operators when a `BlockchainNode` is created with resources below the recommended minimums.
101+
102+
```go
103+
import "k8s.io/apimachinery/pkg/api/resource"
104+
105+
func (a *mychainAdapter) DefaultResources() ResourceDefaults {
106+
return ResourceDefaults{
107+
CPURequest: resource.MustParse("4"),
108+
MemoryRequest: resource.MustParse("16Gi"),
109+
Storage: resource.MustParse("600Gi"),
110+
}
111+
}
112+
```
113+
114+
Use values from the official chain documentation or node operator guides. When in doubt, err on the side of slightly higher recommendations — the webhook issues a warning, not a rejection, so operators can still override downward.
115+
116+
## Step 4: Implement VersionPolicy()
117+
118+
If the chain's images are published with versioned tags (e.g. `v1.2.3`) on a supported registry, implement `VersionPolicy()` to enable `ChainVersionCatalog` auto-tracking:
119+
120+
```go
121+
func (a *mychainAdapter) VersionPolicy() ChainVersionPolicy {
122+
return ChainVersionPolicy{
123+
Registry: "ghcr.io",
124+
Repository: "myorg/mychain",
125+
TagPattern: `^v\d+\.\d+\.\d+$`,
126+
}
127+
}
128+
```
129+
130+
Supported registries: `docker.io`, `ghcr.io`, `us-docker.pkg.dev` (Google Artifact Registry), `public.ecr.aws` (Amazon ECR Public).
131+
132+
If the chain only publishes a `:latest` tag (no versioned tags), **skip this method** — the base adapter's no-op implementation will be used and the chain will simply not appear in the version catalog.
133+
134+
## Step 5: Optional Interfaces
99135

100136
Implement any of these if needed:
101137

@@ -108,7 +144,7 @@ Implement any of these if needed:
108144
| `StartupProbeProvider` | Long startup time (e.g. TRON Java, >5min) |
109145
| `InitContainerProvider` | Custom init containers (e.g. SUI snapshot download) |
110146

111-
## Step 4: Add Snapshot Bucket
147+
## Step 6: Add Snapshot Bucket
112148

113149
Edit `internal/snapshot/snapshot.go`, add to `bucketForChain()`:
114150

@@ -117,15 +153,15 @@ case nodesv1alpha1.ChainMyChain:
117153
return "snapshots-mychain"
118154
```
119155

120-
## Step 5: Add Webhook Validation
156+
## Step 7: Add Webhook Validation
121157

122158
Edit `api/v1alpha1/blockchainnode_webhook.go`, add to `validationRegistry`:
123159

124160
```go
125161
nodesv1alpha1.ChainMyChain: {MinStorage: resource.MustParse("100Gi"), MinMemory: resource.MustParse("4Gi")},
126162
```
127163

128-
## Step 6: Create Sample CR
164+
## Step 8: Create Sample CR
129165

130166
Create `config/samples/nodes_v1alpha1_blockchainnode_mychain.yaml`:
131167

@@ -160,13 +196,13 @@ spec:
160196
161197
Add it to `config/samples/kustomization.yaml`.
162198

163-
## Step 7: Update Documentation
199+
## Step 9: Update Documentation
164200

165201
1. Add chain to `docs/adapters.md` table and per-chain section
166202
2. Add to README.md supported chains table
167203
3. Update Helm CRD if enum changed: copy regenerated CRD to `charts/blockchain-node-operator/templates/crds/`
168204

169-
## Step 8: Tests
205+
## Step 10: Tests
170206

171207
Add to existing test files:
172208
- `internal/adapters/adapters_test.go` — chain is auto-covered by `allChains` loop tests if added to the list
@@ -180,6 +216,8 @@ Add to existing test files:
180216
- [ ] `DefaultImage()` returns a real, versioned image
181217
- [ ] `HealthCheck()` works with the chain's RPC
182218
- [ ] `ConfigTemplate()` returns valid config (or empty)
219+
- [ ] `DefaultResources()` returns recommended CPU/memory/storage from official docs
220+
- [ ] `VersionPolicy()` implemented (or intentionally skipped for `:latest`-only images)
183221
- [ ] Snapshot bucket in `snapshot.go`
184222
- [ ] Webhook validation entry
185223
- [ ] Sample CR YAML

docs/architecture.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Architecture
2+
3+
## Overview
4+
5+
The blockchain-node-operator is a Kubernetes operator that manages blockchain node workloads declaratively. Users declare the desired state via Custom Resources and the operator continuously reconciles the actual cluster state to match — creating and maintaining StatefulSets, ConfigMaps, Services, and PodMonitors for each blockchain node, handling upgrades, rollbacks, and snapshot bootstrapping automatically.
6+
7+
## Custom Resource Definitions
8+
9+
### BlockchainNode
10+
11+
The primary CRD. Each instance describes one blockchain node: the chain type (via `.spec.chain`), the container image or version tracking policy, resource requirements, storage, networking, and health thresholds. The operator owns all child resources created from a BlockchainNode.
12+
13+
### ChainVersionCatalog
14+
15+
A cluster-scoped CRD that holds discovered image tags per chain. The operator polls configured container registries on a schedule and writes the latest resolved tag back into this resource. BlockchainNodeReconciler reads from it when `VersionPolicy` tracking is enabled on a node.
16+
17+
## Component Diagram
18+
19+
```mermaid
20+
graph TD
21+
User([User / GitOps]) -->|apply BlockchainNode CR| K8sAPI[Kubernetes API]
22+
23+
K8sAPI --> BNR[BlockchainNodeReconciler]
24+
25+
BNR --> CM[ConfigMap\nchain config / genesis]
26+
BNR --> STS[StatefulSet\nnode + init containers]
27+
BNR --> SVC[Service\nRPC / P2P ports]
28+
BNR --> PM[PodMonitor\nPrometheus scrape]
29+
30+
STS --> IC[Snapshot Init Container\nMinIO bootstrap]
31+
32+
BNR --> AR[Adapter Registry\n102 chain adapters]
33+
AR --> Adapter[Chain Adapter\nDefaultResources / VersionPolicy\nports / config template]
34+
35+
BNR --> AU[Auto-Upgrade Reconciler]
36+
AU --> CVC[ChainVersionCatalog]
37+
CVC --> RC[Registry Clients]
38+
RC --> DH[DockerHub v2 API]
39+
RC --> GH[GHCR OCI v2]
40+
RC --> OCI[OCI v2\nGAR / ECR Public]
41+
42+
AU -->|rolling restart| STS
43+
AU -->|CrashLoopBackOff ≥3| RB[Rollback to previous tag]
44+
45+
K8sAPI --> WH[Admission Webhooks\nvalidating + defaulting]
46+
```
47+
48+
## Reconciliation Flow
49+
50+
1. **Fetch CR** — load the `BlockchainNode` object; requeue on not-found after a short delay.
51+
2. **Resolve adapter** — look up the chain adapter in the registry by `spec.chain`; return a permanent error for unknown chains.
52+
3. **Handle deletion / finalizer** — if DeletionTimestamp is set, run cleanup (remove PodMonitor, external resources) and strip the finalizer; otherwise ensure the finalizer is present.
53+
4. **ensureConfigMap** — render the chain-specific config template via the adapter and create-or-update the ConfigMap.
54+
5. **ensureStatefulSet** — merge adapter defaults with user overrides (resources, storage, env, ports); create-or-update the StatefulSet. If a snapshot URL is configured, inject the init container.
55+
6. **ensureService** — reconcile the headless Service and, if enabled, a separate LoadBalancer/NodePort Service for RPC exposure.
56+
7. **ensurePodMonitor** — create or update the Prometheus Operator `PodMonitor` using the metrics port declared by the adapter.
57+
8. **reconcileUpgrade** — if `VersionPolicy` is active, compare the running image tag against the latest tag in `ChainVersionCatalog`; trigger a rolling restart when a newer tag is found.
58+
9. **refreshStatus** — update `.status` fields: current image, sync phase, block height, peer count, health condition, last upgrade time.
59+
60+
## Health Monitoring
61+
62+
The operator watches pod metrics and logs to derive node health:
63+
64+
- **Block-lag threshold** — if the node's block height lags behind peers or a reference RPC endpoint by more than `spec.health.maxBlockLag`, the condition is set to `Degraded`.
65+
- **Sync stall detection** — if the block height does not advance for longer than `spec.health.syncStallTimeout`, the node is considered stalled.
66+
- **Peer count** — if connected peers fall below `spec.health.minPeers`, a warning condition is emitted.
67+
- **Auto-restart on degraded timeout** — if the node remains in `Degraded` for longer than `spec.health.autoRestartTimeout`, the operator deletes the pod to trigger a fresh start. The threshold is configurable per node to avoid restart loops on slow-syncing chains.
68+
69+
Full details: [health-monitoring.md](health-monitoring.md).
70+
71+
## ChainVersionCatalog and Registry Polling
72+
73+
Each chain adapter declares a `VersionPolicy` that specifies:
74+
75+
- `registry` — which registry client to use
76+
- `image` — the repository path
77+
- `tagPattern` — a regex that filters valid release tags (e.g. `^v\d+\.\d+\.\d+$`)
78+
79+
The catalog controller polls each registry on a configurable interval (default 1 h), collects all matching tags, applies semver normalization and sorting, and writes the latest resolved tag into the `ChainVersionCatalog` status. The `BlockchainNodeReconciler` reads this value during `reconcileUpgrade`.
80+
81+
Adapters where only a `:latest` tag is published (Aptos, Aurora, HyperLiquid, MegaETH, Monad) have `VersionPolicy` disabled — auto-tracking is not possible for these chains.
82+
83+
## Auto-Upgrade State Machine
84+
85+
```
86+
Running ──[newer tag available]──> Upgrading
87+
|
88+
└──[rollout healthy]──> Running
89+
|
90+
└──[CrashLoopBackOff ≥3 restarts]──> Rolling back
91+
|
92+
└──> Running (previous tag restored)
93+
```
94+
95+
The operator records the previous image tag in an annotation on the StatefulSet before each upgrade. On rollback, that annotation is read and the image is reverted. The upgrade history (timestamp, from-tag, to-tag, outcome) is appended to `.status.upgradeHistory`.

0 commit comments

Comments
 (0)