Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,35 @@ Useful launcher options:
STANDTERM_HOST=127.0.0.1 STANDTERM_PORT=5000 ./run.sh
```

## Browser-managed SSH Sessions And Keys

Quick Connect can load saved SSH profiles and the six most recent successful
SSH targets. Use **Settings > SSH Sessions** to create, update, reorder, or
delete profiles and to clear history. Profiles and history stay in the current
browser and never store passwords.

A saved profile can explicitly generate an Ed25519 key with **Use browser key
authentication**. The private `CryptoKey` is non-extractable and stays in that
browser's IndexedDB. Copy the displayed OpenSSH public key to the remote
account's `~/.ssh/authorized_keys`, then select the exact saved profile in Quick
Connect. **Use key** remains optional, even when the profile has a key. Browser
key authentication is allowed only from loopback or an authorized HTTPS browser.

During authentication, Python sends the SSH challenge to the initiating browser
and receives only its Ed25519 signature; the private key is never sent to the
StandTerm Python process. A changed host, port, or username disables the profile
key binding. Deleting or unlinking a keyed profile permanently deletes that
browser key. These keys are protected from export, but they are not hardware
keys: script running in the same browser origin could still request signatures.

**Settings > General > Import & Export** transfers browser preferences, SSH
profiles and order, SSH history, and persistent UI layout in a versioned JSON
envelope containing a Base64 ZIP archive. Import merges profiles by stable ID,
appends new IDs, and deduplicates history. A local keyed profile keeps its local
host, port, and username so import cannot silently rebind its key. SSH keys, key
IDs, passwords, browser authorization identity, access tokens, and runtime
diagnostics are never included or changed by import.

## Browser Authorization And HTTPS

When StandTerm listens on a non-loopback address, HTTPS is enabled by default so
Expand Down
328 changes: 293 additions & 35 deletions app.py

Large diffs are not rendered by default.

25 changes: 11 additions & 14 deletions run.bat
Original file line number Diff line number Diff line change
Expand Up @@ -168,23 +168,20 @@ if errorlevel 1 (
)
)

call :dependencies_available
if errorlevel 1 (
echo [*] Python dependencies are missing or unavailable; dependency check will run.
set "FORCE_RECHECK=true"
)

if "%FORCE_RECHECK%"=="false" if exist "%INSTALLED_FLAG%" (
call :stamp_matches
if errorlevel 1 (
echo [*] Dependency stamp is stale; dependency check will run.
set "FORCE_RECHECK=true"
)
)

REM Consult the install stamp before running the dependency checker. The stamp
REM records the runtime kind, the Python version, and the requirements.txt hash,
REM so a match means this runtime already satisfies requirements.txt. The checker
REM imports paramiko and eventlet, which the server itself imports lazily or not
REM at all, so running it on every warm start costs seconds for no new information.
if "%FORCE_RECHECK%"=="true" goto :install_deps
if not exist "%INSTALLED_FLAG%" goto :install_deps

call :stamp_matches
if errorlevel 1 (
echo [*] Dependency stamp is stale; dependency check will run.
goto :install_deps
)

echo [*] Skipping dependency check (valid flag exists^).
echo [*] Hint: Use 'run.bat --force' to re-check.
exit /b 0
Expand Down
28 changes: 14 additions & 14 deletions run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -316,18 +316,21 @@ if ! python_is_usable python; then
venv_activation_is_current || exit 1
fi

echo "[*] Checking Python dependencies..."
if ! verify_dependencies; then
echo "[*] Python dependencies are missing or unavailable; dependency check will run."
FORCE_RECHECK=true
fi
if [ "$FORCE_RECHECK" = false ] && [ -f "$INSTALLED_FLAG" ] && ! install_stamp_matches; then
echo "[*] Dependency stamp is stale; dependency check will run."
FORCE_RECHECK=true
fi

# 3. Check and install dependencies
if [ "$FORCE_RECHECK" = true ] || ! install_stamp_matches; then
# Consult the install stamp before importing anything. The stamp records the
# platform, the Python version, and the requirements.txt hash, so a match means
# this venv already satisfies requirements.txt. The import verification below
# loads paramiko and eventlet, which the server itself imports lazily or not at
# all, and on a /mnt or network filesystem that costs several seconds on every
# warm start.
if [ "$FORCE_RECHECK" = false ] && install_stamp_matches; then
echo "[*] Skipping dependency check (flag exists)."
echo "[*] Hint: Use './run.sh --force' or delete '$INSTALLED_FLAG' to re-check."
else
if [ "$FORCE_RECHECK" = false ] && [ -f "$INSTALLED_FLAG" ]; then
echo "[*] Dependency stamp is stale; dependency check will run."
fi
echo "[*] Checking Python dependencies..."
rm -f "$INSTALLED_FLAG"
ensure_venv_pip || exit 1
if [ -f "$REQ_FILE" ]; then
Expand All @@ -347,9 +350,6 @@ if [ "$FORCE_RECHECK" = true ] || ! install_stamp_matches; then
echo " Fix the package error above, then rerun ./run.sh --force."
exit 1
fi
else
echo "[*] Skipping dependency check (flag exists)."
echo "[*] Hint: Use './run.sh --force' or delete '$INSTALLED_FLAG' to re-check."
fi

# 4. Start the server
Expand Down
14 changes: 5 additions & 9 deletions run_at_wsl+screen.bat
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,16 @@ if errorlevel 1 (
exit /b 1
)

wsl.exe --cd "%PROJECT_DIR%" bash -lc "command -v screen >/dev/null 2>&1"
if errorlevel 1 (
echo [!] ERROR: screen is required inside WSL but was not found.
echo Install with: sudo apt install screen
pause
exit /b 1
)

REM The screen check runs inside the launch command below instead of in its own
REM wsl.exe round trip. Each wsl.exe invocation costs a WSL entry, and several
REM seconds when the distro is not already running, so asking one question is
REM not worth a separate trip.
echo [*] Starting or attaching WSL screen session: %SCREEN_NAME%
echo [*] Reattach from Windows with:
echo wsl.exe screen -r standterm
echo [*] Force reattach with:
echo wsl.exe screen -d -r standterm
wsl.exe --cd "%PROJECT_DIR%" bash -lc "export STANDTERM_RESTART_PATH='%STANDTERM_RESTART_PATH%'; chmod +x ./run.sh; screen -wipe standterm >/dev/null 2>&1 || true; if screen -ls | grep -Eq '[[:space:]][0-9]+\.standterm[[:space:]].*\(Detached\)'; then exec screen -r standterm; fi; if screen -ls | grep -Eq '[[:space:]][0-9]+\.standterm[[:space:]].*\(Attached\)'; then exec screen -d -r standterm; fi; exec screen -S standterm bash -lc 'exec ./run.sh %*'"
wsl.exe --cd "%PROJECT_DIR%" bash -lc "command -v screen >/dev/null 2>&1 || { echo '[!] ERROR: screen is required inside WSL but was not found.'; echo ' Install with: sudo apt install screen'; exit 1; }; export STANDTERM_RESTART_PATH='%STANDTERM_RESTART_PATH%'; chmod +x ./run.sh; screen -wipe standterm >/dev/null 2>&1 || true; if screen -ls | grep -Eq '[[:space:]][0-9]+\.standterm[[:space:]].*\(Detached\)'; then exec screen -r standterm; fi; if screen -ls | grep -Eq '[[:space:]][0-9]+\.standterm[[:space:]].*\(Attached\)'; then exec screen -d -r standterm; fi; exec screen -S standterm bash -lc 'exec ./run.sh %*'"
if errorlevel 1 (
pause
exit /b 1
Expand Down
Loading