Skip to content

feat(core): add control plane with REST API, WebSocket, and embedded web UI#69

Open
Evsio0n wants to merge 7 commits intoencodeous:mainfrom
Evsio0n:main
Open

feat(core): add control plane with REST API, WebSocket, and embedded web UI#69
Evsio0n wants to merge 7 commits intoencodeous:mainfrom
Evsio0n:main

Conversation

@Evsio0n
Copy link
Copy Markdown

@Evsio0n Evsio0n commented Apr 12, 2026

Summary

截屏2026-04-12 09 25 35

Adds a full-featured control plane module to nylon with a REST API, WebSocket real-time log streaming, and an embedded web UI (SPA) for mesh network monitoring and management.

Phases

Phase 1 — Read-only REST API

  • /api/v1/status — node identity and role
  • /api/v1/nodes — all mesh participants
  • /api/v1/routes — Babel route table
  • /api/v1/neighbours — neighbour links with endpoint/metric details
  • /api/v1/prefixes — advertised prefixes
  • /api/v1/forward — kernel forwarding table
  • /api/v1/sysroutes — system route table

Phase 2 — Write Operations & WebSocket

  • POST /api/v1/reload — trigger config reload
  • POST /api/v1/flush_routes — flush Babel routes
  • WS /api/v1/ws — real-time packet trace stream
  • Write ops use the existing Dispatch mechanism to safely mutate shared state

Phase 3 — Embedded Web UI

  • Zero build dependencies: Vanilla JS + Pico CSS (CDN)
  • go:embed for static assets, no external file serving needed
  • SPA with client-side routing, 8 pages:
    • Dashboard — node stats, routes, neighbours, prefix counts
    • Topology — interactive mesh graph (vis.js) with multi-hop aggregation
    • Nodes, Routes, Neighbours, Prefixes, Forward — tabular views
    • Live Log — WebSocket trace viewer
  • 10s auto-refresh (topology is manual-only)
  • Actions: Reload, Flush Routes

Topology Aggregation

  • Control plane listens on both 127.0.0.1:58175 and mesh prefix address (e.g. 10.114.154.11:58175)
  • Mesh listener retries binding for up to 30s (WG interface may not exist at Init time)
  • /api/v1/topology performs BFS across mesh: queries each node's /status + /neighbours via mesh network, producing a complete {nodes, edges} graph
  • Edges with infinite metric (4294967295 / Babel infinity) are excluded

Files Changed

  • core/control_plane.go (+852) — REST API, WebSocket, topology aggregation, mesh listener
  • core/entrypoint.go (+1) — register ControlPlane module
  • core/ui/index.html (+189) — SPA shell
  • core/ui/app.js (+469) — SPA logic, vis.js topology rendering
  • core/ui/style.css (+263) — dark theme, topology canvas, animations

Design Decisions

  • Non-fatal control plane — if bind fails, nylon continues normally
  • Dispatch for state access — all reads go through the existing serial dispatch loop, no new locks needed
  • Mesh listener delayed bind — ControlPlane.Init() runs before Nylon creates the WG interface; goroutine retries every 1s for 30s

Debian added 7 commits April 12, 2026 08:15
- Add ControlPlane NyModule with REST API + WebSocket + embedded SPA
- Phase 1: read-only endpoints (status, nodes, routes, neighbours, prefixes, forward, sysroutes)
- Phase 2: write endpoints (reload, flush_routes) + WebSocket trace streaming
- Phase 3: embedded Web UI via go:embed (vanilla JS + Pico CSS)
  - Dashboard with stats overview, node list, and action buttons
  - Pages: Nodes, Routes, Neighbours, Prefixes, Forward, Live Log
  - WebSocket real-time trace event viewer
  - 10s auto-refresh on data pages
- UI served at /ui/, root / redirects to /ui/
- Non-fatal bind failure (control plane is optional)
- Add Topology page with vis.js Network interactive graph
- Router nodes: gold dots, Client nodes: blue triangles, Self: green diamond
- Edges show metric, inferred routes shown as dashed lines
- Physics-based layout with drag/zoom/hover tooltips
- Legend and navigation buttons included
…ries

- Control plane now also listens on mesh interface IP (port 58175)
  so other nylon nodes can query the API through the mesh tunnel
- New /api/v1/topology endpoint: BFS traversal across mesh, querying
  each node's /api/v1/status + /api/v1/neighbours via mesh network
- Produces complete {nodes, edges} graph with metrics for vis.js
- Frontend Topology page simplified to consume the new endpoint
- Non-fatal: mesh listener silently skipped if bind fails
NodeCfg.Addresses contains WireGuard endpoint addresses (public IPs),
not mesh tunnel addresses. The mesh IP comes from NodeCfg.Prefixes
(e.g. 10.114.154.11 from prefix 10.114.154.11/32).

Fixes both listenMesh and resolveNodeAddr to use Prefixes.
ControlPlane.Init() runs before the Nylon module creates the WireGuard
interface, so the mesh IP doesn't exist yet. Changed to a goroutine
that retries binding every second for up to 30 attempts.
Babel uses uint32 max as infinity meaning unreachable. Don't draw edges
for these — still add the node to the graph but skip the connection.
- Remove topology from 10s auto-refresh cycle
- Add Refresh button with loading state and node/link count
- Fade-in animation when topology data loads (PJAX-like feel)
- Status text shows loading state and result summary
@encodeous
Copy link
Copy Markdown
Owner

Hi, this is very cool! I love to see people building on nylon.

However, I think this can be a separate project, since it has its own scope and maintenance needs, and is not critical for the functionality of nylon. I think a feasible way is to interact with nylon over the built-in (wireguard-compatible) IPC mechanism:

  • Currently, we just have a read-only nylon inspect command, but we can easily add more apis! I think this would be a great way to maintain a clear boundary.
  • We can work together to introduce a set of apis, that will hopefully be more stable than directly hooking into the nylon code.

I think this contribution is great, and it is something that I would appreciate as well, let me know what you think.

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.

2 participants