nextrmnl

nextrmnl

Docker app from DerKezorm's Repository

Overview

nextrmnl is SSH and SFTP in the browser for the machines in your own network: a terminal (xterm.js) with tabs, jump hosts and copy and paste the way PuTTY does it, plus file transfer next to it. Accounts with a vault each for keys and passwords, sealed with the account's password so that not even the operator can read it. Host keys are checked and remembered, unknown hosts are shown with their fingerprint first. Connections can be shared between accounts without sharing access, sign-in works with a password, an optional second factor from an authenticator app, or OpenID Connect (one-button setup for authentik). By default nextrmnl only connects into private networks. Backups as encrypted ZIP files, a log that never contains terminal content or credentials. The interface is available in English and German.

nextrmnl

SSH and SFTP in the browser, self-hosted, for the machines in your own network. Accounts with a vault each, host keys that are checked and remembered, and a terminal that copies and pastes the way PuTTY and Windows Terminal do.

nextrmnl is part of the nex apps and looks like them.

Screenshots

Terminal with the connection list pinned to the left

The terminal, with the connection list pinned to the left. It floats above the terminal by default.

Files next to the terminal

Files over SFTP next to the terminal: browse, upload, download, rename, delete.

The vault with keys and stored passwords

The vault: keys and stored passwords, sealed with the account's password.

What it does

  • Terminal in the browser (xterm.js) over a WebSocket to the nextrmnl server, which speaks SSH to your machines. Full-screen, tabs for open sessions, jump hosts, a command to run after sign-in, keepalives.
  • Sessions that survive the browser: a reload, a closed tab or a dropped network does not end the shell. It keeps running for a few minutes (5 by default, set by the operator, 0 turns it off) and comes back with its last screen; after a short drop only what was missed is sent again. Another device of the same account can take it over. Closing the tab on purpose ends it at once, and the browser asks before leaving a page with an open shell, so Ctrl+W in nano does not cost the session.
  • Split view: two terminals side by side, above each other, or four; each field picks its session. Type into all sends the keyboard to every field at once, with red frames as long as it is on.
  • Commands kept per account and typed into the terminal with a click, never run: Enter is yours.
  • Import from ~/.ssh/config (hosts, users, ports, ProxyJump) or a PuTTY registry export. Keys and passwords stay where they are.
  • On a phone: a key bar with Esc, Tab, Ctrl, Alt and the arrows, and nextrmnl installs as an app.
  • Direct links to a connection (/connect/<id>) for bookmarks and dashboards; opening one asks before it connects.
  • Search in the terminal with Ctrl+Shift+F, all matches marked. Color schemes (Dracula, Nord, Solarized, Gruvbox, One Dark, Tokyo Night, or the nex colors that follow light and dark) and any installed font, Nerd Fonts included; changes apply to open terminals right away.
  • SFTP next to the terminal: browse, upload with drag and drop, download, rename, delete, all through the same SSH connection.
  • Copy and paste like PuTTY: selecting copies, Ctrl+Shift+C, Ctrl+Insert, Ctrl+C with a selection; paste with Ctrl+V, Ctrl+Shift+V, Shift+Insert or a right click. Several lines are shown before they run.
  • Accounts: the first account is the operator, others come by invitation link or through OpenID Connect. Connections can be shared; sharing passes name, address and settings, never access.
  • Second factor: a code from an authenticator app on top of the password (TOTP), a passkey or a security key such as a YubiKey (WebAuthn, on HTTPS), with recovery codes for the day the phone or the key is gone. The operator can require it for every password account; whoever locks themselves out gets it reset by the operator.
  • Guest accounts and shares with an end: an invitation can make an account that ends on a day, and a share can end on a day too. Then sign-in, connection and open terminals end with it.
  • Notifications to nexsift, Gotify, ntfy or a webhook: locked accounts, changed host keys, sign-ins, opened sessions, failed backups, each group on or off. Off by default.
  • A vault per account for private keys and stored passwords, encrypted with a key that only the account's password unwraps. Not the operator, not a backup, not a database dump can read it. Generate Ed25519 or RSA keys, or paste existing ones. Download the vault as an encrypted file and restore it, here or on another nextrmnl.
  • Host keys are compared on every connection. Unknown hosts are shown with their fingerprint before you trust them; a changed key is refused until you say otherwise.
  • Allowed targets: by default nextrmnl only connects into private networks (10/8, 172.16/12, 192.168/16, 100.64/10 for VPNs, loopback, link-local). The operator can add networks and names, or open it up.
  • OpenID Connect: several providers side by side (authentik, Microsoft Entra ID with common or organizations, Keycloak, Authelia, Pocket ID and any other with discovery), one button each on the sign-in page. Accounts are matched by the identity at the provider only, never by an email address: existing accounts link themselves under their account with their password, new people come by invitation (also through the provider) or, if the operator allows it per provider, as members. Per provider the operator decides whether it checks the second factor itself; if not, nextrmnl asks for its own code afterwards. For authentik there is a one-button setup that creates provider, application and signing key with a one-time API token, or a blueprint file to do it without a token. Without a public address the button proposes the one your browser uses and registers it only after you confirm; changing the public address later asks first, because the provider still knows the old return address. Sign-in with a password can be turned off once a provider is on; the operator always keeps the password.
  • Backups: consistent copies on a schedule and before every schema change, downloads as an AES encrypted ZIP that 7-Zip opens without nextrmnl, restore with a preview and a restart.
  • A log with four levels (the deep ones switch themselves off), a request id in every line and in every error message, downloadable. Never with terminal content, keystrokes, passwords, keys or tokens.
  • Read-only API keys for dashboards such as nexdeck: four endpoints under /api/v1 (status, sessions, history, connections) with the key as Authorization: Bearer. Off by default; the operator switches them on and creates them under Settings, Security. Each connection carries its direct link.
  • German and English; another language is one JSON file.

