The Problem
EdgeVPN addresses the pain of building decentralized, introspectable peer-to-peer networks without a second time. It provides a Go-based p2p stack with a React UI, a blockchain ledger, and built-in VPN/reverse-proxy capabilities—all without a central server. The repo is a portfolio of four self-contained projects (pkg, api, cmd, internal) sharing a libp2p-based p2p core, but the wiring, configuration, and reliability story are not trivial to untangle.
What This Does
EdgeVPN is a statically compiled p2p VPN and reverse-proxy suite. The core p2p stack lives in pkg/, using libp2p for transport, Kademlia for peer discovery, and a blockchain-style ledger (pkg/blockchain/ledger.go) for node state and token-gated access. The cmd/ directory provides CLI entry points (cmd/app.go, cmd/join.go, cmd/peergate.go), while api/react-ui/ provides a React/Vite frontend at api/react-ui/src/. A DNS server (cmd/dns.go) resolves internal node IPs via shared tokens, and a lightweight DNS gateway exposes internal services as TCP endpoints.
Key files:
pkg/blockchain/ledger.go– definesWithSigner,WithOwnership,WithEnforcedOwnership; 35 callers, 8 calleespkg/logger/logger.go– logging and join messages; 29 callerspkg/node/options.go– libp2p option configuration; 13 callersapi/client/service/node.go– node lifecycle; reads/writes files, makes outbound network callsapi/react-ui/src/pages/PeersPage.tsx– UI; 0 incoming, 7 outgoing imports (high instability)cmd/join.go– CLI entry point for joining a p2p zonecmd/join.go– CLI entry point for joining a p2p zonecmd/join.go– CLI entry point for joining a p2p zone
Execution starts at main (internal/docsgen/main.go:25), which reaches 141 functions. Start (cmd/join.go:23) is the primary CLI entry, called from 5 places. Traced paths show main -> run -> pruneStale touches the filesystem, and Start -> Advertize makes an outbound network call via c.Client.Put.
How It Is Wired
Control flows through a small set of hub functions. main in internal/docsgen/main.go:25 reaches 141 functions and is the sole entry point. Start (cmd/join.go:23) reaches 101 functions and is called from 5 places. The widest blast radius comes from Start, which touches 101 functions; TestNewAppHasAllCommands routes through 124 functions.
The call graph is a DAG with context functions like New, Close, Fatal, Add, and ID are called from 18–33 places each. Start and Advertize are the primary branching points—any change to their signature ripples widely. pkg/blockchain/ledger.go is the most central module: 35 callers, 8 callees, defines WithSigner, WithOwnership, WithEnforcedOwnership. pkg/logger/logger.go (29 callers) and pkg/node/options.go (13 callers) are the next most connected. The import graph has 0 circular dependencies; the most connected module is api/react-ui/src/lib/api (7 importers, 1 importee, instability 0.13).
How To Use It
Setup: Clone via https://github.com/moses-y/edgevpn. Build the Go binary with make (Makefile present) or via Docker (Dockerfile present). The React UI requires npm install and npm run dev inside api/react-ui/.
Configuration: No .env or config file is committed. Token-based access is configured via cmd/join.go flags and pkg/node/options.go (WithMinNodes, WithRoles, WithMinStake). Token-based zone access is defined in pkg/blockchain/policy.go and enforced via WithOwnership on the node options.
Running it: Build with make build or go build ./cmd/app.go. Run a VPN node with ./edgevpn join --token <TOKEN>. Start the UI with npm run dev --prefix=api/react-ui. The CLI entry point is cmd/join.go.
Real-World Use
A Kubernetes operator (e.g., Kairos) uses EdgeVPN as a layer for decentralized cluster networking. A developer runs ./edgevpn join --token <TOKEN> on each node to create a trusted p2p VPN zone, then accesses peers via the React UI at http://localhost:5173. File transfer and reverse-proxy tunnels work without VPN setup, using cmd/ CLI flags or the api/react-ui/ UI.
Code Health & Issues
Seven findings from static analysis (0 critical, 2 high, 4 medium, 1 low):
- [HIGH] Pin third-party GitHub Actions to commit SHAs in
.github/workflows–goreleaser/goreleaser-action@v7,JamesIves/github-pages-deploy-action@releases/v3,codecov/codecov-action@v7.0.0. Mutable action tags risk secrets leakage. - [HIGH] Drop privileged mode and host networking in
docker-compose.yml–network_mode: host. Grants full host capabilities; escape is a one-liner. - [MEDIUM] Declare least-privilege GITHUB_TOKEN permissions in
.github/workflows/build.yml– 4 workflows have nopermissionsdeclaration; 2 reference secrets. - [MEDIUM] Pin container base images by digest in
Dockerfile– mutable tags (node:22-alpine,golang:1.26-alpine,alpine) mean varying CVE sets across builds. - [MEDIUM] Gate PRs on dependency vulnerability scan – no
dependency-review-actionin CI. - [MEDIUM] Add non-root USER to Docker image – no
USERdirective; root access means any container escape becomes a host problem. - [LOW] 3 TODO/FIXME markers in
install.sh– stale markers erode signal.
Beyond the measured findings: the repo has no license violation, tests pass, and Docker/CI are present. However, the React UI is high‑instability (PeersPage: 0 incoming, 7 incoming), duplicated CLI scripts across .github/, and oversized cmd/util.go (644 lines) make changes ripple widely. The cmd/ CLI scripts are duplicated across 3 files (filetest.sh, servicestest.sh, vpntest.sh), and cmd/util.go at 644 lines is a single point of failure. The api/react-ui/src/hooks/usePolling.ts has high branching density (26 branch points over 87 lines). The React UI has 3 TODO/FIXME markers in install.sh.
The Bottom Line
EdgeVPN is a capable, well‑structured p2p networking suite with a solid Go core, a functional React UI, and real-world usage at Kairos. The core p2p logic, ledger, and node options are well‑wired and relatively contained. However, the CI has action‑tag volatility, the Docker image runs privileged with host networking, and the React UI has high branching density and duplicated startup scripts. The cmd/util.go at 644 lines is a single point of failure. Use it if you need a self‑contained p2p VPN or reverse-proxy without a central server, but pin your GitHub Actions, drop privileged container mode, and add a non-root user to the Docker image before production use.