Name | sshler JSON |
Version |
0.3.3
JSON |
| download |
home_page | None |
Summary | A local FastAPI-powered SSH multiplexer for tmux-in-browser — from your laptop only. |
upload_time | 2025-10-22 19:30:42 |
maintainer | None |
docs_url | None |
author | You |
requires_python | >=3.12 |
license | None |
keywords |
ssh
tmux
fastapi
sftp
htmx
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# sshler
sshler is a lightweight, local-only web UI that lets you browse remote files over SFTP and jump into tmux sessions in your browser — without installing anything on the remote host.
## Features
- **Cross-platform**: Runs on Windows 11, macOS, and Linux (anywhere with Python 3.12+)
- **Local workspace**: Browse your own filesystem and launch native tmux sessions alongside remote hosts (uses WSL tmux on Windows, native tmux on Linux/macOS)
- **SSH integration**: Uses your existing SSH keys and honors OpenSSH aliases
- **Terminal in browser**: Opens `tmux new -As <session> -c <dir>` on the remote host and bridges it via WebSocket + xterm.js
- **File management**: HTMX-based file browser with preview, edit, delete, and "Open Terminal Here"
- **Auto-configuration**: Creates starter config on first run
- **Alias resolution**: Falls back to `ssh -G` when DNS fails; reset overrides with one click
- **File operations**: Preview, edit (≤256 KB), and delete files with CodeMirror editor
- **Bilingual UI**: Full English and Japanese language support
## Install
### PyPI (recommended)
```bash
pip install sshler
# Launch once to create the config + systemd/service assets
sshler serve
```
Requires Python **3.12+**.
### Development
```bash
uv pip install -e .
# or: pip install -e .
```
After cloning the repository, install the dev extras and run the usual tooling:
```bash
uv sync --group dev
uv run ruff check .
uv run pytest
```
## Run
```bash
sshler serve
```
The app will open `http://127.0.0.1:8822` in your default browser.
## Configuration / 設定
sshler reads your existing OpenSSH config (`~/.ssh/config`) and shows every concrete `Host` entry automatically. Any favourites, default directories, or custom hosts you add through the UI are stored in a companion YAML file.
- **日本語:** OpenSSH の設定 (`~/.ssh/config`) を読み取り、すべての `Host` が自動的に一覧に表示されます。お気に入りやデフォルトディレクトリ、カスタムホストは付属の YAML に保存されます。
A config file is created on first run:
- Windows: `%APPDATA%\sshler\boxes.yaml`
- macOS/Linux: `~/.config/sshler/boxes.yaml`
Example:
```yaml
boxes:
- name: gabu-server
host: example.tailnet.ts.net # literal IP/FQDN or keep as placeholder
ssh_alias: gabu-server # optional: resolves via `ssh -G gabu-server`
user: gabu
port: 22
keyfile: "C:/Users/gabu/.ssh/id_ed25519"
favorites:
- /home/gabu
- /home/gabu/projects
- /srv/codex
default_dir: /home/gabu
```
> Tip: Set `default_dir` if your home path isn’t `/home/<user>`.
> If you rely on an OpenSSH alias, add `ssh_alias:` and sshler will run `ssh -G` to expand it when DNS fails.
- **日本語のヒント:** ホームディレクトリが `/home/<user>` 以外なら `default_dir` を設定してください。OpenSSH のエイリアスを利用する場合は `ssh_alias:` を追加すると、DNS 失敗時に `ssh -G` で解決します。
### Resetting overrides / 上書き設定のリセット
Boxes imported from SSH config show a highlighted border and “Refresh” button. If you change something in `~/.ssh/config`, hit Refresh to drop any stored overrides (host/user/port/key) so the new settings take effect without editing `boxes.yaml`.
- **日本語:** SSH 設定から取り込まれたボックスは枠が強調表示され、「Refresh」ボタンで上書き設定を削除できます。`~/.ssh/config` を更新した際はボタンを押すだけで最新状態になります。
### Adding custom boxes / カスタムボックスの追加
Hit “Add Box” in the UI to define a host that isn’t in your SSH config (for example, a throwaway Docker container). Fields you leave blank fall back to your SSH defaults.
- **日本語:** UI の “Add Box” から SSH 設定に存在しないホストも追加できます(例: 一時的な Docker コンテナ)。未入力の項目は SSH のデフォルト値が使われます。
### Security model (important)
- sshler is designed for **single-user localhost** use. By default `sshler serve` binds to `127.0.0.1` and prints a random `X-SSHLER-TOKEN` that every state-changing request must send.
- File uploads are capped at 50 MB (tunable via `--max-upload-mb`). Uploaded content is never executed server-side.
- SSH connections still honour your system `known_hosts`. Only set `known_hosts: ignore` if you fully understand the risk.
- If you expose sshler beyond localhost, opt-in via `--allow-origin` and add `--auth user:pass` (basic auth). Use it only on networks you trust and put TLS in front (nginx, Caddy, etc.).
- There is no telemetry, analytics, or call-home behaviour.
### CLI options
```bash
sshler serve \
--host 127.0.0.1 \
--port 8822 \
--max-upload-mb 50 \
--allow-origin http://workstation:8822 \
--auth coder:supersecret \
--no-ssh-alias \
--log-level info
```
- `--host` (alias `--bind`) sets the bind address (default: `127.0.0.1` for localhost-only). Use `0.0.0.0` to expose on all interfaces, but **only on trusted networks with `--auth` and TLS**.
- `--port` sets the port number (default: `8822`).
- `--allow-origin` can be repeated to expand CORS; combine it with `--auth` if you expose the UI beyond localhost.
- `--auth user:pass` enables HTTP basic authentication (recommended if binding to `0.0.0.0`).
- `--max-upload-mb` sets the upload size limit (default: 50 MB).
- `--no-ssh-alias` disables the `ssh -G` fallback when DNS fails.
- `--token` lets you supply your own `X-SSHLER-TOKEN` (otherwise a secure random value is generated).
- `--log-level` feeds directly into uvicorn (options: `critical`, `error`, `warning`, `info`, `debug`, `trace`).
The server prints the token (and, if enabled, the basic auth username) on startup so you can copy it into API clients or browser extensions.
- **日本語:** サーバー起動時にトークン(および Basic 認証を有効にした場合はユーザー名)を表示するので、API クライアントやブラウザ拡張に貼り付けて利用できます。
### Terminal notifications
- Send a bell (`printf '\a'`) from tmux or your shell to flash the browser title and raise a desktop notification whenever the sshler tab is hidden.
- For richer messages use OSC 777: `printf '\033]777;notify=Codex%20done|Check%20the%20output\a'`. The text before the `|` becomes the title; the second part is the body.
- JSON payloads are also supported: `printf '\033]777;notify={"title":"Codex","message":"All tasks finished"}\a'`.
- The first notification prompts the browser for permission. Denying it still leaves the in-app toast and title badge when you return to the tab.
- **日本語:** タブが非表示のときに `printf '\a'` でベルを送ると、ブラウザタイトルが点滅しデスクトップ通知が表示されます。`printf '\033]777;notify=タイトル|本文\a'` で任意メッセージを送信でき、JSON (`notify={"title":"...","message":"..."}`) にも対応しています。初回は通知許可のダイアログが表示されますが、拒否した場合でもタブ復帰時にトーストとタイトルのバッジで気付けます。
## Autostart / 自動起動
### Windows (Task Scheduler)
1. Run `where sshler` to locate the installed executable (for example, `%LOCALAPPDATA%\Programs\Python\Python312\Scripts\sshler.exe`).
2. Open **Task Scheduler → Create Task…**.
3. Under **Triggers**, add “At log on”.
4. Under **Actions**, choose “Start a program” and point to the `sshler.exe` path. Add arguments such as `serve --no-browser` and set **Start in** to a writable directory.
5. Tick “Run with highest privileges” if you need WSL access, then save. sshler will now launch automatically every time you sign in.
- **日本語:** Task Scheduler で「ログオン時」をトリガーにし、`sshler.exe serve --no-browser` を実行するタスクを作成すると、サインイン時に自動起動します。
### Linux / macOS (systemd user service)
Create `~/.config/systemd/user/sshler.service`:
```ini
[Unit]
Description=sshler – local tmux bridge
After=network.target
[Service]
Type=simple
ExecStart=%h/.local/bin/sshler serve --bind 127.0.0.1 --no-browser
Restart=on-failure
[Install]
WantedBy=default.target
```
Reload and enable:
```bash
systemctl --user daemon-reload
systemctl --user enable --now sshler.service
```
- **日本語:** 上記の systemd ユーザーサービスを作成し、`systemctl --user enable --now sshler.service` を実行するとサインイン時に自動起動します。
### Dependencies & licenses
- FastAPI, uvicorn, asyncssh, platformdirs, yaml (PyPI packages, permissive licenses)
- HTMX (MIT) and xterm.js (MIT) are loaded from unpkg
- CodeMirror (MIT) powers the editor
All assets are used under their respective MIT/BSD-style licenses. sshler itself ships under the MIT license.
- **日本語:** 依存ライブラリはいずれも寛容なライセンス (MIT/BSD) で提供されています。sshler 本体も MIT ライセンスで配布されます。
## Why "sshler"?
Because sometimes you want less VS Code, more terminal — but still in a nice browser tab.
---
# 日本語ドキュメント
sshler はローカル専用の軽量 Web UI で、リモートファイルを SFTP で閲覧したり、ブラウザ上で tmux セッションに接続したりできます。リモート側に追加ソフトをインストールする必要はありません。
## 特徴
- **クロスプラットフォーム**: Windows 11、macOS、Linux で動作(Python 3.12+ が必要)
- **ローカルワークスペース**: ローカルファイルシステムを閲覧し、リモートホストと並べてネイティブの tmux セッションを起動(Windows では WSL tmux、Linux/macOS ではネイティブ tmux を使用)
- **SSH 統合**: 既存の SSH 鍵を使用し、OpenSSH エイリアスに対応
- **ブラウザ内ターミナル**: リモートホストで `tmux new -As <session> -c <dir>` を開き、WebSocket + xterm.js 経由で接続
- **ファイル管理**: プレビュー、編集、削除、「ここでターミナルを開く」機能を備えた HTMX ベースのファイルブラウザ
- **自動設定**: 初回起動時にスターター設定を作成
- **エイリアス解決**: DNS 失敗時は `ssh -G` にフォールバック。ワンクリックで上書きをリセット
- **ファイル操作**: CodeMirror エディタでファイルのプレビュー、編集(256 KB 以下)、削除が可能
- **バイリンガル UI**: 英語と日本語の完全サポート
## インストール
### PyPI(推奨)
```bash
pip install sshler
# 設定ファイルと systemd/サービスアセットを作成するため一度起動
sshler serve
```
Python **3.12+** が必要です。
### 開発用
```bash
uv pip install -e .
# または: pip install -e .
```
リポジトリをクローンした後、dev extras をインストールして通常のツールを実行:
```bash
uv sync --group dev
uv run ruff check .
uv run pytest
```
## 実行
```bash
sshler serve
```
デフォルトブラウザで `http://127.0.0.1:8822` が開きます。
## 設定
sshler は既存の OpenSSH 設定(`~/.ssh/config`)を読み取り、すべての具体的な `Host` エントリを自動的に表示します。UI を通じて追加したお気に入り、デフォルトディレクトリ、カスタムホストは、付属の YAML ファイルに保存されます。
設定ファイルは初回実行時に作成されます:
- Windows: `%APPDATA%\sshler\boxes.yaml`
- macOS/Linux: `~/.config/sshler/boxes.yaml`
例:
```yaml
boxes:
- name: gabu-server
host: example.tailnet.ts.net
ssh_alias: gabu-server
user: gabu
port: 22
keyfile: "C:/Users/gabu/.ssh/id_ed25519"
favorites:
- /home/gabu
- /home/gabu/projects
- /srv/codex
default_dir: /home/gabu
```
> ヒント: ホームパスが `/home/<user>` でない場合は `default_dir` を設定してください。OpenSSH エイリアスを使用する場合は `ssh_alias:` を追加すると、DNS 失敗時に `ssh -G` で解決します。
## 名前の由来
VS Code だけに頼らず、ブラウザタブの中で軽快にターミナルを扱いたい──そんな願いからこの名前になりました。
Raw data
{
"_id": null,
"home_page": null,
"name": "sshler",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.12",
"maintainer_email": null,
"keywords": "ssh, tmux, fastapi, sftp, htmx",
"author": "You",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/17/57/fb0c31fe078d6e89744528cbf7d610ce0332f7907e0d60c8d52aefd8d6fb/sshler-0.3.3.tar.gz",
"platform": null,
"description": "# sshler\r\n\r\nsshler is a lightweight, local-only web UI that lets you browse remote files over SFTP and jump into tmux sessions in your browser \u2014 without installing anything on the remote host.\r\n\r\n## Features\r\n\r\n- **Cross-platform**: Runs on Windows 11, macOS, and Linux (anywhere with Python 3.12+)\r\n- **Local workspace**: Browse your own filesystem and launch native tmux sessions alongside remote hosts (uses WSL tmux on Windows, native tmux on Linux/macOS)\r\n- **SSH integration**: Uses your existing SSH keys and honors OpenSSH aliases\r\n- **Terminal in browser**: Opens `tmux new -As <session> -c <dir>` on the remote host and bridges it via WebSocket + xterm.js\r\n- **File management**: HTMX-based file browser with preview, edit, delete, and \"Open Terminal Here\"\r\n- **Auto-configuration**: Creates starter config on first run\r\n- **Alias resolution**: Falls back to `ssh -G` when DNS fails; reset overrides with one click\r\n- **File operations**: Preview, edit (\u2264256 KB), and delete files with CodeMirror editor\r\n- **Bilingual UI**: Full English and Japanese language support\r\n\r\n## Install\r\n\r\n### PyPI (recommended)\r\n\r\n```bash\r\npip install sshler\r\n\r\n# Launch once to create the config + systemd/service assets\r\nsshler serve\r\n```\r\n\r\nRequires Python **3.12+**.\r\n\r\n### Development\r\n\r\n```bash\r\nuv pip install -e .\r\n# or: pip install -e .\r\n```\r\n\r\nAfter cloning the repository, install the dev extras and run the usual tooling:\r\n\r\n```bash\r\nuv sync --group dev\r\nuv run ruff check .\r\nuv run pytest\r\n```\r\n\r\n## Run\r\n\r\n```bash\r\nsshler serve\r\n```\r\n\r\nThe app will open `http://127.0.0.1:8822` in your default browser.\r\n\r\n## Configuration / \u8a2d\u5b9a\r\n\r\nsshler reads your existing OpenSSH config (`~/.ssh/config`) and shows every concrete `Host` entry automatically. Any favourites, default directories, or custom hosts you add through the UI are stored in a companion YAML file.\r\n\r\n- **\u65e5\u672c\u8a9e:** OpenSSH \u306e\u8a2d\u5b9a (`~/.ssh/config`) \u3092\u8aad\u307f\u53d6\u308a\u3001\u3059\u3079\u3066\u306e `Host` \u304c\u81ea\u52d5\u7684\u306b\u4e00\u89a7\u306b\u8868\u793a\u3055\u308c\u307e\u3059\u3002\u304a\u6c17\u306b\u5165\u308a\u3084\u30c7\u30d5\u30a9\u30eb\u30c8\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3001\u30ab\u30b9\u30bf\u30e0\u30db\u30b9\u30c8\u306f\u4ed8\u5c5e\u306e YAML \u306b\u4fdd\u5b58\u3055\u308c\u307e\u3059\u3002\r\n\r\nA config file is created on first run:\r\n\r\n- Windows: `%APPDATA%\\sshler\\boxes.yaml`\r\n- macOS/Linux: `~/.config/sshler/boxes.yaml`\r\n\r\nExample:\r\n\r\n```yaml\r\nboxes:\r\n - name: gabu-server\r\n host: example.tailnet.ts.net # literal IP/FQDN or keep as placeholder\r\n ssh_alias: gabu-server # optional: resolves via `ssh -G gabu-server`\r\n user: gabu\r\n port: 22\r\n keyfile: \"C:/Users/gabu/.ssh/id_ed25519\"\r\n favorites:\r\n - /home/gabu\r\n - /home/gabu/projects\r\n - /srv/codex\r\n default_dir: /home/gabu\r\n```\r\n\r\n> Tip: Set `default_dir` if your home path isn\u2019t `/home/<user>`.\r\n> If you rely on an OpenSSH alias, add `ssh_alias:` and sshler will run `ssh -G` to expand it when DNS fails.\r\n\r\n- **\u65e5\u672c\u8a9e\u306e\u30d2\u30f3\u30c8:** \u30db\u30fc\u30e0\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u304c `/home/<user>` \u4ee5\u5916\u306a\u3089 `default_dir` \u3092\u8a2d\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002OpenSSH \u306e\u30a8\u30a4\u30ea\u30a2\u30b9\u3092\u5229\u7528\u3059\u308b\u5834\u5408\u306f `ssh_alias:` \u3092\u8ffd\u52a0\u3059\u308b\u3068\u3001DNS \u5931\u6557\u6642\u306b `ssh -G` \u3067\u89e3\u6c7a\u3057\u307e\u3059\u3002\r\n\r\n### Resetting overrides / \u4e0a\u66f8\u304d\u8a2d\u5b9a\u306e\u30ea\u30bb\u30c3\u30c8\r\n\r\nBoxes imported from SSH config show a highlighted border and \u201cRefresh\u201d button. If you change something in `~/.ssh/config`, hit Refresh to drop any stored overrides (host/user/port/key) so the new settings take effect without editing `boxes.yaml`.\r\n\r\n- **\u65e5\u672c\u8a9e:** SSH \u8a2d\u5b9a\u304b\u3089\u53d6\u308a\u8fbc\u307e\u308c\u305f\u30dc\u30c3\u30af\u30b9\u306f\u67a0\u304c\u5f37\u8abf\u8868\u793a\u3055\u308c\u3001\u300cRefresh\u300d\u30dc\u30bf\u30f3\u3067\u4e0a\u66f8\u304d\u8a2d\u5b9a\u3092\u524a\u9664\u3067\u304d\u307e\u3059\u3002`~/.ssh/config` \u3092\u66f4\u65b0\u3057\u305f\u969b\u306f\u30dc\u30bf\u30f3\u3092\u62bc\u3059\u3060\u3051\u3067\u6700\u65b0\u72b6\u614b\u306b\u306a\u308a\u307e\u3059\u3002\r\n\r\n### Adding custom boxes / \u30ab\u30b9\u30bf\u30e0\u30dc\u30c3\u30af\u30b9\u306e\u8ffd\u52a0\r\n\r\nHit \u201cAdd Box\u201d in the UI to define a host that isn\u2019t in your SSH config (for example, a throwaway Docker container). Fields you leave blank fall back to your SSH defaults.\r\n\r\n- **\u65e5\u672c\u8a9e:** UI \u306e \u201cAdd Box\u201d \u304b\u3089 SSH \u8a2d\u5b9a\u306b\u5b58\u5728\u3057\u306a\u3044\u30db\u30b9\u30c8\u3082\u8ffd\u52a0\u3067\u304d\u307e\u3059\uff08\u4f8b: \u4e00\u6642\u7684\u306a Docker \u30b3\u30f3\u30c6\u30ca\uff09\u3002\u672a\u5165\u529b\u306e\u9805\u76ee\u306f SSH \u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u5024\u304c\u4f7f\u308f\u308c\u307e\u3059\u3002\r\n\r\n### Security model (important)\r\n\r\n- sshler is designed for **single-user localhost** use. By default `sshler serve` binds to `127.0.0.1` and prints a random `X-SSHLER-TOKEN` that every state-changing request must send.\r\n- File uploads are capped at 50\u202fMB (tunable via `--max-upload-mb`). Uploaded content is never executed server-side.\r\n- SSH connections still honour your system `known_hosts`. Only set `known_hosts: ignore` if you fully understand the risk.\r\n- If you expose sshler beyond localhost, opt-in via `--allow-origin` and add `--auth user:pass` (basic auth). Use it only on networks you trust and put TLS in front (nginx, Caddy, etc.).\r\n- There is no telemetry, analytics, or call-home behaviour.\r\n\r\n### CLI options\r\n\r\n```bash\r\nsshler serve \\\r\n --host 127.0.0.1 \\\r\n --port 8822 \\\r\n --max-upload-mb 50 \\\r\n --allow-origin http://workstation:8822 \\\r\n --auth coder:supersecret \\\r\n --no-ssh-alias \\\r\n --log-level info\r\n```\r\n\r\n- `--host` (alias `--bind`) sets the bind address (default: `127.0.0.1` for localhost-only). Use `0.0.0.0` to expose on all interfaces, but **only on trusted networks with `--auth` and TLS**.\r\n- `--port` sets the port number (default: `8822`).\r\n- `--allow-origin` can be repeated to expand CORS; combine it with `--auth` if you expose the UI beyond localhost.\r\n- `--auth user:pass` enables HTTP basic authentication (recommended if binding to `0.0.0.0`).\r\n- `--max-upload-mb` sets the upload size limit (default: 50 MB).\r\n- `--no-ssh-alias` disables the `ssh -G` fallback when DNS fails.\r\n- `--token` lets you supply your own `X-SSHLER-TOKEN` (otherwise a secure random value is generated).\r\n- `--log-level` feeds directly into uvicorn (options: `critical`, `error`, `warning`, `info`, `debug`, `trace`).\r\n\r\nThe server prints the token (and, if enabled, the basic auth username) on startup so you can copy it into API clients or browser extensions.\r\n\r\n- **\u65e5\u672c\u8a9e:** \u30b5\u30fc\u30d0\u30fc\u8d77\u52d5\u6642\u306b\u30c8\u30fc\u30af\u30f3\uff08\u304a\u3088\u3073 Basic \u8a8d\u8a3c\u3092\u6709\u52b9\u306b\u3057\u305f\u5834\u5408\u306f\u30e6\u30fc\u30b6\u30fc\u540d\uff09\u3092\u8868\u793a\u3059\u308b\u306e\u3067\u3001API \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3084\u30d6\u30e9\u30a6\u30b6\u62e1\u5f35\u306b\u8cbc\u308a\u4ed8\u3051\u3066\u5229\u7528\u3067\u304d\u307e\u3059\u3002\r\n\r\n### Terminal notifications\r\n\r\n- Send a bell (`printf '\\a'`) from tmux or your shell to flash the browser title and raise a desktop notification whenever the sshler tab is hidden.\r\n- For richer messages use OSC 777: `printf '\\033]777;notify=Codex%20done|Check%20the%20output\\a'`. The text before the `|` becomes the title; the second part is the body.\r\n- JSON payloads are also supported: `printf '\\033]777;notify={\"title\":\"Codex\",\"message\":\"All tasks finished\"}\\a'`.\r\n- The first notification prompts the browser for permission. Denying it still leaves the in-app toast and title badge when you return to the tab.\r\n\r\n- **\u65e5\u672c\u8a9e:** \u30bf\u30d6\u304c\u975e\u8868\u793a\u306e\u3068\u304d\u306b `printf '\\a'` \u3067\u30d9\u30eb\u3092\u9001\u308b\u3068\u3001\u30d6\u30e9\u30a6\u30b6\u30bf\u30a4\u30c8\u30eb\u304c\u70b9\u6ec5\u3057\u30c7\u30b9\u30af\u30c8\u30c3\u30d7\u901a\u77e5\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002`printf '\\033]777;notify=\u30bf\u30a4\u30c8\u30eb|\u672c\u6587\\a'` \u3067\u4efb\u610f\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u9001\u4fe1\u3067\u304d\u3001JSON (`notify={\"title\":\"...\",\"message\":\"...\"}`) \u306b\u3082\u5bfe\u5fdc\u3057\u3066\u3044\u307e\u3059\u3002\u521d\u56de\u306f\u901a\u77e5\u8a31\u53ef\u306e\u30c0\u30a4\u30a2\u30ed\u30b0\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u304c\u3001\u62d2\u5426\u3057\u305f\u5834\u5408\u3067\u3082\u30bf\u30d6\u5fa9\u5e30\u6642\u306b\u30c8\u30fc\u30b9\u30c8\u3068\u30bf\u30a4\u30c8\u30eb\u306e\u30d0\u30c3\u30b8\u3067\u6c17\u4ed8\u3051\u307e\u3059\u3002\r\n\r\n## Autostart / \u81ea\u52d5\u8d77\u52d5\r\n\r\n### Windows (Task Scheduler)\r\n\r\n1. Run `where sshler` to locate the installed executable (for example, `%LOCALAPPDATA%\\Programs\\Python\\Python312\\Scripts\\sshler.exe`).\r\n2. Open **Task Scheduler \u2192 Create Task\u2026**.\r\n3. Under **Triggers**, add \u201cAt log on\u201d.\r\n4. Under **Actions**, choose \u201cStart a program\u201d and point to the `sshler.exe` path. Add arguments such as `serve --no-browser` and set **Start in** to a writable directory.\r\n5. Tick \u201cRun with highest privileges\u201d if you need WSL access, then save. sshler will now launch automatically every time you sign in.\r\n\r\n- **\u65e5\u672c\u8a9e:** Task Scheduler \u3067\u300c\u30ed\u30b0\u30aa\u30f3\u6642\u300d\u3092\u30c8\u30ea\u30ac\u30fc\u306b\u3057\u3001`sshler.exe serve --no-browser` \u3092\u5b9f\u884c\u3059\u308b\u30bf\u30b9\u30af\u3092\u4f5c\u6210\u3059\u308b\u3068\u3001\u30b5\u30a4\u30f3\u30a4\u30f3\u6642\u306b\u81ea\u52d5\u8d77\u52d5\u3057\u307e\u3059\u3002\r\n\r\n### Linux / macOS (systemd user service)\r\n\r\nCreate `~/.config/systemd/user/sshler.service`:\r\n\r\n```ini\r\n[Unit]\r\nDescription=sshler \u2013 local tmux bridge\r\nAfter=network.target\r\n\r\n[Service]\r\nType=simple\r\nExecStart=%h/.local/bin/sshler serve --bind 127.0.0.1 --no-browser\r\nRestart=on-failure\r\n\r\n[Install]\r\nWantedBy=default.target\r\n```\r\n\r\nReload and enable:\r\n\r\n```bash\r\nsystemctl --user daemon-reload\r\nsystemctl --user enable --now sshler.service\r\n```\r\n\r\n- **\u65e5\u672c\u8a9e:** \u4e0a\u8a18\u306e systemd \u30e6\u30fc\u30b6\u30fc\u30b5\u30fc\u30d3\u30b9\u3092\u4f5c\u6210\u3057\u3001`systemctl --user enable --now sshler.service` \u3092\u5b9f\u884c\u3059\u308b\u3068\u30b5\u30a4\u30f3\u30a4\u30f3\u6642\u306b\u81ea\u52d5\u8d77\u52d5\u3057\u307e\u3059\u3002\r\n\r\n### Dependencies & licenses\r\n\r\n- FastAPI, uvicorn, asyncssh, platformdirs, yaml (PyPI packages, permissive licenses)\r\n- HTMX (MIT) and xterm.js (MIT) are loaded from unpkg\r\n- CodeMirror (MIT) powers the editor\r\n\r\nAll assets are used under their respective MIT/BSD-style licenses. sshler itself ships under the MIT license.\r\n\r\n- **\u65e5\u672c\u8a9e:** \u4f9d\u5b58\u30e9\u30a4\u30d6\u30e9\u30ea\u306f\u3044\u305a\u308c\u3082\u5bdb\u5bb9\u306a\u30e9\u30a4\u30bb\u30f3\u30b9 (MIT/BSD) \u3067\u63d0\u4f9b\u3055\u308c\u3066\u3044\u307e\u3059\u3002sshler \u672c\u4f53\u3082 MIT \u30e9\u30a4\u30bb\u30f3\u30b9\u3067\u914d\u5e03\u3055\u308c\u307e\u3059\u3002\r\n\r\n## Why \"sshler\"?\r\n\r\nBecause sometimes you want less VS Code, more terminal \u2014 but still in a nice browser tab.\r\n\r\n---\r\n\r\n# \u65e5\u672c\u8a9e\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\r\n\r\nsshler \u306f\u30ed\u30fc\u30ab\u30eb\u5c02\u7528\u306e\u8efd\u91cf Web UI \u3067\u3001\u30ea\u30e2\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u3092 SFTP \u3067\u95b2\u89a7\u3057\u305f\u308a\u3001\u30d6\u30e9\u30a6\u30b6\u4e0a\u3067 tmux \u30bb\u30c3\u30b7\u30e7\u30f3\u306b\u63a5\u7d9a\u3057\u305f\u308a\u3067\u304d\u307e\u3059\u3002\u30ea\u30e2\u30fc\u30c8\u5074\u306b\u8ffd\u52a0\u30bd\u30d5\u30c8\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3059\u308b\u5fc5\u8981\u306f\u3042\u308a\u307e\u305b\u3093\u3002\r\n\r\n## \u7279\u5fb4\r\n\r\n- **\u30af\u30ed\u30b9\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0**: Windows 11\u3001macOS\u3001Linux \u3067\u52d5\u4f5c\uff08Python 3.12+ \u304c\u5fc5\u8981\uff09\r\n- **\u30ed\u30fc\u30ab\u30eb\u30ef\u30fc\u30af\u30b9\u30da\u30fc\u30b9**: \u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0\u3092\u95b2\u89a7\u3057\u3001\u30ea\u30e2\u30fc\u30c8\u30db\u30b9\u30c8\u3068\u4e26\u3079\u3066\u30cd\u30a4\u30c6\u30a3\u30d6\u306e tmux \u30bb\u30c3\u30b7\u30e7\u30f3\u3092\u8d77\u52d5\uff08Windows \u3067\u306f WSL tmux\u3001Linux/macOS \u3067\u306f\u30cd\u30a4\u30c6\u30a3\u30d6 tmux \u3092\u4f7f\u7528\uff09\r\n- **SSH \u7d71\u5408**: \u65e2\u5b58\u306e SSH \u9375\u3092\u4f7f\u7528\u3057\u3001OpenSSH \u30a8\u30a4\u30ea\u30a2\u30b9\u306b\u5bfe\u5fdc\r\n- **\u30d6\u30e9\u30a6\u30b6\u5185\u30bf\u30fc\u30df\u30ca\u30eb**: \u30ea\u30e2\u30fc\u30c8\u30db\u30b9\u30c8\u3067 `tmux new -As <session> -c <dir>` \u3092\u958b\u304d\u3001WebSocket + xterm.js \u7d4c\u7531\u3067\u63a5\u7d9a\r\n- **\u30d5\u30a1\u30a4\u30eb\u7ba1\u7406**: \u30d7\u30ec\u30d3\u30e5\u30fc\u3001\u7de8\u96c6\u3001\u524a\u9664\u3001\u300c\u3053\u3053\u3067\u30bf\u30fc\u30df\u30ca\u30eb\u3092\u958b\u304f\u300d\u6a5f\u80fd\u3092\u5099\u3048\u305f HTMX \u30d9\u30fc\u30b9\u306e\u30d5\u30a1\u30a4\u30eb\u30d6\u30e9\u30a6\u30b6\r\n- **\u81ea\u52d5\u8a2d\u5b9a**: \u521d\u56de\u8d77\u52d5\u6642\u306b\u30b9\u30bf\u30fc\u30bf\u30fc\u8a2d\u5b9a\u3092\u4f5c\u6210\r\n- **\u30a8\u30a4\u30ea\u30a2\u30b9\u89e3\u6c7a**: DNS \u5931\u6557\u6642\u306f `ssh -G` \u306b\u30d5\u30a9\u30fc\u30eb\u30d0\u30c3\u30af\u3002\u30ef\u30f3\u30af\u30ea\u30c3\u30af\u3067\u4e0a\u66f8\u304d\u3092\u30ea\u30bb\u30c3\u30c8\r\n- **\u30d5\u30a1\u30a4\u30eb\u64cd\u4f5c**: CodeMirror \u30a8\u30c7\u30a3\u30bf\u3067\u30d5\u30a1\u30a4\u30eb\u306e\u30d7\u30ec\u30d3\u30e5\u30fc\u3001\u7de8\u96c6\uff08256 KB \u4ee5\u4e0b\uff09\u3001\u524a\u9664\u304c\u53ef\u80fd\r\n- **\u30d0\u30a4\u30ea\u30f3\u30ac\u30eb UI**: \u82f1\u8a9e\u3068\u65e5\u672c\u8a9e\u306e\u5b8c\u5168\u30b5\u30dd\u30fc\u30c8\r\n\r\n## \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\r\n\r\n### PyPI\uff08\u63a8\u5968\uff09\r\n\r\n```bash\r\npip install sshler\r\n\r\n# \u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u3068 systemd/\u30b5\u30fc\u30d3\u30b9\u30a2\u30bb\u30c3\u30c8\u3092\u4f5c\u6210\u3059\u308b\u305f\u3081\u4e00\u5ea6\u8d77\u52d5\r\nsshler serve\r\n```\r\n\r\nPython **3.12+** \u304c\u5fc5\u8981\u3067\u3059\u3002\r\n\r\n### \u958b\u767a\u7528\r\n\r\n```bash\r\nuv pip install -e .\r\n# \u307e\u305f\u306f: pip install -e .\r\n```\r\n\r\n\u30ea\u30dd\u30b8\u30c8\u30ea\u3092\u30af\u30ed\u30fc\u30f3\u3057\u305f\u5f8c\u3001dev extras \u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3057\u3066\u901a\u5e38\u306e\u30c4\u30fc\u30eb\u3092\u5b9f\u884c\uff1a\r\n\r\n```bash\r\nuv sync --group dev\r\nuv run ruff check .\r\nuv run pytest\r\n```\r\n\r\n## \u5b9f\u884c\r\n\r\n```bash\r\nsshler serve\r\n```\r\n\r\n\u30c7\u30d5\u30a9\u30eb\u30c8\u30d6\u30e9\u30a6\u30b6\u3067 `http://127.0.0.1:8822` \u304c\u958b\u304d\u307e\u3059\u3002\r\n\r\n## \u8a2d\u5b9a\r\n\r\nsshler \u306f\u65e2\u5b58\u306e OpenSSH \u8a2d\u5b9a\uff08`~/.ssh/config`\uff09\u3092\u8aad\u307f\u53d6\u308a\u3001\u3059\u3079\u3066\u306e\u5177\u4f53\u7684\u306a `Host` \u30a8\u30f3\u30c8\u30ea\u3092\u81ea\u52d5\u7684\u306b\u8868\u793a\u3057\u307e\u3059\u3002UI \u3092\u901a\u3058\u3066\u8ffd\u52a0\u3057\u305f\u304a\u6c17\u306b\u5165\u308a\u3001\u30c7\u30d5\u30a9\u30eb\u30c8\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3001\u30ab\u30b9\u30bf\u30e0\u30db\u30b9\u30c8\u306f\u3001\u4ed8\u5c5e\u306e YAML \u30d5\u30a1\u30a4\u30eb\u306b\u4fdd\u5b58\u3055\u308c\u307e\u3059\u3002\r\n\r\n\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306f\u521d\u56de\u5b9f\u884c\u6642\u306b\u4f5c\u6210\u3055\u308c\u307e\u3059\uff1a\r\n\r\n- Windows: `%APPDATA%\\sshler\\boxes.yaml`\r\n- macOS/Linux: `~/.config/sshler/boxes.yaml`\r\n\r\n\u4f8b\uff1a\r\n\r\n```yaml\r\nboxes:\r\n - name: gabu-server\r\n host: example.tailnet.ts.net\r\n ssh_alias: gabu-server\r\n user: gabu\r\n port: 22\r\n keyfile: \"C:/Users/gabu/.ssh/id_ed25519\"\r\n favorites:\r\n - /home/gabu\r\n - /home/gabu/projects\r\n - /srv/codex\r\n default_dir: /home/gabu\r\n```\r\n\r\n> \u30d2\u30f3\u30c8: \u30db\u30fc\u30e0\u30d1\u30b9\u304c `/home/<user>` \u3067\u306a\u3044\u5834\u5408\u306f `default_dir` \u3092\u8a2d\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002OpenSSH \u30a8\u30a4\u30ea\u30a2\u30b9\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\u306f `ssh_alias:` \u3092\u8ffd\u52a0\u3059\u308b\u3068\u3001DNS \u5931\u6557\u6642\u306b `ssh -G` \u3067\u89e3\u6c7a\u3057\u307e\u3059\u3002\r\n\r\n## \u540d\u524d\u306e\u7531\u6765\r\n\r\nVS Code \u3060\u3051\u306b\u983c\u3089\u305a\u3001\u30d6\u30e9\u30a6\u30b6\u30bf\u30d6\u306e\u4e2d\u3067\u8efd\u5feb\u306b\u30bf\u30fc\u30df\u30ca\u30eb\u3092\u6271\u3044\u305f\u3044\u2500\u2500\u305d\u3093\u306a\u9858\u3044\u304b\u3089\u3053\u306e\u540d\u524d\u306b\u306a\u308a\u307e\u3057\u305f\u3002\r\n",
"bugtrack_url": null,
"license": null,
"summary": "A local FastAPI-powered SSH multiplexer for tmux-in-browser \u2014 from your laptop only.",
"version": "0.3.3",
"project_urls": {
"Homepage": "https://github.com/gabu-quest/sshler",
"Issues": "https://github.com/gabu-quest/sshler/issues",
"Repository": "https://github.com/gabu-quest/sshler"
},
"split_keywords": [
"ssh",
" tmux",
" fastapi",
" sftp",
" htmx"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "c8140df6f6408e6ff018ad8bacce5f8888c0a69d73b659a10e00067cbde80088",
"md5": "06083d7e76c045ae23ccdc21c917f0ab",
"sha256": "27d345f41a1e839c6b59906134c3eed0e776801ef38f105f078d95feea03fb9f"
},
"downloads": -1,
"filename": "sshler-0.3.3-py3-none-any.whl",
"has_sig": false,
"md5_digest": "06083d7e76c045ae23ccdc21c917f0ab",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.12",
"size": 59109,
"upload_time": "2025-10-22T19:30:40",
"upload_time_iso_8601": "2025-10-22T19:30:40.741781Z",
"url": "https://files.pythonhosted.org/packages/c8/14/0df6f6408e6ff018ad8bacce5f8888c0a69d73b659a10e00067cbde80088/sshler-0.3.3-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "1757fb0c31fe078d6e89744528cbf7d610ce0332f7907e0d60c8d52aefd8d6fb",
"md5": "b4258dd009b7b7776573be7f22b7d501",
"sha256": "171b8fcea77eabe5e2aead266f120cab021fc18e2964a2576ea1029aa9d9ea54"
},
"downloads": -1,
"filename": "sshler-0.3.3.tar.gz",
"has_sig": false,
"md5_digest": "b4258dd009b7b7776573be7f22b7d501",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.12",
"size": 61986,
"upload_time": "2025-10-22T19:30:42",
"upload_time_iso_8601": "2025-10-22T19:30:42.253441Z",
"url": "https://files.pythonhosted.org/packages/17/57/fb0c31fe078d6e89744528cbf7d610ce0332f7907e0d60c8d52aefd8d6fb/sshler-0.3.3.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-22 19:30:42",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "gabu-quest",
"github_project": "sshler",
"github_not_found": true,
"lcname": "sshler"
}