Skip to main content
You run upsun ssh, and a second later you have a shell inside your production container. You never uploaded a public key. The container has no authorized_keys file with your name in it. What let you in? The answer is an SSH certificate. A few years ago we wrote about how Platform.sh provisions these certificates: the CLI generates a key pair, sends the public key to a service called Certifier, and gets back a certificate that your SSH client presents automatically. That article covered how the certificate is issued. This one covers the other side: what’s inside the certificate, and what the platform does with it when you connect.

Read your own certificate

The certificate sits in your CLI session directory, and ssh-keygen decodes it:
Two things stand out before you even look at the extensions. The Key ID is your user ID, the same UUID the API knows you by. And the validity window is about 15 minutes. This certificate expires before your coffee gets cold, and the CLI transparently requests a new one when it does. That short lifetime is the first layer of security: a stolen certificate is a paperweight within minutes.

The extensions carry your permissions

The permit-* entries are standard OpenSSH extensions. The @platform.sh ones are ours, and they turn the certificate from an identity document into an authorization document. One of them is a flag: has-mfa@platform.sh says your session was authenticated with multi-factor authentication. Its presence alone is the signal; it carries no value. The rest are data. access@platform.sh embeds a permission map, a JSON document describing what your session can touch, resource by resource:
We call this an access document. The keys are selectors: a bare value matches a resource ID, and org= matches every resource in an organization. Read this one out loud and it’s a user profile: member of one organization, admin on one project, view access on every other project the organization owns. Selectors can even carry auth requirements, so abcdefgh1234567&amr=mfa grants its permissions only to MFA-verified sessions. access-id@platform.sh is an identifier for the same document server-side, which matters later. token-id@platform.sh names the OAuth 2 token your session is built on. And token-claims@platform.sh embeds claims from that token:
If you’ve worked with OpenID Connect, amr and auth_time look familiar: how you authenticated, and when. The certificate is, in effect, a signed snapshot of your auth session that any SSH server can verify offline.

What happens when you connect

You never reach your container directly. Every SSH connection lands on a proxy at the edge of the region, and that proxy is where the certificate gets taken apart. The first check is trust. The proxy keeps a registry of certificate authority fingerprints, refreshed on a schedule from the same authority endpoint your SSH client can query. If your certificate wasn’t signed by one of those CAs, it’s treated as a plain SSH key, and uploaded SSH keys follow a separate, more restricted path. Next come the critical options. Your certificate above has none, but certificates can carry options that pin them to specific regions or hosts. A certificate scoped to eu-5 is rejected everywhere else, no matter how valid its signature is. Then the proxy asks a question the certificate can’t answer on its own: is the token behind it still alive? The token-id@platform.sh extension gets checked against a revocation list. Log out of the CLI, or have your session revoked by an organization owner, and every certificate minted from that token dies with it, even inside its 15-minute window. The check fails closed: if the revocation service can’t be reached, the connection is denied rather than waved through on stale claims.

The access check

With trust established, the proxy decides whether you can reach this specific environment. It prefers a fresh copy of your access document, fetched by the access-id@platform.sh identifier, and falls back to the copy embedded in the certificate. Either way, the document gets evaluated against the route you’re connecting to: the project, the environment type, and the permission SSH requires there. If the token was revoked, the embedded claims aren’t trusted at all. The proxy queries the auth service in real time with your user ID, and strips the authentication methods from the result, which forces a fresh login for anything that requires one. One more gate remains: MFA. Organizations can require MFA for SSH access to their projects. The proxy compares that requirement against the has-mfa@platform.sh flag. When the flag is missing and the route demands it, you get a banner instead of a shell:
The Parameters line isn’t decoration. The CLI parses it, walks you through the additional authentication, and retries.

The last hop

Your connection still has one more leg: from the proxy to the container. Your certificate doesn’t travel there, and neither does your key. Instead, the proxy signs a new certificate with its own upstream key, valid for 2 minutes, carrying your user ID, your client IP, and your access document trimmed down to what this route needs. That’s how the container knows who you are without ever seeing your credentials. The trimming matters. Connect to abcdefgh1234567 with the document above, and the container learns one thing: you’re an admin on this project. What else you can see in your organization is none of its business, so it never arrives.

The takeaway

Every upsun ssh runs through this pipeline: a 15-minute certificate proves who you are, a CA registry proves who vouched for you, a revocation check proves your session still exists, an access document proves you belong on this route, and an MFA flag proves how strongly you authenticated. The claims are signed into the certificate, which is what lets the proxy trust them without re-deriving your identity on every connection. The round trips that remain, the revocation check and the fresh access document, exist for the one thing a signed snapshot can’t tell you: what changed after it was signed. That’s the trade authorized_keys never offered. A key file says one thing: this key may connect, forever, until someone remembers to delete the line. A certificate says who you are, what you may touch, how you logged in, and when the claim stops being true. Revoking access is one API call, not a fleet-wide file edit. The same trade shows up in audits. SOC 2 and PCI DSS reviews ask the questions this pipeline answers mechanically: who can reach production, how they authenticated, and how fast access dies when someone leaves. On Upsun those answers hold on every connection, because the platform enforces them rather than a policy document: SSH credentials expire in minutes, revocation is central, and MFA is checked at the door. Certificate-based SSH is one control among the security and compliance measures you inherit by deploying on the platform, and the certifications behind them are in the Trust Center. And none of it needed a proprietary protocol. This is how a modern platform does SSH in a regulated environment: a public key infrastructure, with a certificate authority that signs short-lived identities and every hop validating them on its own. That shape is what makes it safe to run at scale. Trust is central, but enforcement is decentralized: nothing has to be pushed to the machines you connect to, so there is no fleet of authorized_keys files to keep in sync and no container holding a credential that outlives your session. And each hop stays isolated, because it only ever receives the claims it needs. A container that gets compromised learns your role on that one project, not the shape of your organization. Stock OpenSSH certificates, extended through the fields the format reserves for custom data, decoded by the same ssh-keygen you’ve used for years. If you’re building something similar, the format leaves you the same room it left us.
Last modified on August 18, 2026