Configuration
Cloud SSH is configured through CLI flags and environment variables. The metadata store is SQLite by default.
Service Startup
Run the HTTP service with defaults:
cargo run -p cloud-ssh-server --locked -- serve
The default HTTP listener is 127.0.0.1:8080. Override it with:
cargo run -p cloud-ssh-server --locked -- serve --http-addr 127.0.0.1:9000
Select the single-tenant deployment id before hosted tenant routing is enabled:
cargo run -p cloud-ssh-server --locked -- serve --tenant-id default
For persistent deployments, set DATABASE_URL to a SQLite database on a
backed-up volume:
DATABASE_URL=sqlite:///var/lib/cloud-ssh/cloud-ssh.db?mode=rwc \
cargo run -p cloud-ssh-server --locked -- serve
Cloud SSH enables foreign keys, a busy timeout, WAL journaling, and
synchronous=NORMAL on SQLite connections.
/health and /healthz are credential-free liveness checks. /ready returns
ready only when the metadata store is reachable, embedded migrations are fully
applied without blocking status, and session cryptography is configured.
HTTP Route Layout
Product APIs are mounted under /api:
| Prefix | Purpose |
|---|---|
/api/auth/v1 | login, browser session, logout, SSH keys |
/api/admin/v1 | organization, project, identity, audit APIs |
/api/room/v1 | browser room WebSocket, events, snapshots |
Operations endpoints stay outside /api:
| Path | Purpose |
|---|---|
/health | liveness |
/healthz | liveness alias |
/ready | metadata, migration, session crypto |
/metrics | Prometheus metrics |
SSH Adapter
Enable the classic SSH listener with a stable OpenSSH host key:
ssh-keygen -t ed25519 -f ./cloud-ssh-host-ed25519 -N ''
cargo run -p cloud-ssh-server --locked -- serve \
--ssh-addr 127.0.0.1:2222 \
--ssh-host-key ./cloud-ssh-host-ed25519
Equivalent environment variables:
CLOUD_SSH_SSH_ADDR=0.0.0.0:2222
CLOUD_SSH_SSH_HOST_KEY=/var/lib/cloud-ssh/ssh_host_ed25519
The host key must be stable and operator managed. Do not generate a random host key on each service start.
Browser Sessions
Configure browser login redirects with a public base URL and a 32-byte or longer session HMAC key:
CLOUD_SSH_PUBLIC_BASE_URL=https://cloud.example.com \
CLOUD_SSH_SESSION_HMAC_KEY=0123456789abcdef0123456789abcdef \
cargo run -p cloud-ssh-server --locked -- serve
Session and CSRF cookies use Secure, HttpOnly for the session cookie, and
SameSite=Lax by default. For browser login over local plain HTTP only, set:
CLOUD_SSH_ALLOW_INSECURE_COOKIES=true
Room Runtime
Override the local room PTY shell when the default system shell is not the desired runtime:
CLOUD_SSH_ROOM_SHELL=/bin/bash \
cargo run -p cloud-ssh-server --locked -- serve
Keep detached room PTYs alive longer for cross-device resume:
CLOUD_SSH_ROOM_IDLE_TTL_SECONDS=3600 \
cargo run -p cloud-ssh-server --locked -- serve
Limit in-process room runtime growth for single-node deployments:
CLOUD_SSH_MAX_LIVE_ROOMS=128 \
CLOUD_SSH_MAX_CLIENTS_PER_ROOM=32 \
CLOUD_SSH_MAX_TOTAL_CLIENTS=512 \
cargo run -p cloud-ssh-server --locked -- serve
Identity Providers
Provider client_secret_ref values are resolved from the process environment
with env:VARIABLE_NAME references, for example
env:CLOUD_SSH_GITHUB_CLIENT_SECRET.
Configure the default GitHub OAuth App provider:
cargo run -p cloud-ssh-server --locked -- identity provider upsert-github \
--client-id "$GITHUB_CLIENT_ID" \
--client-secret-ref env:CLOUD_SSH_GITHUB_CLIENT_SECRET
Configure a generic OIDC provider from its discovery document:
cargo run -p cloud-ssh-server --locked -- identity provider upsert-oidc \
--discovery-url https://idp.example.com/.well-known/openid-configuration \
--client-id "$OIDC_CLIENT_ID" \
--client-secret-ref env:CLOUD_SSH_OIDC_CLIENT_SECRET
OIDC scopes default to openid profile email. Pass --scopes when an IdP
requires a group or custom claim scope; the configured value must include
openid:
cargo run -p cloud-ssh-server --locked -- identity provider upsert-oidc \
--discovery-url https://idp.example.com/.well-known/openid-configuration \
--client-id "$OIDC_CLIENT_ID" \
--client-secret-ref env:CLOUD_SSH_OIDC_CLIENT_SECRET \
--scopes "openid profile email groups"
Explicit endpoint arguments can override discovery values for providers with non-standard routing:
cargo run -p cloud-ssh-server --locked -- identity provider upsert-oidc \
--issuer-url https://idp.example.com \
--authorization-endpoint https://idp.example.com/oauth2/authorize \
--token-endpoint https://idp.example.com/oauth2/token \
--jwks-endpoint https://idp.example.com/oauth2/jwks \
--client-id "$OIDC_CLIENT_ID" \
--client-secret-ref env:CLOUD_SSH_OIDC_CLIENT_SECRET
Provider issuer and endpoint URLs must be absolute HTTP(S) URLs without credentials or fragments. Remote providers must use HTTPS. Plain HTTP is accepted only for localhost or loopback development fixtures.
Add an active allowlist rule before new identity-provider subjects can provision local Cloud SSH principals:
cargo run -p cloud-ssh-server --locked -- identity allowlist upsert \
--id rule-example-domain \
--rule-type email_domain \
--value example.com \
--project default \
--role developer
The GitHub login path supports email, email_domain, github_user,
github_organization, and bootstrap_owner allowlist rules. The OIDC login
path supports email, email_domain, oidc_group, oidc_claim, and
bootstrap_owner.
Bootstrap And Targets
Create the default tenant, project, owner principal, prod room, prod target
alias, and a Cloud SSH user public key:
cargo run -p cloud-ssh-server --locked -- bootstrap \
--ssh-public-key-file ~/.ssh/id_ed25519.pub
With the SSH listener enabled, the default target is reachable as:
ssh prod@<cloud-ssh-host>
Create another local room and alias after bootstrap:
cargo run -p cloud-ssh-server --locked -- room create dev --target-alias dev
cargo run -p cloud-ssh-server --locked -- target alias add ops --room prod
Room and target alias commands print a fully qualified
<organization>.<project>.<alias> SSH selector that is always safe to copy,
plus the short <alias> selector when that project is the user’s default
project.
Migrations
Validate embedded metadata migrations without connecting to the database:
cargo run -p cloud-ssh-server --locked -- migrate check
Inspect the selected database migration table without applying migrations:
cargo run -p cloud-ssh-server --locked -- migrate status
Apply metadata migrations to the default SQLite database:
cargo run -p cloud-ssh-server --locked -- migrate up
migrate up and service or management commands that apply migrations first
inspect the migration table. They refuse to continue when failed, unknown, or
checksum-mismatched migration rows are present; pending embedded migrations are
the only migratable state.
Observability
GET /metrics exposes Prometheus text counters without browser session
credentials, but requires metrics authorization by default.
CLOUD_SSH_METRICS_BEARER_TOKEN=0123456789abcdef0123456789abcdef
Configured metrics tokens must be at least 32 bytes and contain visible ASCII characters without whitespace. For protected internal scrape networks only, opt in to anonymous metrics access:
CLOUD_SSH_ALLOW_UNAUTHENTICATED_METRICS=true
Cloud SSH keeps plain fmt logs enabled by default and exports OpenTelemetry
traces over OTLP HTTP/protobuf when OTEL_EXPORTER_OTLP_ENDPOINT or
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT is set:
RUST_LOG=cloud_ssh_server=debug,cloud_ssh=debug \
OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318 \
cargo run -p cloud-ssh-server --locked -- serve
Retention And Maintenance
Scheduled retention runs every hour by default. It keeps the latest 10,000 room events per room, 10 screen snapshots per room, and 100,000 audit events per tenant.
CLOUD_SSH_RETENTION_INTERVAL_SECONDS=3600
CLOUD_SSH_RETAIN_ROOM_EVENTS=10000
CLOUD_SSH_RETAIN_ROOM_SNAPSHOTS=10
CLOUD_SSH_RETAIN_AUDIT_EVENTS=100000
Set CLOUD_SSH_RETENTION_INTERVAL_SECONDS=0 to disable scheduled retention and
rely on manual maintenance.
Run the same retention pass manually:
cargo run -p cloud-ssh-server --locked -- maintenance prune \
--retain-room-events 10000 \
--retain-room-snapshots 10 \
--retain-audit-events 100000
Write a SQLite metadata backup and rehearse opening a restored copy:
cargo run -p cloud-ssh-server --locked -- maintenance backup \
--output /var/backups/cloud-ssh/cloud-ssh-$(date +%Y%m%d%H%M%S).db
cargo run -p cloud-ssh-server --locked -- maintenance restore-check \
--input /var/backups/cloud-ssh/cloud-ssh-20260626120000.db \
--restore-to /tmp/cloud-ssh-restore-check.db
Rate Limits And Proxy Headers
High-risk entrypoints are rate limited by sliding windows:
CLOUD_SSH_RATE_LIMIT_WINDOW_SECONDS=60
CLOUD_SSH_LOGIN_RATE_LIMIT=20
CLOUD_SSH_CALLBACK_RATE_LIMIT=60
CLOUD_SSH_SSH_AUTH_RATE_LIMIT=120
CLOUD_SSH_ROOM_ATTACH_RATE_LIMIT=120
CLOUD_SSH_RATE_LIMIT_MAX_KEYS=100000
Set CLOUD_SSH_TRUST_PROXY_HEADERS=true only when Cloud SSH is behind a
trusted reverse proxy that controls client address headers.
Outbound identity-provider HTTP calls use a bounded 10 second timeout by default. Tune it with:
CLOUD_SSH_PROVIDER_HTTP_TIMEOUT_SECONDS=10
Docker Runtime
Build and smoke-test the local service image:
make docker-build
make docker-smoke
Run the image with a persistent SQLite volume:
docker run --rm \
-p 8080:8080 \
-p 2222:2222 \
-v cloud-ssh-data:/var/lib/cloud-ssh \
-e CLOUD_SSH_SESSION_HMAC_KEY=0123456789abcdef0123456789abcdef \
-e CLOUD_SSH_SSH_ADDR=0.0.0.0:2222 \
-e CLOUD_SSH_SSH_HOST_KEY=/var/lib/cloud-ssh/ssh_host_ed25519 \
cloud-ssh:dev
The deployment environment template is .env.example.