Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

PrefixPurpose
/api/auth/v1login, browser session, logout, SSH keys
/api/admin/v1organization, project, identity, audit APIs
/api/room/v1browser room WebSocket, events, snapshots

Operations endpoints stay outside /api:

PathPurpose
/healthliveness
/healthzliveness alias
/readymetadata, migration, session crypto
/metricsPrometheus 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.