Skip to content

feat: add Zig peer manager module with NAPI bindings#308

Draft
nazarhussain wants to merge 27 commits intomainfrom
nh/peer-manager
Draft

feat: add Zig peer manager module with NAPI bindings#308
nazarhussain wants to merge 27 commits intomainfrom
nh/peer-manager

Conversation

@nazarhussain
Copy link
Copy Markdown
Contributor

@nazarhussain nazarhussain commented Apr 7, 2026

Summary

  • Implement peer management logic engine in Zig (store, scoring, prioritization, relevance)
  • Line-by-line port of packages/beacon-node/src/network/peers from Lodestar TS
  • Expose via NAPI bindings as a tick-driven stateful module (no internal threads)
  • Designed to work standalone for the eventual native client

Architecture

Layered modules in src/peer_manager/:

  • store.zigStringHashMap-backed peer data storage
  • scorer.zig — RPC score tracking with exponential decay, gossipsub blending, reconnection cooldowns
  • prioritize.zig — subnet-aware peer connect/disconnect algorithm (PeerDAS custody groups, attestation/sync subnets)
  • relevance.zig — fork digest, clock, finalized root validation
  • manager.zig — orchestrator composing all layers, returns Action[] for caller dispatch

NAPI bindings in bindings/napi/peer_manager.zig with 23 exported functions.

Test plan

  • 58 Zig unit tests across all layers (zig build test:peer_manager)
  • TypeScript integration tests (bindings/test/peerManager.test.ts) — requires NAPI build
  • Side-by-side testnet validation with TS implementation

Current Architecture

image

Zig Transition Architecture

image

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses critical stability and compatibility issues within the peerManager module. By hardening the NAPI boundary, improving memory management for discovery payloads, and aligning the API contract with Lodestar expectations, the changes ensure more robust peer management. Additionally, the introduction of explicit TypeScript definitions improves the maintainability and usability of the binding surface.

Highlights

  • Lodestar Action Compatibility: Added parseExternalPeerActionName() to support Lodestar-style action names (e.g., MidToleranceError) alongside existing Zig enum tags, improving API compatibility.
  • Memory Safety: Resolved memory lifetime issues by moving discovery query buffers onto the PeerManager and ensuring peer IDs are properly duplicated within NAPI callbacks, preventing use-after-free errors.
  • TypeScript Definitions: Added comprehensive TypeScript definitions for the peerManager API, including configuration, metadata, and action types, enhancing developer experience and type safety.
  • Metadata Plumbing: Updated the NAPI boundary to correctly parse and pass custodyGroups and samplingGroups metadata from JavaScript to the Zig peer manager.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@nazarhussain nazarhussain changed the title [codex] Fix peerManager binding ownership and API compatibility feat: add Zig peer manager module with NAPI bindings Apr 7, 2026
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive PeerManager implementation in Zig, including NAPI bindings for Node.js integration. The system manages peer lifecycles, scoring with exponential decay, and subnet-aware prioritization. The review feedback focuses on aligning error handling with the repository's fail-fast style guide by treating out-of-memory conditions as fatal and optimizing HashMap operations in the PeerStore to avoid redundant lookups.

Comment on lines +130 to +133
) catch |err| switch (err) {
error.PeerAlreadyExists => return self.actions.items,
error.OutOfMemory => return err,
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error handling here is redundant. store.addPeer only returns error.PeerAlreadyExists or error.OutOfMemory. Since error.OutOfMemory is a fatal error that should crash the program (per the style guide), you can simplify this to try self.store.addPeer(...) and let the caller handle the OOM or let it propagate.

try self.store.addPeer(peer_id, direction, self.clock_fn(), self.config);
References
  1. Assertions and error handling: The style guide mandates that assertion failures (and by extension, unrecoverable errors like OOM) should crash the program to downgrade catastrophic bugs into liveness bugs.

Comment thread src/peer_manager/store.zig Outdated
Comment on lines +54 to +55
const owned_key = try self.allocator.dupe(u8, peer_id);
errdefer self.allocator.free(owned_key);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The errdefer block here is good, but owned_key is not actually used if put fails. It is better to perform the allocation only after confirming the peer does not exist, or use getOrPut to avoid double-lookup.

const result = try self.peers.getOrPut(peer_id); if (result.found_existing) return error.PeerAlreadyExists; const owned_key = try self.allocator.dupe(u8, peer_id); errdefer self.allocator.free(owned_key); result.key_ptr.* = owned_key;
References
  1. Efficiency: Avoid redundant lookups in HashMaps by using getOrPut.

nazarhussain and others added 20 commits April 14, 2026 11:54
Design spec for the Zig peer manager module covering all three delivery
phases: PeerStore, PeerScorer, and full PeerManager. Includes TS source
reference archive from Lodestar unstable branch.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- BLOCK-1: Fix checkPingAndStatus — use initial timestamp trick for
  inbound grace period instead of fabricated disconnect check
- BLOCK-2: onConnectionOpen emits send_ping + send_status for outbound
- BLOCK-3: onConnectionClose applies cooldown for inbound peers
- BLOCK-4: addPeer takes Config for direction-dependent timestamps
- FLAG-1: Add HEARTBEAT_INTERVAL_MS and CHECK_PING_STATUS_INTERVAL
- FLAG-2: Add negative_gossip_score_ignore_threshold to Config
- FLAG-3: Document gossipsub weight derivation formula
- FLAG-4: onStatusReceived takes current_slot parameter
- FLAG-5: Remove incorrect cooldown from onGoodbye
- FLAG-6: Add getGossipScore accessor to PeerScorer
- FLAG-7: Fix misleading "prune after decay" comment
- NOTE-3: Clarify StatusScore values in sort description
- NOTE-5: Move emit_peer_connected to onStatusReceived

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
10 tasks across 6 chunks: types/constants, PeerStore, PeerScorer,
relevance + prioritization, PeerManager orchestrator, NAPI bindings
+ TS integration tests. Bottom-up build order with TDD approach.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…vals

Port all score thresholds, peer manager intervals, and prioritization
constants from the TypeScript implementation.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Port all shared enums, structs, action types, config, and bitvector
helpers from the TypeScript peer manager implementation.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Create module entry point with re-exports and register peer_manager
module and test step in the build system.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…d cooldowns

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…connect logic

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…prioritization

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
….zig

Add peer_manager to zbuild.zon modules and bindings library imports.
Run zbuild sync to regenerate build.zig with correct registration.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Use 4 mid_tolerance reports (-20) instead of 10 (-50) to stay above
the ban threshold, which would trigger a cooldown and block decay.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Replace getValueUint64 with getValueInt64 + @intcast (zapi has no u64 getter)
- Replace typeOf with typeof (zapi naming convention)

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant