No description
  • Rust 61.8%
  • TypeScript 33.9%
  • CSS 1.7%
  • Shell 1.3%
  • Python 1.1%
  • Other 0.1%
Find a file
Max Fiedler eddd92926f End every walk down the folders on a loop of them
In PostgreSQL mode, Bulk Edit on a folder and a change of a folder's
visibility walked down the user's own folders without a guard against
a loop of them, as did the two statements that write a credential
profile or a login sequence into the session rows of a folder, which
the app's window does not use in that mode. shellstation_own_folder
keeps a regular user from making such a loop; with one, from before
that trigger or under Option B, a statement that had to walk to the
end ran until its limit of 60 seconds and failed. The walk is through
the user's own folders only, so a loop held up no one but its owner.
Sort Alphabetically on a folder, which sorts the folders below it as
well, went down the folders in the app itself and without a guard too,
in either mode: on a folder of such a loop it did not come to an end,
sent the database one statement after another and took more and more
memory, until the app was closed.

- The five statements walk with a UNION, not a UNION ALL, as the ones
  of Apply Credential Profile and Apply Login Sequence do since the
  commit before this one. Nothing else about them changes, and nothing
  in the database, so no start of the owning role is needed.
- Sort Alphabetically goes into a folder once. It also reads the
  folders once for the whole sort, where it read them again for every
  folder it went into.
- The visibility command's statements moved into a function of their
  own, set_item_visibility, which the command calls, so that the lab
  can test them.

A new ignored test, folder_walks_end_on_a_loop, has a superuser make
two folders of a user's name each other as their parent, with a third
below one of them, a folder apart from those, and a session in the
first, the third and the one apart. From either folder of the loop
each statement returns and covers the two sessions there once. The
first version of the test had no folder apart and proved nothing: the
way PostgreSQL went about the statements, it looked for each of the
user's sessions or folders among the folders of the walk and stopped
at the first it found, so every statement returned from a walk that
does not end, and the test passed with the guard taken out of all
five. With the folder apart, the guard taken out of any one of the
five fails the test. The sort has a unit test on an in-memory SQLite
database, a_recursive_sort_ends_on_folders_that_name_each_other: from
either folder of a loop and on a folder that names itself as its
parent it ends, and without the guard it fails after its 10 seconds.
The seven PostgreSQL tests pass against the PostgreSQL 18 server of
ssh-testlab, and the 435 unit tests pass. Not tested: Bulk Edit, the
change of visibility and the sort from the menu. The app itself was
not started.

The pre-commit review and audit ran twice, PASS and SECURE both times,
with nothing critical or high. The first round's audit found the sort,
which was fixed, and its review found that the explanation of the
first test version held only for one way PostgreSQL can go about a
statement, which was reworded. The second round was on the sort alone.
Deferred with the user's sign-off, with a new known-issues entry: in
PostgreSQL mode the sort goes into every folder below it that the user
can see, other users' shared ones included, with two transactions for
each, and cannot be cancelled; its statements wait for row locks, and
a sort that fails part of the way leaves the folders before it sorted.
Still open from before, and in the known issues: the command that
saves a mapping for one session does not ask whether the session was
confirmed. The edits after the last round were not reviewed again: the
case of a folder that is its own parent in the unit test, the new
known-issues entry, and one sentence of the changelog entry.

DESIGN.md 4.5.1 says that these walks end on a loop, and the changelog
has the entry in place of the known issue this closes.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-13 07:04:18 +01:00
.claude Claude agent updates 2026-05-23 10:50:59 +02:00
.forgejo Add in-app updater via tauri-plugin-updater 2026-06-08 19:38:05 +02:00
.vscode Upgrade russh 2026-04-13 22:06:19 +02:00
docs End every walk down the folders on a loop of them 2026-11-13 07:04:18 +01:00
public Project initialization 2026-03-23 23:34:11 +01:00
src Let a user assign their own login to another user's shared session 2026-11-12 20:36:59 +01:00
src-tauri End every walk down the folders on a loop of them 2026-11-13 07:04:18 +01:00
ssh-testlab Publish the test lab's ports on localhost only 2026-11-12 20:50:21 +01:00
tools Application logging 2026-04-10 09:13:25 +02:00
.gitattributes Git linguist-vendored 2026-04-13 11:24:30 +02:00
.gitignore Add in-app updater via tauri-plugin-updater 2026-06-08 19:38:05 +02:00
.prettierignore Claude 2026-03-24 11:33:25 +01:00
.prettierrc.json Claude 2026-03-24 11:33:25 +01:00
CHANGELOG.md End every walk down the folders on a loop of them 2026-11-13 07:04:18 +01:00
CLAUDE.md Move ADMIN_GUIDE, DESIGN, DEVELOPMENT, RELEASING into docs/ 2026-05-23 13:28:11 +02:00
eslint.config.js Tell a connect of the old page from the new by a per-page token 2026-11-10 10:53:29 +01:00
index.html Project initialization 2026-03-23 23:34:11 +01:00
justfile Crap 2026-05-23 13:24:30 +02:00
LICENSE Add GPL-3.0 license file and embed screenshot in README 2026-04-21 22:01:26 +02:00
package-lock.json Remove the unused @tauri-apps/plugin-opener npm package 2026-10-29 21:12:38 +01:00
package.json Remove the unused @tauri-apps/plugin-opener npm package 2026-10-29 21:12:38 +01:00
README.md Import SecureCRT button bars and the Command Manager 2026-11-09 17:34:55 +01:00
shellstation.png Add GPL-3.0 license file and embed screenshot in README 2026-04-21 22:01:26 +02:00
tsconfig.json Project initialization 2026-03-23 23:34:11 +01:00
tsconfig.node.json Refresh Rust + npm lockfiles to latest patches 2026-06-08 15:51:15 +02:00
vite.config.ts Add vitest harness and Terminal paste tests 2026-07-27 15:47:12 +02:00

