djylb/nps: a fork of NPS that adds a node management API
NPS Enhanced — Lightweight intranet tunneling and reverse proxy with Web UI | NPS 内网穿透 反向代理 增强版 全修 新版 二开
At a glance
- What is it?
- NPS Enhanced is a Go NAT traversal and reverse proxy server with a Web UI, a companion client called npc, and a v0.35.0 management API. It is a fork of ehang-io/nps, so the question is what the fork actually changes and what it costs you.
- Who is it for?
- Adopt djylb/nps if you already run ehang-io/nps and want the node management API, certificate renewal through certmagic, or an actively published release stream: the last push was on 2026-06-10 and v0.34.7 shipped on 2026-03-12. Do not adopt it if you need a managed control plane, per-tenant isolation, or a documented rollback path, because none of those appear in the README.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 113 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem djylb/nps solves, and who it is for
The project exposes services that sit behind NAT or a firewall to the public internet without touching the router. That is the same job frp, ngrok and the original NPS do. The README frames it as "a lightweight and efficient NAT traversal and reverse proxy system for exposing services behind NAT or firewalls," and lists TCP, UDP, HTTP, HTTPS and SOCKS5 among the protocols it forwards.
The audience is narrower than that sentence suggests. This is for someone who owns the server on the public side, is willing to run both a server process (nps) and a client process (npc), and wants a browser panel instead of a config file as the daily interface. The README points to separate clients for Android and OpenWrt, which tells you the intended deployments include home routers and phones, not just cloud VMs.
The fork matters here. djylb/nps is based on ehang-io/nps, and the README describes it as "an actively maintained and extensively improved edition with continuous refactoring." If you are already running the upstream project, the fork is the interesting part; if you are starting fresh, treat it as one option among several in the same category.
How nps, npc and the Web UI fit together
The repository splits cleanly into a server, a client and a web front end: server/, client/, web/, plus cmd/nps and cmd/npc as the two entry points, which the Makefile builds with `go build -o $(BIN_DIR)/nps ./cmd/nps` and the matching npc target. The bridge/ and lib/ directories hold the shared transport and helper code.
The data flow is the classic reverse-tunnel shape. You run nps on a host with a public address and a reachable port. You run npc on the machine inside the private network, and it dials out to the server. Because the connection is outbound, no inbound firewall rule is needed on the private side. The server then accepts traffic on the ports or hostnames you configured and forwards it down the established tunnel to npc, which hands it to the local service.
The connection between the two is not fixed to one transport. The README lists TCP, KCP, TLS, QUIC, WS and WSS as supported connection protocols, and go.mod pulls in kcp-go and quic-go, which is consistent with that claim. In practice this is the setting you will tune first: a plain TCP tunnel is the easiest to debug, while KCP or QUIC are the choices when packet loss on the path makes TCP tunnels stall.
The Web UI is the management surface. The README says it provides "real-time monitoring of traffic, connection status, and client states," and that certificates, traffic limiting and access expiration are configured there. The v0.35.0 notes go further and describe a public `/api/*` management surface with snapshots, batch requests, config export and import, and scoped actor access, plus reverse WebSocket channels, callback delivery, retry queues and replay for platform integrations. That API is the fork's main structural addition over the upstream design.
Installing the nps server with Docker or install.sh
The README gives two server paths. Docker is the shortest. The image is published on Docker Hub as duan2001/nps, and the documented run command uses host networking with the configuration directory mounted from the working directory:
docker pull duan2001/nps
docker run -d --restart=always --name nps --net=host -v $(pwd)/conf:/conf -v /etc/localtime:/etc/localtime:ro duan2001/npsAfter that container starts, the README's tip is explicit: edit nps.conf, meaning the file now at ./conf/nps.conf, to set listening ports and Web admin credentials before you rely on the service. Do not skip this. A freshly started server with default credentials is reachable on whatever ports the default configuration opens.
The scripted Linux install is the alternative, and it puts files in fixed locations. The README states the default configuration path is /etc/nps/ and the binary path is /usr/bin/:
wget -qO- https://raw.githubusercontent.com/djylb/nps/refs/heads/master/install.sh | sudo sh -s nps
nps install
nps startPassing `nps` as the argument to install.sh selects the server; the same script with `npc` installs the client. The README's tip for first-time setup is to edit /etc/nps/nps.conf and verify it before running `nps start`. Lifecycle commands are `nps start`, `nps stop`, `nps restart` and `nps uninstall`, and updates run through `nps update && nps restart`.
On Windows the README points at install.ps1 in the repository root, followed by `\nps.exe install` and `\nps.exe start`. It notes that Windows 7, 8 and 8.1 users need the release assets whose names end in old, and links the 64-bit and 32-bit variants. Updating on Windows is a three-step sequence: stop, run nps-update.exe update, start.
Installing npc and your first tunnel
The client is where configuration mistakes actually happen, so the README pushes you toward the Web UI rather than hand-written flags. The Docker form is:
docker pull duan2001/npc
docker run -d --restart=always --name npc --net=host duan2001/npc -server=xxx:123,yyy:456 -vkey=key1,key2 -type=tls,tcp -log=offRead the flags carefully. `-server` takes a comma-separated list of host:port pairs, `-vkey` takes a matching comma-separated list of verification keys, and `-type` takes a comma-separated list of transports. The parallel lists are positional, so the first server pairs with the first key and the first type. The README's tip is to copy these values from the client page in the NPS Web UI "to avoid manual input mistakes," and given the list alignment that advice is worth following literally.
The Linux client install mirrors the server:
wget -qO- https://raw.githubusercontent.com/djylb/nps/refs/heads/master/install.sh | sudo sh -s npc
/usr/bin/npc install -server=xxx:123,yyy:456 -vkey=xxx,yyy -type=tls -log=off
npc startHere `-type=tls` is a single transport rather than a list. Once npc connects, the client appears in the Web UI with its traffic counters and connection state, and you create the tunnel from the panel: choose TCP, UDP, HTTP, HTTPS or SOCKS5, bind a server-side port or hostname, and point it at the local address of the service you want to expose. The README does not walk through that panel flow step by step; it defers to the documentation site for detailed configuration options.
Where djylb/nps is the wrong tool
The most concrete limitation is that the README never documents rollback. Updates are `nps update && nps restart` on Linux and stop, update, start on Windows, with no described way to pin a previous version or revert a bad release. If you are running this in front of production traffic, you are trusting the release channel.
The second is client IP fidelity. The README states plainly that if you need the real client IP you should use the project together with mmproxy, and gives SSH as the example. That means the tunnel does not preserve the original source address for you; a service behind npc that logs or rate-limits by IP will see the wrong one unless you add that extra component.
Third, the management API is new and scoped to platforms. The v0.35.0 notes describe it as a surface for external platforms, with signing, replay and change-window resynchronization. That is a lot of machinery for what is still a self-hosted tool with a single administrative role. The README shows a Web UI and a management API, but nothing about multi-tenant separation or per-user resource quotas beyond the traffic limiting and access expiration features. If you need to hand tunnels to untrusted users, this is not the design.
Finally, protocol breadth is not the same as protocol depth. P2P mode and HTTP/3 are listed as features, and go.mod carries pion/stun and goupnp, which fits a NAT traversal implementation. But the README does not explain when P2P succeeds versus when it falls back, and that is exactly the case where operators need documentation.
How it compares to frp and upstream NPS
frp is the obvious alternative and it appears in this repository's own topic list. Both projects tunnel inbound traffic over an outbound connection from a client to a server. The difference is in the control surface. frp is configured primarily through TOML files for the server and client, with a dashboard that is largely read-only. djylb/nps puts the Web UI first: tunnels, hosts, clients and certificates are managed in the panel, and the README's client instructions tell you to copy connection parameters out of that panel rather than write them by hand. If your workflow is config-as-code in git, frp fits better. If you want to add a tunnel from a browser on a laptop, nps fits better.
The comparison with upstream ehang-io/nps is more direct, because this is a fork of it. The README positions the fork around continuous refactoring, better stability and the v0.35.0 node control plane. The practical difference for an operator is the release cadence: v0.34.5, v0.34.6 and v0.34.7 landed within a week of each other in March 2026, and the last push to the repository was on 2026-06-10. Upstream's own cadence is not something this material covers, so if that is your deciding factor, check both repositories directly.
One more practical fork detail: the Docker images are published under the duan2001 namespace on Docker Hub, while GHCR images live under the djylb GitHub organization. If your registry policy only allows one of those, that decides the install path for you.
Licence, upgrade cost and what to check before adopting
The project is GPL-3.0. That matters more for a fork than for a standalone tool: if you modify nps and distribute it, or offer it as a network service in a way your counsel reads as distribution, the GPL's source obligations apply. The repository does not include any separate commercial-licence or exception file in its top-level entries, so there is no dual-licensing escape hatch visible here. This is a description of the licence file, not legal advice; if you plan to embed nps in a product, have someone qualified read the GPL-3.0 text against your deployment.
Upgrade cost is low on paper and higher in practice. The Docker path is a pull and a recreate, and the scripted path is `nps update && nps restart`. But updates replace the binary in place, and the README documents no version pinning, no staged rollout and no rollback. Combined with three releases in five days during March 2026, that means you should snapshot your /etc/nps/ or ./conf directory and keep the previous binary before updating. The configuration format is the part most likely to change under you, since the v0.35.0 notes describe a management workflow refactor around users, clients, tunnels, hosts and node-facing operations.
Before you commit, verify three things in your own environment: that the build you pulled actually exposes the `/api/*` surface described in the v0.35.0 notes, that the transport you selected (KCP, QUIC, WS or WSS) is the one your network path tolerates, and that the client IP behaviour is acceptable for whatever sits behind npc without mmproxy in front of it.
Editorial conclusion
Adopt djylb/nps if you already run ehang-io/nps and want the node management API, certificate renewal through certmagic, or an actively published release stream: the last push was on 2026-06-10 and v0.34.7 shipped on 2026-03-12. Do not adopt it if you need a managed control plane, per-tenant isolation, or a documented rollback path, because none of those appear in the README. Before deploying, read /etc/nps/nps.conf after running the installer and confirm the listening ports and Web admin credentials, then verify that the /api surface described in the v0.35.0 notes exists in the build you pulled.
Frequently asked questions
What is djylb/nps?
It is a NAT traversal and reverse proxy system written in Go, forked from ehang-io/nps and described in its README as an enhanced, actively maintained edition. It ships a server called nps, a client called npc, and a Web management interface.
How do I install the djylb/nps server?
Either pull duan2001/nps and run it with host networking and ./conf mounted, or run the install.sh script with nps as the argument, then nps install and nps start. The README advises editing nps.conf for listening ports and Web admin credentials before starting.
How does the npc client connect to the nps server?
npc dials out to the server using the -server, -vkey and -type flags, which accept comma-separated lists for multiple servers, keys and transports. The README recommends copying those values from the client page in the NPS Web UI rather than typing them.
Does djylb/nps preserve the real client IP through the tunnel?
Not by itself. The README states that if you need the real client IP you can use the project together with mmproxy, giving SSH as the example.
What transport protocols can npc use to reach the server?
The README lists TCP, KCP, TLS, QUIC, WS and WSS as the supported connection protocols for reaching the server.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/djylb-nps)