You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: SQLite-backed durable state + refresh tokens (v1.2.0)
Durable state moves to a single SQLite database (rusqlite, bundled — still a
single binary) at /var/lib/mcp-ssh/mcp-ssh.db, in a hybrid model: SQLite holds
the low-frequency structured state, while high-frequency live job output keeps
streaming to per-job log files.
OAuth (src/oauth, src/db):
- access + refresh tokens persisted in SQLite, so logins survive a restart
(the agent self-updates by restarting its own service — previously that
logged every client out). Access tokens 24h, refresh tokens 1 year, rotated
on use (RFC 6749 §10.4). grant_type=refresh_token added + advertised; the
/token response returns a refresh_token. Many tokens coexist → multiple
clients (desktop, mobile, CLI) stay authenticated independently.
Jobs (src/jobs):
- job metadata + a bounded output tail persisted to SQLite, so job(list) and
poll survive restarts; live output still streams to log files under
/var/lib/mcp-ssh/logs/jobs. poll falls back to the saved tail when the live
log is gone. Reaper runs on startup AND hourly: drops >24h jobs (rows +
files), trims finished logs to a tail (5000 lines <3h, 500 after), mtime-ages
orphan files, and marks jobs stuck 'running' across a restart as failed.
- job ids/log filenames now use '-' for the time (job-23-30-07), not ':' —
colon-free is portable everywhere (Windows/scp/globbing).
- fix: poll() self-deadlocked on a MutexGuard held to the end of an `if let`
block (temporary-in-scrutinee); bind the lookup to a local first.
config: db_path (MCP_SSH_DB) decoupled from the job-log dir.
Built in parallel by three agents (oauth, jobs, docs) + integration here.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>