Security

How Gateway authenticates you, protects SSH credentials at rest, and keeps one customer's data unreachable from another's session.

Authentication

Gateway holds no password and no passkey. Every sign-in is an OAuth2 Authorization Code flow with PKCE against an identity provider you choose; Gateway only issues its own session cookie afterward. There is no local credential to leak, phish, or brute force, because there is no local credential.

Account recovery is entirely your identity provider's responsibility. If you lose access to every linked identity provider, Gateway has no separate recovery path. This is a deliberate trade-off for not holding a credential in the first place, not an oversight.

A second identity provider reporting the same email address never attaches to an existing account automatically. Linking a new sign-in method is always an explicit action taken from a signed-in session.

Session handling

Session cookies are __Host- prefixed, HttpOnly, Secure, and SameSite=Lax. Only a SHA-256 hash of the session token is stored; the token itself exists solely in your browser. Sessions expire after 7 days of inactivity or 30 days regardless of activity, whichever comes first, and can be revoked at any time.

Sensitive actions (rotating or revoking an SSH identity, removing a linked identity, deleting a server) require a step-up reauthentication: a fresh round trip through your identity provider within the last 10 minutes. A stale session cannot perform them, even with a valid cookie.

Credential encryption

Each server's SSH private key is protected by envelope encryption. A per-credential data key encrypts the private key with AES-256-GCM; that data key is itself encrypted under a versioned platform key that exists only as a secret on the Credentials Worker. The additional authenticated data on both encryption layers binds the ciphertext to its credential, server, generation, and key version, so a swapped database row or a replayed storage object fails to decrypt rather than silently succeeding.

The encrypted key material is stored in a dedicated R2 bucket that only the Credentials Worker can reach. The API Worker your browser talks to never holds the platform key and never touches that bucket directly.

How a request moves through Gateway The browser reaches the website and the app through Cloudflare Pages, and reaches the API through the API Worker. The API Worker reads and writes D1 and the customer files R2 bucket directly, and reaches the Credentials Worker only through an internal service binding. The Credentials Worker is the only component with access to the platform KEK and the credentials R2 bucket. A separate, publicly reachable executor lease endpoint on the Credentials Worker is authenticated by machine credentials only, never by a browser session cookie. Browser Cloudflare Pages website + app API Worker /api/*, /auth/* D1 accounts, servers R2 customer files Credentials Worker holds the platform KEK R2 encrypted SSH keys Executors (future) service binding lease, machine auth only
The API Worker never holds the platform KEK. Every credential operation crosses the service binding into the Credentials Worker, the only component that can decrypt an SSH private key.

Access boundaries

Every resource lookup is scoped to the authenticated account. Requesting a server or file that belongs to someone else returns the same "not found" response as a resource that does not exist at all, rather than confirming it exists but is forbidden.

The credential lease endpoint that a future executor fleet will call is reachable only on its own hostname, authenticated by a machine credential, and rejects any request carrying a browser session cookie. It cannot be reached through the browser-facing API at all.

What this deployment does not yet do

Gateway does not open SSH connections, execute commands, or perform SSH host-key verification, because the execution layer that would do those things is not part of this deployment. Rotating an SSH identity generates a new keypair and revokes the old one in Gateway; installing the new public key on the remote host is a manual step until an executor exists to automate it.