Start

services:
  nextrmnl:
    image: ghcr.io/derkezorm/nextrmnl:latest
    container_name: nextrmnl
    restart: unless-stopped
    ports:
      - "8460:8000"
    volumes:
      - ./data:/data
    environment:
      PUID: 1000
      PGID: 1000
      TZ: Europe/Berlin
docker compose up -d

Open http://<your-host>:8460 and create the operator account. The password is at least 12 characters; it also protects your vault.

Put nextrmnl behind a reverse proxy with TLS before you use it from anywhere but your own desk. It holds the access to your machines. Pasting with a right click also needs HTTPS; the browser allows reading the clipboard only on secure origins (Ctrl+V works regardless). Name the proxy in NEXTRMNL_TRUSTED_PROXIES (see below): nextrmnl takes https and the client's address from it and from nobody else.

Where things are stored

Everything lives in /data: the SQLite database nextrmnl.db, secret.key, backups/, logs/. Mount it from a local disk, never from an SMB or NFS share.

secret.key protects the server-side secrets (the client secrets of the sign-in providers). The vaults do not depend on it: they open with the account's password only. Both go into the backup archive.

Environment

Variable Default Meaning
NEXTRMNL_DATA_DIR /data Data directory
NEXTRMNL_SECRET_KEY created on first start Protects the server-side secrets; when set, it wins over secret.key
NEXTRMNL_PUBLIC_URL from the request The address people use to reach nextrmnl; for invitation links and the OIDC redirect. The public address under Settings, Sign-in wins when set
NEXTRMNL_LOG_LEVEL stored setting quiet, normal, detailed or trace; overrides the setting, the emergency exit when the app does not start
NEXTRMNL_TRUSTED_PROXIES none Addresses or networks of reverse proxies whose X-Forwarded-For and X-Forwarded-Proto are believed, comma separated (172.18.0.0/16, 10.0.0.5). No other sender is believed, 127.0.0.1 included. Without it every request counts as coming from its peer and with the scheme of its connection: a proxy in front makes all its clients share one sign-in brake, and behind a proxy that ends TLS the session cookie is not Secure (unless NEXTRMNL_COOKIE_SECURE is on), passkeys need the public address, and the sign-in settings warn that the server sees http
NEXTRMNL_SESSION_DAYS 14 A browser session ends after this many days
NEXTRMNL_API_DOCS false Serves /api/docs and /api/openapi.json; off by default
NEXTRMNL_COOKIE_SECURE auto on, off or auto (HTTPS when the connection is, or when a proxy in NEXTRMNL_TRUSTED_PROXIES says so in X-Forwarded-Proto)
NEXTRMNL_PORT 8000 Port inside the container, for host networking
PUID, PGID 1000 Owner of the files in the data directory

