Skip to content

[WIP] HA failover - #2814

Closed
alpe wants to merge 20 commits into
mainfrom
alex/adr023_ha-failover_raft5
Closed

[WIP] HA failover#2814
alpe wants to merge 20 commits into
mainfrom
alex/adr023_ha-failover_raft5

Conversation

@alpe

@alpealpe commented Nov 4, 2025

Copy link
Copy Markdown
Contributor

🚧 WIP - early version

Integration tests are failing and some unit tests are missing

Overview

@github-actions

github-actionsBot commented Nov 4, 2025

Copy link
Copy Markdown
Contributor

The latest Buf updates on your PR. Results from workflow CI and Release / buf-check (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed⏩ skipped✅ passed✅ passedNov 10, 2025, 1:27 PM

@codecov

codecovBot commented Nov 7, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 34.65517% with 758 lines in your changes missing coverage. Please review.
✅ Project coverage is 59.88%. Comparing base (3a9c9ba) to head (c7952fe).
⚠️ Report is 9 commits behind head on main.

Files with missing linesPatch %Lines
pkg/raft/node.go0.00%245 Missing ⚠️
pkg/raft/node_mock.go50.94%60 Missing and 18 partials ⚠️
block/internal/syncing/raft_retriever.go0.00%74 Missing ⚠️
pkg/rpc/server/http.go1.66%58 Missing and 1 partial ⚠️
node/full.go28.20%49 Missing and 7 partials ⚠️
node/failover.go66.66%38 Missing and 13 partials ⚠️
pkg/rpc/client/raft_cluster.go0.00%46 Missing ⚠️
block/internal/executing/executor.go4.76%35 Missing and 5 partials ⚠️
block/internal/syncing/syncer.go29.41%34 Missing and 2 partials ⚠️
pkg/raft/election.go75.86%15 Missing and 6 partials ⚠️
... and 10 more
Additional details and impacted files
@@ Coverage Diff @@## main #2814 +/- ##
==========================================
- Coverage 64.79% 59.88% -4.92% 
==========================================
Files 81 89 +8 Lines 7224 10079 +2855 ==========================================
+ Hits 4681 6036 +1355 - Misses 2003 3470 +1467 - Partials 540 573 +33 
FlagCoverage Δ
combined59.88% <34.65%> (-4.92%)⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@evstackevstack deleted a comment from claudeBotNov 7, 2025
@alpe

alpe commented Nov 7, 2025

Copy link
Copy Markdown
ContributorAuthor

@claude review this PR

@claude

claudeBot commented Nov 7, 2025

Copy link
Copy Markdown
Contributor

Claude finished @alpe's task —— View job


Code Review: HA Failover Implementation

Comprehensive code review completed for the HA failover implementation based on Raft consensus.

Executive Summary

This PR implements high-availability failover using HashiCorp's Raft consensus library. The implementation adds Raft-based leader election, state replication, and automatic failover for sequencer nodes. While the architecture is sound, there are several critical issues that need attention, particularly around error handling, testing coverage, security, and performance.

Current Status: Integration tests failing, 630 lines missing test coverage (36% patch coverage)


1. Architecture & Code Quality

✅ Strengths

  1. Clean separation of concerns - Raft logic isolated in pkg/raft/, integration in node/failover.go
  2. Interface-based design - Good use of Runnable interface for leader/follower modes
  3. Factory pattern for mode switching - Clean approach to dynamic role changes
  4. Modular configuration - Raft config properly separated in pkg/config/config.go

⚠️ Issues & Recommendations

Critical: State Serialization

pkg/raft/node.go:238, pkg/raft/types.go:5

data, err:=json.Marshal(state) // todo:use protobuf

Issue: Using JSON for Raft state replication introduces multiple problems:

  • No schema evolution support
  • Larger payload sizes (impacts network & disk)
  • Slower marshaling/unmarshaling
  • No forward/backward compatibility guarantees

Recommendation: Implement protobuf serialization ASAP. This is marked as TODO but is critical for production use. Add this to types/pb/evnode/v1/ and update RaftBlockState to use proto.


Critical: State Validation Logic

pkg/raft/types.go:16-24

func (sRaftBlockState) assertValid(nextRaftBlockState) error {
ifs.Height>next.Height {
returnfmt.Errorf("invalid height: %d > %d", s.Height, next.Height)
}
ifs.Timestamp>next.Timestamp {
returnfmt.Errorf("invalid timestamp: %d > %d", s.Timestamp, next.Timestamp)
}
returnnil
}

Issues:

  1. Incomplete validation: Comment says "does not ensure that no gaps exist or chain continuity" but gaps could cause catastrophic state divergence
  2. Missing hash chain validation: Should verify next properly references s via hash
  3. No bounds checking: Heights should increment by exactly 1 (or document why gaps are acceptable)
  4. Timestamp validation too weak: Should enforce minimum progression, not just monotonicity

Recommendation:

func (sRaftBlockState) assertValid(nextRaftBlockState) error {
// Height must increment by exactly 1 (no gaps)ifnext.Height!=s.Height+1 {
returnfmt.Errorf("height gap detected: current=%d next=%d", s.Height, next.Height)
}
// Verify hash chain continuityif!bytes.Equal(next.PrevHash, s.Hash) {
returnfmt.Errorf("hash chain broken at height %d", next.Height)
}
// Enforce meaningful timestamp progressionifnext.Timestamp<=s.Timestamp {
returnfmt.Errorf("timestamp must increase: %d -> %d", s.Timestamp, next.Timestamp)
}
// Add max timestamp drift check to prevent far-future timestampsreturnnil
}