ShellStation

Cross-platform terminal manager and SSH/Telnet client for network engineers and sysadmins.

ShellStation replaces tools like mRemoteNG and SecureCRT with a modern, open-source alternative that runs on Linux, macOS, and Windows.

ShellStation main interface

Why ShellStation?

Problem ShellStation
mRemoteNG is Windows-only Runs on Linux, macOS, Windows
XML session storage degrades at scale SQLite (local) or PostgreSQL (team) backend
PuTTY dependency for SSH Built-in SSH2 and Telnet — no external tools
Jump host setup is cumbersome First-class ProxyJump chaining with arbitrary hop depth
SecureCRT costs $100+/seat Free and open source
No multi-user session sharing PostgreSQL mode with row-level security for teams

Features

  • Built-in terminal emulator — xterm.js with WebGL rendering, themes, search, ligatures
  • SSH2 and Telnet — password, public key, keyboard-interactive authentication
  • Jump host chaining — connect through multiple bastions (A -> B -> C -> target)
  • Session management — folders, tags, search, drag-and-drop, bulk editing
  • Command broadcast — send commands to multiple sessions simultaneously
  • Import — migrate from mRemoteNG XML, SecureCRT XML (sessions, button bars and Command Manager), or CSV
  • Credential manager — secrets stored in the OS keychain (Keychain, Secret Service, Credential Manager)
  • Multi-user mode — shared PostgreSQL database with per-user visibility and credential isolation
  • Keyword highlighting — import SecureCRT highlight profiles or create your own
  • Session logging — automatic plain-text logs with ANSI stripping

Installation

Download the latest release for your platform from the Releases page:

  • Linux: .deb or .AppImage
  • macOS: .dmg
  • Windows: .msi

Linux: blank white window? Some GPU drivers, especially virtual GPUs in VMs, break WebKitGTK's DMABUF renderer. Add export WEBKIT_DISABLE_DMABUF_RENDERER=1 to ~/.profile and log out and back in; this applies to all WebKitGTK apps in your session. See Common Issues (Linux).

Building from Source

Prerequisites: Rust, Node.js (v18+), and the Tauri prerequisites for your OS.

git clone https://github.com/LuminaApps/shellstation.git
cd shellstation
npm install
npm run tauri build

The built application will be in src-tauri/target/release/bundle/.

Tech Stack

Layer Technology
App shell Tauri 2 (native webview, small binaries)
Backend Rust (async via Tokio)
Frontend React + TypeScript
Terminal xterm.js
SSH russh (pure Rust SSH2)
Database SQLite (single-user) / PostgreSQL (teams) via sqlx

Configuration

ShellStation stores its configuration in the platform config directory:

  • Linux: ~/.config/shellstation/
  • macOS: ~/Library/Application Support/shellstation/
  • Windows: %APPDATA%\shellstation\

PostgreSQL Multi-User Setup

For team deployments with shared session databases, see the PostgreSQL Administration Guide.

Development

# Start the dev server with hot reload
npm run tauri dev

# Lint (must pass before committing)
cd src-tauri && cargo clippy -- -D warnings
cd src-tauri && cargo fmt -- --check
npx eslint .
npx prettier --check "src/**/*.{ts,tsx,css,json}"
npx tsc --noEmit

See DESIGN.md for architecture details, database schema, and the development roadmap.

Contributing

Contributions are welcome. Please ensure all linters pass with zero warnings before submitting a pull request.

License

This project is licensed under the GNU General Public License v3.0.