Security in short

  • Passwords are hashed with Argon2id. Ten failed sign-ins in a row lock the account for fifteen minutes.
  • With a second factor, the password alone opens nothing: the vault key waits in memory for the code, at most five minutes and five tries. A code counts once; the seed is stored encrypted, recovery codes as hashes.
  • The vault key is wrapped with Argon2id and AES-256-GCM; entries are AES-256-GCM. An open vault is its key in the server's memory, forgotten after 30 minutes without use (configurable), on signing out and on every restart. It is open in one browser session only: another browser of the same account, or a sign-in through a provider, opens it with the password itself.
  • Every changing request needs the header X-Requested-By: nextrmnl; WebSockets must come from the same origin.
  • Every password check while signed in (opening the vault, exporting it, changing the password, the second factor) counts like a sign-in: wrong answers lock the account. Terminals end with the sign-in they came with.
  • A shell waiting for its browser can only be taken back by the account that opened it, not even by the operator. Its last screen (at most 256 KB) stays in the server's memory only, never on disk, in the log or in a backup, and is gone with the session. Signing out ends waiting shells too.
  • A passkey is a second factor, never a replacement for the password: the vault opens with the password alone. Only the public key is stored; a key whose counter goes backwards (a copy) is refused.
  • Notifications are another way out and closed until the operator opens them. Their token is stored encrypted and never shown again; redirects are not followed; a message never carries terminal content, passwords or keys.
  • A direct link never opens a shell by itself: it shows the connection and waits for a click, and only to an account that sees that connection. Commands are text without control characters, so a stored command cannot smuggle in a Ctrl+C or an escape sequence.
  • A member of a shared connection signs in with their own user, key or password, and only their own start command runs in their shell; a stored password stays with the host, port and user it was given for.
  • Responses carry a Content Security Policy, X-Frame-Options: DENY and friends.
  • Host keys are verified before authentication; nothing is trusted automatically.
  • API keys only read. A key is shown once and stored as a hash, works only as a Bearer header, and stops working when the switch goes off or its account is no longer the operator. It opens no terminal, no file and no vault, and what it reads carries no sender addresses.
  • The log never contains terminal content, keystrokes, passwords, passphrases, private keys or tokens, and a test scans the code for the obvious mistakes.

Development

cd backend && python -m venv .venv && .venv/Scripts/pip install -r requirements-dev.txt
.venv/Scripts/uvicorn app.main:app --port 8460 --reload
cd frontend && npm ci && npm run dev

The frontend on port 5460 proxies /api to the backend. Tests: pytest in backend, npx vitest run in frontend.

License

AGPL-3.0.

Download Statistics

115
Total Downloads

Related apps

Details

Repository
ghcr.io/derkezorm/nextrmnl
Last Updated2026-10-13
First Seen2026-09-26

Runtime arguments

Web UI
http://[IP]:[PORT:8000]/
Network
bridge
Shell
sh
Privileged
false

Template configuration

WebUI PortPorttcp

Port you reach nextrmnl at. Container port: 8000

Target
8000
Default
8460
Value
8460
Data (Container Path: /data)Pathrw

Holds the SQLite database with accounts, connections and the sealed vaults, the key secret.key, backups and logs. Keep it on a local disk, not on a network share.

Target
/data
Default
/mnt/user/appdata/nextrmnl
Value
/mnt/user/appdata/nextrmnl
PUIDVariable

User id that should own the files in /data. 99 is the Unraid default.

Default
99
Value
99
PGIDVariable

Group id that should own the files in /data. 100 is the Unraid default.

Default
100
Value
100
Time ZoneVariable

Time zone for timestamps in the log and the session history, for example Europe/Berlin. Leave blank for UTC.

Target
TZ
Public URLVariable

The address people use to reach nextrmnl, for example https://nextrmnl.example.com when it sits behind a reverse proxy. Used for invitation links and the return address of OpenID Connect. Leave blank to take the address of the request; the same can be set later under Settings, Sign-in.

Target
NEXTRMNL_PUBLIC_URL
Container Port (host networking only)Variable

Leave this empty unless you switched Network Type to Host. In bridge mode the WebUI Port above already does the job and setting this will break it, because the mapping still points at 8000. On host networking the port inside the container is the port on your server, so use this to move nextrmnl off 8000 if something else is already there.

Target
NEXTRMNL_PORT
Secret KeyVariable

Protects the server-side secrets (the OpenID Connect client secret, second-factor seeds). Leave blank: nextrmnl creates one on first start and keeps it in /data/secret.key. The vaults do not depend on it; they open with the account's password only.

Target
NEXTRMNL_SECRET_KEY
Secure CookieVariable

auto marks the session cookie Secure when the request arrived over https, also behind a proxy listed under Trusted Proxies that sends X-Forwarded-Proto. Use on only if a proxy terminates TLS and forwards plain http without that header, and never if nextrmnl should also be reachable over http, or nobody can sign in. off never marks it.

Target
NEXTRMNL_COOKIE_SECURE
Default
auto
Value
auto
Trusted ProxiesVariable

Addresses or networks of your reverse proxies, comma separated, for example 172.16.0.0/12. Only their X-Forwarded-For and X-Forwarded-Proto (the rightmost value) are believed, so only they can tell nextrmnl that a request came in over https. Leave blank when nextrmnl is not behind a proxy.

Target
NEXTRMNL_TRUSTED_PROXIES