Major: Raft Node Initialization Error Handling

pkg/raft/node.go:56-113

Issues:

  1. Resource leaks on error: If NewRaft fails, the transport and stores are not closed
  2. Directory permissions too permissive: 0755 for raft data directory
  3. Error propagation loses context: Some errors don't wrap original error

Recommendation:

funcNewNode(cfg*Config, clusterClientclusterClient, logger zerolog.Logger) (*Node, error) {
iferr:=os.MkdirAll(cfg.RaftDir, 0700); err!=nil { // More restrictivereturnnil, fmt.Errorf("create raft dir: %w", err)
}
// ... setup stores and transport ...// Use deferred cleanup on errorvarcleanup []func()
deferfunc() {
iferr!=nil {
for_, fn:=rangecleanup {
fn()
}
}
}()
cleanup=append(cleanup, func() { logStore.Close() })
cleanup=append(cleanup, func() { stableStore.Close() })
cleanup=append(cleanup, func() { transport.Close() })
// ... rest of initialization
}

Major: Race Condition in FSM Apply

pkg/raft/node.go:316-322

iff.applyCh!=nil {
select {
casef.applyCh<-RaftApplyMsg{Index: log.Index, State: &state}:
default:
f.logger.Warn().Msg("apply channel full, dropping message")
}
}

Issues:

  1. Silent message drops: Dropping Raft apply messages can cause state divergence between Raft FSM and application state
  2. No backpressure mechanism: Should block or return error instead of dropping
  3. Channel capacity not documented: What's the expected capacity?

Recommendation:

// Either block (preferred for correctness):iff.applyCh!=nil {
f.applyCh<-RaftApplyMsg{Index: log.Index, State: &state}
}
// OR return error to Raft (will cause node to fail):iff.applyCh!=nil {
select {
casef.applyCh<-msg:
case<-time.After(applyTimeout):
returnfmt.Errorf("apply channel blocked for %s", applyTimeout)
}
}

Major: Election Logic - Sync Check Race

pkg/raft/election.go:90-101

ifbecameLeader&&!isCurrentlyLeader {
ifisStarted {
time.Sleep(d.node.Config().SendTimeout) // Wait for in-flight messagesif!runnable.IsSynced(d.node.GetState()) {
iferr:=d.node.leadershipTransfer(); err!=nil&&!errors.Is(err, raft.ErrNotLeader) {
returnerr
}
continue
}
// ...
}
}

Issues:

  1. Hard-coded sleep is fragile: SendTimeout may not be sufficient for all in-flight messages
  2. TOCTOU race: State could change between IsSynced check and proceeding as leader
  3. Infinite transfer loop: If all nodes are unsynced, this could loop forever
  4. No transfer timeout: leadershipTransfer() might block indefinitely

Recommendation:

  • Add max retry count for leadership transfer
  • Use exponential backoff instead of fixed sleep
  • Add timeout to leadership transfer
  • Consider using Raft log index to verify sync instead of height comparison

2. Error Handling & Resilience

⚠️ Critical Issues

Shutdown Error Swallowing

node/failover.go:167-221

The Run method properly collects multiple errors but:

  1. HTTP server errors ignored on graceful shutdown - Only ErrServerClosed is expected, but other errors during shutdown should be logged
  2. P2P client close error ignored - Line 196: defer f.p2pClient.Close() // nolint: errcheck should collect the error
  3. Inconsistent error accumulation - Some places use errors.Join, others silently ignore

Recommendation: Consistently collect all errors and include them in the return value.


RPC Cluster Client Error Handling

pkg/rpc/client/raft_cluster.go:39-68

func (rRaftClusterClient) broadcast(ctx context.Context, pathstring, objany) error {
varpeerErrs []errorfor_, peer:=ranger.peers {
// ... attempt to contact peer ...ifresp.StatusCode!=http.StatusOK {
peerErrs=append(peerErrs, fmt.Errorf("unexpected status: %s", resp.Status))
continue
}
}
iflen(peerErrs) ==len(r.peers) {
returnerrors.Join(peerErrs...)
}
returnnil// SUCCESS if ANY peer succeeds
}

Issues:

  1. Dangerous partial success semantics: Returns nil if ANY peer succeeds, even if most fail. This can cause split-brain scenarios.
  2. No quorum enforcement: Should require majority (or all) peers to succeed for Raft operations
  3. Timeout is global: 3-second timeout applies to all peers sequentially, not per-peer
  4. No retry logic: Transient network errors cause permanent failures

