Phased plan to go from zero to a usable Lean beacon explorer with a Zig backend.
Goal: Zig binary that talks to leanSpec and exposes the same data via simple API routes.
-
Zig project setup
build.zig(and optionallybuild.zig.zonif adding deps).- Config:
LEAN_API_URL,NEMO_PORT(env or CLI).
-
HTTP server
- Bind to
NEMO_PORT; serve a minimal router (e.g.GET /api/health,GET /api/fork_choice,GET /api/checkpoints/justified). - Use
std.http.Serveror a small Zig HTTP library.
- Bind to
-
Lean API client
- HTTP GET to
LEAN_API_URL + pathfor/lean/v0/health,/lean/v0/fork_choice,/lean/v0/checkpoints/justified. - Parse JSON for fork_choice and checkpoints (e.g.
std.json). - Return 502/503 if leanSpec is unreachable or returns error.
- HTTP GET to
-
Static file serving
- Serve
static/(orpublic/) forGET /andGET /static/*. - One minimal
index.htmlthat shows “Nemo” and maybe callsGET /api/healthorGET /api/fork_choiceand prints JSON (proof of life).
- Serve
Deliverable: Run leanSpec + Nemo; open browser to Nemo; see health/fork_choice JSON (or a minimal page that fetches and displays it).
Goal: Human-readable pages for chain head, slots, and block tree.
-
Dashboard (index)
- Head block root and slot.
- Justified and finalized checkpoint (slot + root).
- Safe target.
- Validator count.
- Link to “Fork choice tree” and “Slots”.
-
Fork choice tree view
- List or tree of blocks from
nodes: root, slot, parent_root, proposer_index, weight. - Optional: simple visual (indent by depth or small graph).
- List or tree of blocks from
-
Slot list
- Endpoint
GET /api/slotsderived from fork_choice nodes (unique slots, optionally with canonical block root per slot). - Page listing slots with links to
/slot/:slot.
- Endpoint
-
Slot detail
GET /api/slot/:slot: blocks at that slot (from nodes).- Page showing block root(s), proposer, weight, parent link.
-
Block by root
GET /api/block/:root: single block from nodes; 404 if unknown.- Page: root, slot, parent_root, proposer_index, weight, link to parent.
Deliverable: Navigate from dashboard to slots and blocks; all data from leanSpec via Nemo’s API.
Goal: Reduce load on leanSpec and improve responsiveness.
-
In-memory cache
- Short TTL (e.g. 2–5 s) for fork_choice and checkpoints.
- Config:
CACHE_TTL_SEC.
-
Error handling and retries
- Retry leanSpec requests with backoff on failure.
- Clear 502/503 and timeout handling in UI (e.g. “Node unavailable, retry in X s”).
-
CORS
- Add CORS headers to Nemo’s API if the UI is served from another origin.
Deliverable: Same UI with snappier repeat loads and clearer errors when leanSpec is down.
Goal: Optional persistence for history and search (Dora-style).
-
Design
- Decide scope: e.g. “last N fork_choice snapshots” or “all blocks seen.”
- Choose storage: SQLite (single file, no extra daemon) vs PostgreSQL (if we want multi-instance or existing Postgres).
-
Indexing
- Background task or per-request: when we fetch fork_choice, optionally write nodes/slots to DB.
- Endpoints: e.g. “slots in range”, “block by root (historical)”.”
-
UI
- “Slots in range” page, optional “search block by root” with history.
Deliverable: Optional DB; Nemo can run with or without it; history views when enabled.
- Execution layer data (Lean consensus only).
- Validator management or key storage.
- Authentication/authorization (assume internal or devnet).
- Reimplementing consensus in Zig (leanSpec remains the authority).
- MVP: std only (HTTP server, client, JSON).
- If needed: one of
zig-httpz,h11e, or similar for routing; or keep minimal withstd.http.Serverand manual path parsing. - Optional later: SQLite C API via
@cImportor a Zig SQLite wrapper for Phase 4.