A self-hosted Linux server administration panel with real-time monitoring, terminal access, and multi-host management -- all from a single web interface.
Browser ──WS──> Gateway (:9090) ──SSH──> tenodera-bridge (remote host)
──spawn──> tenodera-bridge (localhost)
No daemon, no open ports, no API keys on managed hosts. The gateway connects via SSH and spawns the bridge process on demand.
| Category | Capabilities |
|---|---|
| Dashboard | CPU, RAM, swap, disk I/O, network I/O -- real-time streaming charts |
| Terminal | Full PTY shell in the browser (xterm.js) |
| Services | systemd unit management -- start / stop / restart / enable / disable |
| Users & Groups | User account CRUD, group management, lock/unlock, password policy |
| Packages | Installed packages, search, install, update, repository management (apt, dnf, pacman) |
| Storage | Block devices, mount points, partition usage, I/O charts |
| Networking | Interfaces, traffic, firewall (ufw/firewalld/nftables), bridges, VLANs, VPN |
| Containers | Docker / Podman -- containers, images, create, logs (user + root) |
| Files | Remote file browser with sudo fallback |
| Logs | journald viewer with unit/priority filters and timestamps |
| Log Files | Browse /var/log with keyword search, context lines, date/time range |
| Kernel Dump | kdump status, crash kernel config, crash dump browser |
| Multi-host | Manage multiple servers from one panel with SSH host key verification |
curl -sSfL https://raw.githubusercontent.com/ultherego/Tenodera/main/install-panel.sh -o /tmp/install-panel.sh
sudo bash /tmp/install-panel.shThis downloads the source, installs all build dependencies (Rust, Node.js, system libraries), compiles everything natively, installs binaries and systemd services, and starts the panel on port 9090.
On each remote host you want to manage:
curl -sSfL https://raw.githubusercontent.com/ultherego/Tenodera/main/install-bridge.sh -o /tmp/install-bridge.sh
sudo bash /tmp/install-bridge.shNo daemon or service -- the gateway spawns the bridge over SSH when needed.
If you prefer to clone the repo:
git clone https://github.com/ultherego/Tenodera
cd Tenodera
# Panel (gateway host):
cd panel && sudo make all
# Bridge (managed hosts):
cd bridge && sudo make all# Panel (removes gateway, bridge, UI, config, services):
sudo bash install-panel.sh --uninstall
# Bridge only (on managed hosts):
sudo bash install-bridge.sh --uninstallOr from source: cd panel && sudo make uninstall / cd bridge && sudo make uninstall.
After install, the gateway config is at:
/etc/tenodera/gateway.env
Example with all available options:
# ── Network ──────────────────────────────────────────────
TENODERA_BIND_ADDR=0.0.0.0 # Listen address (default: 0.0.0.0)
TENODERA_BIND_PORT=9090 # Listen port (default: 9090)
# ── TLS ──────────────────────────────────────────────────
TENODERA_TLS_CERT=/etc/tenodera/tls/cert.pem # TLS certificate (PEM)
TENODERA_TLS_KEY=/etc/tenodera/tls/key.pem # TLS private key (PEM)
# TENODERA_ALLOW_UNENCRYPTED=1 # Allow plaintext HTTP (dev only!)
# ── Paths ────────────────────────────────────────────────
TENODERA_BRIDGE_BIN=/usr/local/bin/tenodera-bridge # Bridge binary path
TENODERA_UI_DIR=/usr/share/tenodera/ui # Built UI assets
# ── Security ─────────────────────────────────────────────
TENODERA_IDLE_TIMEOUT=900 # Session idle timeout in seconds (default: 900)
TENODERA_MAX_STARTUPS=20 # Max failed logins per IP in 5-min window (default: 20, min: 1)
# ── Logging ──────────────────────────────────────────────
RUST_LOG=tenodera_gateway=info # Log filter (e.g. debug, info, warn)Edit and restart: sudo systemctl restart tenodera-gateway
The gateway requires TLS by default. Generate or provide a certificate:
# Self-signed (testing):
sudo mkdir -p /etc/tenodera/tls
openssl req -x509 -newkey rsa:4096 -nodes -days 365 \
-keyout /etc/tenodera/tls/key.pem \
-out /etc/tenodera/tls/cert.pem \
-subj "/CN=$(hostname)"Then set in gateway.env:
TENODERA_TLS_CERT=/etc/tenodera/tls/cert.pem
TENODERA_TLS_KEY=/etc/tenodera/tls/key.pem
TENODERA_ALLOW_UNENCRYPTED=1
Warning: Without TLS, passwords and session tokens are sent in cleartext.
See SECURITY.md for security recommendations before deploying to production.
Log in with any PAM user that has sudo privileges on the gateway host. The panel uses system credentials (local accounts or LDAP/SSSD).
To add remote hosts, navigate to the Hosts page in the UI. The panel
scans the SSH host key fingerprint and asks for confirmation before adding.
The logged-in user must be able to SSH into the remote host with password
authentication, and tenodera-bridge must be installed there.
# Service management
sudo systemctl status tenodera-gateway
sudo systemctl restart tenodera-gateway
journalctl -u tenodera-gateway -f[ Browser ]
|
| WebSocket (channel-multiplexed JSON)
v
[ Gateway ] Axum HTTP/WS server, PAM auth, session management
|
|--- localhost: spawns tenodera-bridge directly
|--- remote: ssh user@host tenodera-bridge (via sshpass)
v
[ Bridge ] stdin/stdout newline-delimited JSON, per-user process
|
|--- 21 handler modules (system, services, packages, users, terminal, ...)
- Gateway authenticates users via PAM, manages sessions, serves the React UI, and routes WebSocket channels to bridge processes.
- Bridge is a stateless binary that handles system operations via newline-delimited JSON over stdin/stdout.
- Protocol is a shared Rust crate defining the message types used by both gateway and bridge.
No agent daemon runs on managed hosts. No ports need to be opened.
panel/ Central server (gateway + UI)
crates/gateway/ Axum HTTP/WS gateway, PAM auth, SSH transport
ui/ React 19 + TypeScript SPA (Vite 6)
Makefile Build & install
bridge/ Standalone bridge binary (deployed to managed hosts)
src/handlers/ 21 handler modules
Makefile Build & install
protocol/ Shared message types (Rust library crate)
Click to expand screenshots
# Gateway
cd panel && cargo clippy && cargo build
# Frontend (dev server with HMR, proxies /api to :9090)
cd panel/ui && npm ci && npm run dev
# Bridge
cd bridge && cargo clippy && cargo build



