Recommendation:

// Broadcast to all peers in parallel with per-peer timeoutfunc (rRaftClusterClient) broadcast(ctx context.Context, pathstring, objany) error {
typeresultstruct {
peerstringerrerror
}
results:=make(chanresult, len(r.peers))
ctx, cancel:=context.WithTimeout(ctx, 3*time.Second)
defercancel()
for_, peer:=ranger.peers {
gofunc(pstring) {
results<-result{peer: p, err: r.sendToPeer(ctx, p, path, obj)}
}(peer)
}
varsuccessCount, failCountintvarerrs []errorfori:=0; i<len(r.peers); i++ {
res:=<-resultsifres.err!=nil {
failCount++errs=append(errs, fmt.Errorf("%s: %w", res.peer, res.err))
} else {
successCount++
}
}
// Require majority for Raft safetyifsuccessCount<len(r.peers)/2+1 {
returnfmt.Errorf("failed to reach quorum (%d/%d): %w", successCount, len(r.peers), errors.Join(errs...))
}
returnnil
}

3. Security Concerns

🔒 Critical Security Issues

Raft RPC Endpoint Authentication Missing

pkg/rpc/server/http.go:130-194

The /raft/join and /raft/remove endpoints have NO authentication:

mux.HandleFunc("/raft/join", func(w http.ResponseWriter, r*http.Request) {
// ... anyone can call this and join the clusteriferr:=raftNode.AddPeer(rsp.NodeID, rsp.Address); err!=nil {
http.Error(w, "failed to join peer", http.StatusInternalServerError)
}
})

Impact: Any attacker with network access can:

  1. Join malicious nodes to the Raft cluster
  2. Remove legitimate nodes from the cluster
  3. Cause denial of service
  4. Potentially achieve consensus takeover

Recommendation:

  • Add TLS mutual authentication for Raft endpoints
  • Require pre-shared secret or JWT token for cluster operations
  • Implement IP allowlisting for Raft management endpoints
  • Add rate limiting to prevent abuse

Raft Directory Permissions

pkg/raft/node.go:57

iferr:=os.MkdirAll(cfg.RaftDir, 0755); err!=nil {

Issue: Directory is world-readable (0755), exposing Raft state including potentially sensitive data.

Recommendation: Use 0700 to restrict access to owner only.


Snapshot Store Writes to stderr

pkg/raft/node.go:84,94

snapshotStore, err:=raft.NewFileSnapshotStore(cfg.RaftDir, int(cfg.SnapCount), os.Stderr)
transport, err:=raft.NewTCPTransport(cfg.RaftAddr, addr, 3, 10*time.Second, os.Stderr)

Issue: Error output goes to stderr which may be logged. Could leak sensitive state information.

Recommendation: Use io.Discard or a dedicated logger with filtering.


4. Performance & Resource Management

⚠️ Performance Concerns

Blocking State Operations

block/internal/syncing/raft_retriever.go:82-96

The raftApplyLoop processes messages sequentially:

for {
select {
case<-ctx.Done():
returncasemsg:=<-applyCh:
iferr:=r.consumeRaftBlock(ctx, msg.State); err!=nil {
r.logger.Error().Err(err).Uint64("height", msg.State.Height).Msg("failed to apply raft block")
}
}
}

Issues:

  1. Single-threaded processing: One slow block blocks all subsequent blocks
  2. No timeout on consumeRaftBlock: Could block indefinitely
  3. Errors are only logged: Failed blocks should halt the node or trigger catchup

Recommendation:

  • Add timeout to block processing
  • Consider parallel processing with ordering guarantees
  • Implement circuit breaker for persistent failures

Memory Allocation in Hot Path

pkg/raft/node.go:238-248

func (n*Node) Broadcast(_ context.Context, state*RaftBlockState) error {
data, err:=json.Marshal(state) // Allocation every blockiferr!=nil {
returnfmt.Errorf("marshal block state: %w", err)
}
future:=n.raft.Apply(data, n.config.SendTimeout)
// ...
}

Issue: JSON marshaling allocates on every block broadcast. For high-throughput chains, this creates GC pressure.

Recommendation:

  • Use sync.Pool for marshal buffers
  • Pre-allocate buffer based on average block size
  • Switch to protobuf which is more efficient

No Backpressure on Block Production

block/internal/executing/executor.go

When Raft becomes a bottleneck, there's no backpressure mechanism to slow block production. This can lead to:

  • Memory exhaustion from buffered blocks
  • Raft log growth
  • Increased failover time

Recommendation: Monitor Raft lag and implement backpressure when lag exceeds threshold.


5. Testing & Reliability

❌ Major Testing Gaps

Coverage Analysis (from codecov report):

