I run a few small VPSes. Every time I wanted to know what was listening on a port, what was eating memory or how full the disk was, I opened SSH and typed the same handful of commands: ss -tulpn, top, df -h, free -m.
So I built lnxeasy: a small web panel that puts those answers on one page in the browser.
This post is about what it does, and more about three decisions behind it: no dependencies, no root, and security claims you can verify yourself.
What it shows
- System: hostname, IP, kernel, uptime, CPU, load average, memory and swap
- Storage: every disk and file system with total, used and free space
- Ports: listening TCP and UDP ports and the process holding each one
- Processes: every process with CPU, memory, owner and command, CPU use per core, and a details view with parent, children and ports
It only watches for now. Managing services is the next step.
Installing is one command:
curl -fsSL https://raw.githubusercontent.com/lnxeasy/lnxeasy/main/install.sh | sudo bash
It prints an address, a username and a random password. If you'd rather read a script before running it as root (you should), download it first. The repository has a CODE_TOUR.md that explains every step.
Decision 1: zero dependencies
package.json has no dependencies key. The server uses only Node.js built-ins (node:http, node:https, node:crypto, node:fs, node:child_process...). The browser side is plain HTML, CSS and JavaScript.
Why bother? A panel that can see your whole server is a juicy target. Every package you add is code you didn't read, running with the same access. With zero dependencies the whole thing is small enough to read in an evening.
Things I would normally reach for a library for:
| Need | Built with |
|---|---|
| HTTP routing | a 35-line router on node:http
|
| Password hashing |
crypto.scrypt with a random salt |
| Two-step codes (TOTP) |
crypto.createHmac (RFC 6238 is short) |
| Sessions | 256-bit random tokens, HttpOnly, Secure, SameSite=Strict cookies |
| Tests | the built-in node:test runner (217 tests) |
| Env files | node --env-file-if-exists |
Modern Node covers far more than people assume.
Decision 2: it never runs as root
Here is the awkward part: Linux shows which process owns a port only to that process's owner or to root. A monitoring panel wants exactly that information.
Running the whole web server as root would be the easy fix and the wrong one. Instead the installer adds a tiny systemd socket-activated helper:
# lnxeasy-helper@.service (simplified)
[Service]
ExecStart=/usr/bin/ss -tulpnH
StandardInput=null
- systemd listens on
/run/lnxeasy/helper.sock; only root and thelnxeasygroup can open it. - For each connection it starts one fixed command. Input is thrown away, output goes back through the socket.
- The unit cannot write to the file system and is stopped after ten seconds at most.
The network-facing process has no privileges, and the privileged process cannot be told what to do. If lnxeasy were fully taken over, the most it could get from the helper is the list of listening ports.
Decision 3: claims you can check with grep
"Secure" is easy to write in a README. So instead of only listing features, CODE_TOUR.md has a "ten-minute check": commands you run against the repo, each checking one claim.
# It never connects out to another machine
grep -rnE "fetch\(|http\.request|https\.request|net\.connect|createConnection" server.js cli.js src
# It never runs text as code
grep -rnE "\beval\(|new Function|vm\." server.js cli.js src public
# The page never turns server text into HTML
grep -rn "innerHTML\|outerHTML\|insertAdjacentHTML" public
The expected output is written next to each command. If one of them ever prints something unexpected, that's a bug.
The rest of the security model:
- Login required everywhere except the login page and a health check
- Refuses to listen on a public address without HTTPS (self-signed by default, or bring your own with
--cert/--key) - Optional TOTP; a code works only once, even across restarts
- Five failed attempts lock an address out for 15 minutes
- Optional IP allowlist (
lnxeasy allow 203.0.113.7); everyone else gets no answer at all - Strict Content Security Policy,
Origincheck on every write - Commands run with
execFileand fixed arguments, never a shell
What it does not protect against
I think this part matters as much as the list above:
- It has not had an independent security audit. I tested it myself against a local install (unauthorised access, path traversal, injection, XSS, brute force, flooding) and fixed six low and medium findings in v0.4.1. They're listed in SECURITY.md.
- The first visit with a self-signed certificate trusts whatever answers. Compare the fingerprint the installer prints, or use a real certificate.
- It won't stop a large DDoS.
For a server that matters: allowlist your own IP, turn on two-step sign-in, or keep the panel behind a VPN.
What's next, and how to help
lnxeasy is at v0.4.1 and early. Planned: a services view (systemd units), logs, and simple history graphs.
If you'd like to help:
- ⭐ Star the repo so others find it
- Try it on a test server and open an issue with what broke or what you missed
- Read the code tour and tell me what looks wrong. Security reports go through private reporting
- Pick something up: CONTRIBUTING.md explains the module layout (parser → service → routes), and adding a new view is mostly one folder
Thanks for reading. I'd love to hear what you'd want on a page like this.
This post was drafted with the help of an AI assistant and reviewed by me.
Top comments (0)