Security Review Report
Review date: 2026-07-12
Review scope: target refactor release gate (cloudssh.room.v1, SQLite, SSH, HTTP,
WebSocket, browser distribution, automation, and operator documentation).
Deployment classification: Trusted self-hosted single-node service for one
trusted team.
Decision
Decision: ACCEPTED for the documented Trusted self-hosted model only.
The product is not accepted as a hostile-user isolation boundary, a public
signup service, or a hosted multi-tenant execution platform. LocalPtyRuntime
starts every room with the service Unix account and shares that account’s host
filesystem, process permissions, environment, network reachability, and kernel.
A separately specified and reviewed isolated runtime is mandatory before any of
those deployment claims can change.
Review Checklist
| Area | Verification and release evidence | Result |
|---|---|---|
| Runtime trust | docs/security.md, configuration guidance, and the operations runbook warn that LocalPtyRuntime is trusted local execution, not sandboxing. | Pass |
| Input confidentiality | Actor tests verify raw input is absent from durable event values and Debug output. The log gate rejects terminal-input fields. Echoed shell output remains documented as durable data. | Pass |
| Request logging | scripts/security-check.py parses every Rust tracing invocation, rejects secret/query-bearing fields, and verifies middleware records Uri::path() rather than the query string. | Pass |
| Metrics cardinality | The same gate extracts metric label keys and enforces the fixed allowlist phase, outcome, transport, reason, and kind; identity and room labels are rejected. | Pass |
| Browser artifact secrets | make security-check builds and scans every file in web/dist for private keys, JWTs, bearer values, and enabled Cloud SSH operator secrets. | Pass |
| Sessions and providers | Opaque session digests, CSRF/origin validation, bounded provider calls, HTTPS endpoint validation, stable-subject identity, and secret-reference redaction are covered by focused tests. | Pass |
| SSH identity | Stable operator-owned host keys are required. Public development host-key generation helpers are prohibited by the repository gate. | Pass |
| Persistence failure | Disk-full and database-busy tests fail the journal closed. Corrupt/unsupported snapshot acceptance tests reject the snapshot while preserving output segments. | Pass |
| Load and bounds | make load-check has hard process and scenario deadlines and verifies concurrent input ordering, a 32-client output fanout, a 128 KiB-per-client burst, and a 1 MiB SQLite flush to zero durable lag. | Pass |
| Schema and residue | The gate requires one squashed SQLite migration, no ignored Rust tests, no POC source files, no compatibility marker, and no public test/development helper. | Pass |
| Backup and recovery | Backup/restore tests preserve terminal generations, output, and snapshots; the operator runbook covers upgrade, restore, retention, disk-full, busy database, and corrupt snapshots. | Pass |
| Dependency advisory gate | CI and release workflows run rustsec/audit-check against the committed lockfile, and pull requests run GitHub dependency review. Known vulnerabilities block validation; exceptions require an explicit reviewed report change. | Pass |
Automated Evidence
Run from a clean checkout with the locked dependency graphs:
make security-check
make load-check
cargo audit --deny warnings
make production-check
make security-check is not a declaration-only checklist. It inspects tracing
macro fields, request-path logging, metrics labels, the generated frontend
artifacts, migration count, ignored tests, source residue, and this signed-off
review evidence. make load-check executes the production actor and SQLite
journal paths with deterministic fixtures and explicit failure thresholds.
Residual Risks And Operating Conditions
- Terminal output and snapshots are plaintext in SQLite and may contain secrets echoed by a shell. Disk and backup encryption are operator responsibilities.
- One compromised room process has the Unix permissions of the Cloud SSH service account. Run it as a dedicated unprivileged account/container and do not mix hostile users.
- SQLite is a single-node durability boundary. Operators must alert on journal failure and durable lag and must rehearse restore procedures.
- Provider availability and identity correctness depend on operator-controlled provider configuration. Redirects are disabled and response/time limits are enforced, but provider compromise remains outside Cloud SSH’s boundary.
- Advisory databases evolve after a release. Re-run the dependency advisory gate for every lockfile change and before deploying an old revision.
Any expansion beyond these conditions invalidates this acceptance decision and requires a new architecture and security review.