FileCoverageMissing LinesPriority
pkg/raft/node.go0.00%202🔴 CRITICAL
block/internal/syncing/raft_retriever.go0.00%60🔴 CRITICAL
pkg/rpc/client/raft_cluster.go0.00%41🔴 CRITICAL
pkg/rpc/server/http.go2.17%45🔴 HIGH
node/full.go30.00%49🟡 MEDIUM
node/failover.go72.00%35🟢 ACCEPTABLE

Critical Missing Tests:

  1. No unit tests for pkg/raft/node.go - Core Raft wrapper has 0% coverage

    • Bootstrap scenarios
    • Join cluster scenarios
    • Peer add/remove
    • Snapshot/restore
    • Error paths
  2. No integration tests for Raft retriever - raft_retriever.go completely untested

    • Block application from Raft
    • Error handling
    • Stop/restart scenarios
  3. Missing RPC client tests - raft_cluster.go has no tests

    • Partial failure scenarios
    • Network timeout handling
    • Retry logic

Recommendations:

  1. Add unit tests for pkg/raft/node.go:
funcTestNode_Bootstrap(t*testing.T)
funcTestNode_JoinCluster(t*testing.T)
funcTestNode_AddPeer_Idempotent(t*testing.T)
funcTestNode_RemovePeer(t*testing.T)
funcTestNode_LeaderElection(t*testing.T)
funcTestNode_StateReplication(t*testing.T)
funcTestNode_SnapshotRestore(t*testing.T)
  1. Add integration tests for failover scenarios:

    • Leader crash and recovery
    • Network partition (split brain)
    • Cascading failures
    • Rolling restarts
  2. Add chaos testing: Use tools like Jepsen or custom fault injection to test:

    • Random node crashes
    • Network delays/partitions
    • Clock skew
    • Disk failures

E2E Test Issues

test/e2e/failover_e2e_test.go:42-244

The E2E test TestLeaseFailoverE2E is good but has gaps:

  1. Only tests single leader failure - Should test multiple failures
  2. No partition recovery test - What happens with network splits?
  3. No concurrent transaction test - During failover, do transactions get lost?
  4. Assumes clean shutdown - Should test kill -9 scenarios
  5. Fixed timing assumptions - Uses hardcoded sleeps that may be brittle in CI

Recommendations:

  • Add test for follower failure (should be no-op)
  • Add test for majority failure (cluster should halt)
  • Add test for brain split and recovery
  • Use polling with timeout instead of fixed sleeps

6. Configuration & Documentation

⚠️ Configuration Issues

Missing Raft Configuration Validation

pkg/config/config.go:181

The RaftConfig struct has no validation:

typeRaftConfigstruct {
EnableboolNodeIDstringRaftAddrstringRaftDirstringBootstrapboolPeersstringSnapCountuint64SendTimeout time.DurationHeartbeatTimeout time.Duration }

Issues:

  • No validation that NodeID is unique and non-empty
  • No validation that RaftAddr is valid TCP address
  • No validation that Peers format is correct (expects nodeID@addr)
  • No defaults for critical timeouts
  • Bootstrap can be true on multiple nodes (should prevent this)

Recommendation: Add Validate() method:

func (r*RaftConfig) Validate() error {
ifr.Enable {
ifr.NodeID=="" {
returnerrors.New("raft.node_id is required when raft is enabled")
}
ifr.RaftAddr=="" {
returnerrors.New("raft.raft_addr is required")
}
if_, _, err:=net.SplitHostPort(r.RaftAddr); err!=nil {
returnfmt.Errorf("invalid raft.raft_addr: %w", err)
}
// Validate peers formatifr.Peers!="" {
for_, peer:=rangestrings.Split(r.Peers, ",") {
if_, err:=splitPeerAddr(peer); err!=nil {
returnfmt.Errorf("invalid peer format '%s': %w", peer, err)
}
}
}
// Validate timeoutsifr.SendTimeout==0 {
r.SendTimeout=5*time.Second
}
ifr.HeartbeatTimeout==0 {
r.HeartbeatTimeout=1*time.Second
}
}
returnnil
}

Documentation Gaps:

  1. No ADR (Architecture Decision Record) - This is a major architectural change and should have an ADR explaining:

    • Why Raft was chosen over alternatives
    • Tradeoffs and limitations
    • Operational considerations
  2. No runbook - Operations team needs documentation for:

    • How to add/remove nodes from cluster
    • How to recover from failures
    • How to debug Raft issues
    • Monitoring and alerting recommendations
  3. No API documentation - The /raft/join and /raft/remove endpoints need OpenAPI/Swagger docs

  4. Configuration examples missing - Need examples for:

    • 3-node cluster setup
    • 5-node cluster setup
    • Single-node development mode

7. Concurrency & Race Conditions

⚠️ Potential Race Conditions

Leader Election State Management

pkg/raft/election.go:53-148

The Run() method has complex state machine with potential races:

varisStarted, isCurrentlyLeaderboolvarworkerCancel context.CancelFunc=func() {}

Issues:

  1. Multiple goroutines access shared state: isStarted, isCurrentlyLeader not protected by mutex
  2. Context cancellation races: workerCancel could be called concurrently
  3. No happens-before guarantee: Between checking isStarted and starting worker

