- Rust 61.8%
- TypeScript 33.9%
- CSS 1.7%
- Shell 1.3%
- Python 1.1%
- Other 0.1%
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> |
||
|---|---|---|
| .claude | ||
| .forgejo | ||
| .vscode | ||
| docs | ||
| public | ||
| src | ||
| src-tauri | ||
| ssh-testlab | ||
| tools | ||
| .gitattributes | ||
| .gitignore | ||
| .prettierignore | ||
| .prettierrc.json | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| eslint.config.js | ||
| index.html | ||
| justfile | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| shellstation.png | ||
| tsconfig.json | ||
| tsconfig.node.json | ||
| vite.config.ts | ||
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.
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:
.debor.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=1to~/.profileand 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.