Recommendation: Use proper synchronization or state machine library.


Failover State Races

node/failover.go:167-221

Multiple components started concurrently without clear ordering:

wg.Go(func() error {
// RPC server
})
// Then separately:f.p2pClient.Start(ctx)
f.headerSyncService.Start(ctx)
f.dataSyncService.Start(ctx)

Issue: P2P client must be ready before sync services start, but there's no coordination.

Recommendation: Add explicit startup ordering with health checks between stages.


8. Additional Observations

Code Smells

  1. Magic numbers:

    • pkg/raft/election.go:81 - 300 * time.Millisecond ticker should be configured
    • node/failover.go:148-150 - Hard-coded timeouts (10s, 120s)
  2. Copy-paste code:

    • newAggregatorMode and newSyncMode have significant duplication
    • Refactor common logic into shared helper
  3. Inconsistent naming:

    • raftNode vs RaftNode vs raft_node
    • leaderElection vs DynamicLeaderElection
  4. Commented-out code: None found (good!)


Summary of Recommendations by Priority

🔴 CRITICAL (Must Fix Before Production)

  1. Add authentication to Raft RPC endpoints (/raft/join, /raft/remove)
  2. Fix state validation in RaftBlockState.assertValid() - enforce height increments and hash chain
  3. Fix FSM apply channel drops - don't drop messages silently
  4. Add unit tests for pkg/raft/node.go (0% coverage)
  5. Fix RaftClusterClient partial success semantics - require quorum
  6. Implement protobuf serialization for Raft state

🟡 HIGH (Should Fix Soon)

  1. Fix resource leaks in NewNode error paths
  2. Add timeout and retry to leadership transfer
  3. Add integration tests for failover scenarios
  4. Add configuration validation for RaftConfig
  5. Fix directory permissions (0755 → 0700)
  6. Add backpressure mechanism for block production

🟢 MEDIUM (Technical Debt)

  1. Reduce code duplication between aggregator/sync mode setup
  2. Add ADR document explaining Raft design choices
  3. Add operational runbook
  4. Use sync.Pool for marshal buffers
  5. Add chaos/fault injection testing
  6. Improve E2E test coverage

⚪ LOW (Nice to Have)

  1. Add Prometheus metrics for Raft operations
  2. Add distributed tracing support
  3. Refactor magic numbers to constants
  4. Add OpenAPI docs for Raft endpoints

Positive Aspects ✨

  1. Well-structured code - Clean separation of concerns
  2. Good use of Go idioms - Contexts, error wrapping, interfaces
  3. Comprehensive E2E test - The failover E2E test is well-designed
  4. Proper error collection - Multi-error handling in shutdown paths
  5. Configuration flexibility - Good balance of configurability

Conclusion

This PR implements a solid foundation for HA failover using Raft, but requires significant work before production readiness. The main concerns are:

  1. Security - Unprotected Raft endpoints are a critical vulnerability
  2. Testing - 630 lines without test coverage including core Raft functionality
  3. Error Handling - Several cases where errors are dropped or mishandled
  4. State Validation - Insufficient validation could allow state divergence

Recommendation: Address the CRITICAL and HIGH priority issues before merging. Consider breaking this into smaller PRs to improve reviewability and reduce risk.


@alpealpe mentioned this pull request Nov 10, 2025
@alpe
alpeforce-pushed the alex/adr023_ha-failover_raft5 branch from 9a28c30 to d771e5eCompareNovember 10, 2025 13:27
@github-actions

github-actionsBot commented Nov 10, 2025

Copy link
Copy Markdown
Contributor

The latest Buf updates on your PR. Results from workflow CI / buf-check (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed⏩ skipped✅ passed✅ passedNov 12, 2025, 10:33 AM

@julienrbrtjulienrbrt mentioned this pull request Nov 11, 2025
11 tasks
alpeand others added 7 commits November 11, 2025 12:36
This PR adds a prominent warning in the README to inform users not to
use tags or releases before v1.0.0, as they are not considered stable
and may contain breaking changes.
Fixes#2823
Generated with [Claude Code](https://claude.ai/code)
---------
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Marko <tac0turtle@users.noreply.github.com>
## 🎯 Summary
This PR refactors the Docker build and release workflows to improve
maintainability, security, and separation of concerns. The changes
reorganize Dockerfiles into a consistent `apps/` directory structure and
split monolithic workflows into focused, reusable components.
---
## 📋 Changes
### Workflow Refactoring
- __Separated workflows__ for better modularity:
- `ci.yml` - Main CI orchestrator with image tag determination
- `docker.yml` - Reusable Docker build workflow
- `docker-tests.yml` - Docker image testing workflow
- `release.yml` - Tag-based release workflow for app publishing
- __Removed__ `ci_release.yml` (deprecated/consolidated into new
structure)
- __Enhanced__ `test.yml` - Simplified by removing Docker build logic
(134 lines removed)
### Docker Organization
- __Moved Dockerfiles__ to standardized locations:
- `Dockerfile` → `apps/testapp/Dockerfile`
- `da/cmd/local-da/Dockerfile` → `apps/local-da/Dockerfile`
- Updated `apps/grpc/single/Dockerfile` and `docker-compose.yml`
- __Removed__ `Dockerfile.da` (consolidated)
### Documentation
- __Added__ `.github/RELEASE_QUICK_START.md` (231 lines)
- Quick reference guide for Docker and Go module releases
## 🔄 Workflow Architecture
```javascript
┌─────────────┐
│ ci.yml │ (Main orchestrator)
└──────┬──────┘
│
├──► lint.yml
├──► docker.yml ──► Build images
├──► test.yml
├──► docker-tests.yml ──► Test images
└──► proto.yml
┌─────────────────┐
│ release.yml │ (Tag-based releases)
└─────────────────┘
│
└──► Build & push versioned images to GHCR
```
---
## 📝 Release Process
### Docker App Releases (Automated)
```bash
git tag evm/single/v0.2.0
git push origin evm/single/v0.2.0
# Workflow automatically builds and publishes to GHCR
```
### Tag Format
`{app-path}/v{major}.{minor}.{patch}`
Examples:
- `evm/single/v0.2.0` → `ghcr.io/evstack/ev-node-evm-single:v0.2.0`
- `testapp/v1.0.0` → `ghcr.io/evstack/ev-node-testapp:v1.0.0`
See [RELEASE_QUICK_START.md](.github/RELEASE_QUICK_START.md) for
complete guide.
---------
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
<!--
Please read and fill out this form before submitting your PR.
Please make sure you have reviewed our contributors guide before
submitting your
first PR.
NOTE: PR titles should follow semantic commits:
https://www.conventionalcommits.org/en/v1.0.0/
-->
## Overview
<!-- Please provide an explanation of the PR, including the appropriate
context,
background, goal, and rationale. If there is an issue with this
information,
please provide a tl;dr and link the issue. Ex: Closes #<issue number>
-->
…2829)
<!--
Please read and fill out this form before submitting your PR.
Please make sure you have reviewed our contributors guide before
submitting your
first PR.
NOTE: PR titles should follow semantic commits:
https://www.conventionalcommits.org/en/v1.0.0/
-->
## Overview
<!-- Please provide an explanation of the PR, including the appropriate
context,
background, goal, and rationale. If there is an issue with this
information,
please provide a tl;dr and link the issue. Ex: Closes #<issue number>
-->
Inspired by #2781 and replace
all.
This change replaces occurrences of interface{} with the predeclared
identifier any, introduced in Go 1.18 as an alias for interface{}.
As noted in the [Go 1.18 Release
Notes](https://go.dev/doc/go1.18#language):
This improves readability and aligns the codebase with modern Go
conventions.
Signed-off-by: promalert <promalert@outlook.com>
Bumps [actions/github-script](https://github.com/actions/github-script)
from 7 to 8.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/github-script/releases">actions/github-script's
releases</a>.</em></p>
<blockquote>
<h2>v8.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Update Node.js version support to 24.x by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/637">actions/github-script#637</a></li>
<li>README for updating actions/github-script from v7 to v8 by <a
href="https://github.com/sneha-krip"><code>@​sneha-krip</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/653">actions/github-script#653</a></li>
</ul>
<h2>⚠️ Minimum Compatible Runner Version</h2>
<p><strong>v2.327.1</strong><br />
<a
href="https://github.com/actions/runner/releases/tag/v2.327.1">Release
Notes</a></p>
<p>Make sure your runner is updated to this version or newer to use this
release.</p>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/github-script/pull/637">actions/github-script#637</a></li>
<li><a
href="https://github.com/sneha-krip"><code>@​sneha-krip</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/github-script/pull/653">actions/github-script#653</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/github-script/compare/v7.1.0...v8.0.0">https://github.com/actions/github-script/compare/v7.1.0...v8.0.0</a></p>
<h2>v7.1.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Upgrade husky to v9 by <a
href="https://github.com/benelan"><code>@​benelan</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/482">actions/github-script#482</a></li>
<li>Add workflow file for publishing releases to immutable action
package by <a
href="https://github.com/Jcambass"><code>@​Jcambass</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/485">actions/github-script#485</a></li>
<li>Upgrade IA Publish by <a
href="https://github.com/Jcambass"><code>@​Jcambass</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/486">actions/github-script#486</a></li>
<li>Fix workflow status badges by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/497">actions/github-script#497</a></li>
<li>Update usage of <code>actions/upload-artifact</code> by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/512">actions/github-script#512</a></li>
<li>Clear up package name confusion by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/514">actions/github-script#514</a></li>
<li>Update dependencies with <code>npm audit fix</code> by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/515">actions/github-script#515</a></li>
<li>Specify that the used script is JavaScript by <a
href="https://github.com/timotk"><code>@​timotk</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/478">actions/github-script#478</a></li>
<li>chore: Add Dependabot for NPM and Actions by <a
href="https://github.com/nschonni"><code>@​nschonni</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/472">actions/github-script#472</a></li>
<li>Define <code>permissions</code> in workflows and update actions by
<a href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in
<a
href="https://redirect.github.com/actions/github-script/pull/531">actions/github-script#531</a></li>
<li>chore: Add Dependabot for .github/actions/install-dependencies by <a
href="https://github.com/nschonni"><code>@​nschonni</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/532">actions/github-script#532</a></li>
<li>chore: Remove .vscode settings by <a
href="https://github.com/nschonni"><code>@​nschonni</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/533">actions/github-script#533</a></li>
<li>ci: Use github/setup-licensed by <a
href="https://github.com/nschonni"><code>@​nschonni</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/473">actions/github-script#473</a></li>
<li>make octokit instance available as octokit on top of github, to make
it easier to seamlessly copy examples from GitHub rest api or octokit
documentations by <a
href="https://github.com/iamstarkov"><code>@​iamstarkov</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/508">actions/github-script#508</a></li>
<li>Remove <code>octokit</code> README updates for v7 by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/557">actions/github-script#557</a></li>
<li>docs: add &quot;exec&quot; usage examples by <a
href="https://github.com/neilime"><code>@​neilime</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/546">actions/github-script#546</a></li>
<li>Bump ruby/setup-ruby from 1.213.0 to 1.222.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/github-script/pull/563">actions/github-script#563</a></li>
<li>Bump ruby/setup-ruby from 1.222.0 to 1.229.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/github-script/pull/575">actions/github-script#575</a></li>
<li>Clearly document passing inputs to the <code>script</code> by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/603">actions/github-script#603</a></li>
<li>Update README.md by <a
href="https://github.com/nebuk89"><code>@​nebuk89</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/610">actions/github-script#610</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/benelan"><code>@​benelan</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/github-script/pull/482">actions/github-script#482</a></li>
<li><a href="https://github.com/Jcambass"><code>@​Jcambass</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/github-script/pull/485">actions/github-script#485</a></li>
<li><a href="https://github.com/timotk"><code>@​timotk</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/github-script/pull/478">actions/github-script#478</a></li>
<li><a
href="https://github.com/iamstarkov"><code>@​iamstarkov</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/github-script/pull/508">actions/github-script#508</a></li>
<li><a href="https://github.com/neilime"><code>@​neilime</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/github-script/pull/546">actions/github-script#546</a></li>
<li><a href="https://github.com/nebuk89"><code>@​nebuk89</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/github-script/pull/610">actions/github-script#610</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/github-script/compare/v7...v7.1.0">https://github.com/actions/github-script/compare/v7...v7.1.0</a></p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/actions/github-script/commit/ed597411d8f924073f98dfc5c65a23a2325f34cd"><code>ed59741</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/github-script/issues/653">#653</a>
from actions/sneha-krip/readme-for-v8</li>
<li><a
href="https://github.com/actions/github-script/commit/2dc352e4baefd91bec0d06f6ae2f1045d1687ca3"><code>2dc352e</code></a>
Bold minimum Actions Runner version in README</li>
<li><a
href="https://github.com/actions/github-script/commit/01e118c8d0d22115597e46514b5794e7bc3d56f1"><code>01e118c</code></a>
Update README for Node 24 runtime requirements</li>
<li><a
href="https://github.com/actions/github-script/commit/8b222ac82eda86dcad7795c9d49b839f7bf5b18b"><code>8b222ac</code></a>
Apply suggestion from <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a></li>
<li><a
href="https://github.com/actions/github-script/commit/adc0eeac992408a7b276994ca87edde1c8ce4d25"><code>adc0eea</code></a>
README for updating actions/github-script from v7 to v8</li>
<li><a
href="https://github.com/actions/github-script/commit/20fe497b3fe0c7be8aae5c9df711ac716dc9c425"><code>20fe497</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/github-script/issues/637">#637</a>
from actions/node24</li>
<li><a
href="https://github.com/actions/github-script/commit/e7b7f222b11a03e8b695c4c7afba89a02ea20164"><code>e7b7f22</code></a>
update licenses</li>
<li><a
href="https://github.com/actions/github-script/commit/2c81ba05f308415d095291e6eeffe983d822345b"><code>2c81ba0</code></a>
Update Node.js version support to 24.x</li>
<li>See full diff in <a
href="https://github.com/actions/github-script/compare/v7...v8">compare
view</a></li>
</ul>
</details>
<br />
[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/github-script&package-manager=github_actions&previous-version=7&new-version=8)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…2826)
Bumps
[golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action)
from 8.0.0 to 9.0.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/golangci/golangci-lint-action/releases">golangci/golangci-lint-action's
releases</a>.</em></p>
<blockquote>
<h2>v9.0.0</h2>
<p>In the scope of this release, we change Nodejs runtime from node20 to
node24 (<a
href="https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/">https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/</a>).</p>
<h2>What's Changed</h2>
<h3>Changes</h3>
<ul>
<li>feat: add install-only option by <a
href="https://github.com/ldez"><code>@​ldez</code></a> in <a
href="https://redirect.github.com/golangci/golangci-lint-action/pull/1305">golangci/golangci-lint-action#1305</a></li>
<li>feat: support Module Plugin System by <a
href="https://github.com/ldez"><code>@​ldez</code></a> in <a
href="https://redirect.github.com/golangci/golangci-lint-action/pull/1306">golangci/golangci-lint-action#1306</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/golangci/golangci-lint-action/compare/v8.0.0...v9.0.0">https://github.com/golangci/golangci-lint-action/compare/v8.0.0...v9.0.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/golangci/golangci-lint-action/commit/0a35821d5c230e903fcfe077583637dea1b27b47"><code>0a35821</code></a>
docs: update readme</li>
<li><a
href="https://github.com/golangci/golangci-lint-action/commit/043b1b8d1c47e4591c1719682a050a7a0a82e19c"><code>043b1b8</code></a>
feat: support Module Plugin System (<a
href="https://redirect.github.com/golangci/golangci-lint-action/issues/1306">#1306</a>)</li>
<li><a
href="https://github.com/golangci/golangci-lint-action/commit/a66d26a4652b1a0b28a56b7c8b194c20f7c0b7f6"><code>a66d26a</code></a>
feat: add install-only option (<a
href="https://redirect.github.com/golangci/golangci-lint-action/issues/1305">#1305</a>)</li>
<li><a
href="https://github.com/golangci/golangci-lint-action/commit/7fe1b22e0c4632d6260fedfafd4b6025ac7418c3"><code>7fe1b22</code></a>
build(deps): bump the dependencies group with 2 updates (<a
href="https://redirect.github.com/golangci/golangci-lint-action/issues/1303">#1303</a>)</li>
<li><a
href="https://github.com/golangci/golangci-lint-action/commit/14973f18c82b6d66679563f71666ccee11907cb2"><code>14973f1</code></a>
build(deps-dev): bump the dev-dependencies group with 2 updates (<a
href="https://redirect.github.com/golangci/golangci-lint-action/issues/1299">#1299</a>)</li>
<li><a
href="https://github.com/golangci/golangci-lint-action/commit/8c2d575d9b37153325eebc4bb3a94cd09e1fae5d"><code>8c2d575</code></a>
build(deps): bump <code>@​types/node</code> from 24.8.1 to 24.9.1 in the
dependencies group...</li>
<li><a
href="https://github.com/golangci/golangci-lint-action/commit/b002b6ecfcabe6ac0e2c6cba1bcc779eb34ac51f"><code>b002b6e</code></a>
build(deps): bump actions/setup-node from 5 to 6 (<a
href="https://redirect.github.com/golangci/golangci-lint-action/issues/1296">#1296</a>)</li>
<li><a
href="https://github.com/golangci/golangci-lint-action/commit/c13f4ed1a9a677a28be0df3e11c34a78db85c77c"><code>c13f4ed</code></a>
build(deps): bump <code>@​types/node</code> from 24.7.2 to 24.8.1 in the
dependencies group...</li>
<li><a
href="https://github.com/golangci/golangci-lint-action/commit/b68d21b131098f33ec55c11c242113b4a10dc30a"><code>b68d21b</code></a>
docs: improve readme</li>
<li><a
href="https://github.com/golangci/golangci-lint-action/commit/06188a2a4a13a4786b4584e086b2040214cd4ca5"><code>06188a2</code></a>
build(deps): bump github/codeql-action from 3 to 4 (<a
href="https://redirect.github.com/golangci/golangci-lint-action/issues/1293">#1293</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/golangci/golangci-lint-action/compare/v8.0.0...v9.0.0">compare
view</a></li>
</ul>
</details>
<br />
[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=golangci/golangci-lint-action&package-manager=github_actions&previous-version=8.0.0&new-version=9.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Comment thread.github/workflows/lint.yml Fixed
Comment thread.github/workflows/lint.yml Fixed
Comment thread.github/workflows/release.yml Fixed
@github-actions

Copy link
Copy Markdown
Contributor
PR Preview Action v1.6.2

🚀 View preview at
https://evstack.github.io/docs-preview/pr-2814/

Built to branch main at 2025-11-11 11:39 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

* main:
build(deps): Bump golangci/golangci-lint-action from 8.0.0 to 9.0.0 (#2826)
build(deps): Bump actions/github-script from 7 to 8 (#2827)
refactor: replace interface{} with any for clarity and modernization (#2829)
add note on full node for evm (#2825)
chore: docker workflow refactor (#2820)
docs: add version notice warning about pre-v1 releases (#2824)
@alpealpe mentioned this pull request Nov 12, 2025
@alpealpe closed this Nov 17, 2025
@alpe
alpe deleted the alex/adr023_ha-failover_raft5 branch February 19, 2026 12:02
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

5 participants

@alpe@github-advanced-security@tac0turtle@auricom@promalert