The mental model: it's the AWS PEM flow
If you've ever launched an EC2 instance and SSH'd in, you already know everything you need. Sandbox Platform uses the same mechanics:
The dashboard generates an ed25519 key pair on our server. We keep the public half and bake it into your sandbox's ~/.ssh/authorized_keys when the VM boots. We give you the private half once as a downloadable .pem file, then forget it. Save the .pem, run ssh, you're in.
SSH passwords are off on every sandbox. The only way to log in is with the matching private key. That means there's no password to brute-force, no password to leak, and no "your sshd was open to the world" risk. The cryptography is the auth boundary — not the network.
Quickstart: 5 clicks and you're in
Generate a key pair in the dashboard
Open /dashboard, scroll to SSH Keys, click Add Key, pick Generate Key Pair, give it a name (e.g. "MacBook"), and click Generate & Download.
A yellow banner appears: "Save your private key now — it will not be shown again!" Click Download .pem File. The file lands in your Downloads folder.
Set permissions on the .pem (one time)
SSH refuses to use a private key that's world-readable. Lock it down:
chmod 600 ~/Downloads/MacBook.pem
Create a sandbox
Back in the dashboard, click Create Sandbox (or POST /api/sandboxes). Give it a name and instance size; ab0t.micro is fine for testing. Wait ~75 seconds for the status to flip from provisioning to running.
The dashboard will show the sandbox's public IP and a ready-to-paste ssh command.
SSH in
Copy the command from the dashboard (or compose it yourself):
ssh -i ~/Downloads/MacBook.pem ec2-user@<sandbox-ip>
First time only, SSH asks if you trust the host fingerprint. Type yes and press Enter. You're in.
You're logged into your sandbox as ec2-user with sudo rights. Run anything: htop, git clone, docker run, your own scripts. The VM is yours until you destroy it or it auto-stops.
Usernames: ec2-user vs ubuntu vs root
Sandboxes use Amazon Linux 2 by default. The right username depends on the image:
| Sandbox image | SSH user |
|---|---|
| Amazon Linux 2 (default) | ec2-user |
| Ubuntu | ubuntu |
| Anything — if both are wrong | Try root |
If you get Please login as the user "ec2-user" rather than the user "root", the server is telling you which user to use. Re-run with that one.
Why is opening port 22 safe?
Sandbox public IPs are reachable from anywhere on the internet. That sounds scary — "the SSH port is open to the world!" — until you remember that SSH passwords are off. The only way in is with the matching private key.
This is the same posture used by:
- GitHub.com when you
git clone git@github.com:...— their22is open globally; the keys are the auth. - Every AWS EC2 quickstart tutorial — default Lightsail / EC2 SGs open
22to the world; the.pemis the auth. - DigitalOcean droplets, Linode, Vultr, Hetzner — same model.
The cryptographic boundary (your key) is enforced at the SSH layer; the network boundary (a public port) is incidental. We tested this explicitly: anonymous and wrong-key SSH attempts are rejected at the auth layer with Permission denied (publickey), not at the connection layer.
.pem is the entire security boundary — treat it accordingly
- Never commit a
.pemto git, even a private repo. Add*.pemto.gitignoreglobally. - Don't email it, paste it in Slack, or store it in a shared drive.
- Rotate keys periodically — delete the old one in the dashboard, generate a new one.
- If you think a
.pemleaked: delete the key in the dashboard immediately. Future sandboxes won't include it. Existing sandboxes already have the public key inauthorized_keyson disk — recreate or terminate those sandboxes.
How it actually works (under the hood)
If you're curious what's happening when you click Generate:
- The dashboard calls
POST /api/ssh-keys/generateon Sandbox Platform. - Our backend asks the resource service to generate an ed25519 key pair. The private half is returned once, in PEM format. We never persist it — it goes straight to your browser as a download.
- The public half is recorded in our database, scoped to your user and current organization.
- When you create a sandbox, our backend looks up your public keys for the current org and passes them in the EC2 launch request as
metadata.ssh_authorized_keys. - cloud-init on the EC2 reads that metadata, writes your public key to
/home/ec2-user/.ssh/authorized_keys, sets correct permissions, and startssshd. - You SSH in.
sshdchallenges you with a public-key auth handshake. Your.pemsigns the response.sshdverifies againstauthorized_keysand lets you in.
If you switch workspaces (e.g. between your personal workspace and a team you're a member of), the keys you see on the dashboard belong to that workspace. A sandbox launched in workspace A gets workspace-A keys baked in. A sandbox launched in workspace B gets workspace-B keys. This isolates teams without interfering with personal keys.
For most people that means: if you have one workspace, you have one keyring. Easy. If you switch into a team workspace, generate a fresh key there.
Multiple keys (laptop + work + CI)
Each key is independent. You can have a key per machine you SSH from:
MacBook— for your laptopWorkLinux— for your office workstationCI— for an automated pipeline
All of them get baked into every new sandbox you create. Delete one and it's removed from future sandbox launches; existing sandboxes keep working until you destroy them.
Already have a key? Upload your public key instead
If you already have an SSH key on your laptop (e.g. ~/.ssh/id_ed25519), you don't need a new one. Upload the public half and SSH with the private half you already have:
# If you don't have a key yet, generate one: ssh-keygen -t ed25519 -C "your-email@example.com" # Copy the public key to your clipboard: cat ~/.ssh/id_ed25519.pub | pbcopy # macOS cat ~/.ssh/id_ed25519.pub | xclip # Linux
In the dashboard, click Add Key → Upload Existing Key, paste the public key, give it a name, click Upload. Now SSH in without the -i flag — your local SSH agent finds the matching private key automatically:
ssh ec2-user@<sandbox-ip>
Power-user alternative: short-lived certificates
The .pem flow is simple and matches what most people already know. There's a second path that some teams prefer: OpenSSH certificates. Instead of a long-lived .pem on disk, the dashboard issues a 10-minute cert against an in-VM CA. The cert is signed, time-bounded, and never written to your filesystem unless you choose to.
You'd reach for this if:
- You don't want any long-lived private key material on your laptop.
- You want auditable, time-bounded SSH access (the cert expires; no manual revocation needed).
- You want to grant temporary access to a teammate without sharing a
.pem.
The flow is in the dashboard's Ephemeral SSH Certificate panel. Paste your local public key, pick a TTL (60–1800 seconds), get back a cert, then ssh -i your_key -o CertificateFile=fresh_cert ec2-user@<ip>. Re-issue when the cert expires.
The PEM flow is universal, well-documented everywhere on the internet, and works with ssh, scp, rsync, VS Code Remote, and every other tool. The cert flow is great when you have a specific reason; otherwise stick with PEM.
Troubleshooting
Permission denied (publickey)
The most common SSH failure. Means the key isn't being accepted. Check:
- Wrong username. Default is
ec2-userfor Amazon Linux. Tryubuntuon Ubuntu images. - Wrong PEM file. Verify with
ssh-keygen -lf MacBook.pem— the fingerprint should match what the dashboard shows for that key. - Key not baked in. If you generated the key after the sandbox launched, it isn't in
authorized_keys. Generate the key first, then create the sandbox. (Or use the cert flow.) - You're in the wrong workspace. Keys are scoped to your current org. Switch back to the workspace where the key lives.
Connection timed out
SSH never reaches the server. The sshd never sees you. Check:
- Sandbox isn't running yet. Status must be
runningwith a real public IP (notpending). Refresh the dashboard. - Your network blocks outbound TCP/22. Some corporate / coffee-shop networks restrict SSH. Try a hotspot or VPN.
- cloud-init still running. First-boot scripts can take 60–90 seconds. If the IP is up but SSH refuses, wait another minute.
Bad permissions on the .pem
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: UNPROTECTED PRIVATE KEY FILE! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Fix: chmod 600 your-key.pem
I deleted the key — can I get it back?
No. The private key was returned to your browser exactly once at generation time and was never persisted on our side. If you lost the .pem, generate a new key. Existing sandboxes still authorize the old key until you destroy them, so you can either keep the old sandboxes accessible via the still-valid old key (if you have it) or recreate them with the new key.
Multiple sandboxes — do I need a key per sandbox?
No. One key works for every sandbox in your workspace. The same public key is baked into every new sandbox at launch.
Doing it from the API
Everything in the dashboard is a thin wrapper over a public API. If you're scripting:
export SANDBOX_API_KEY="ab0t_sk_live_..." export SANDBOX_URL="https://sandbox.dev.ab0t.com" # 1. Generate a key pair (returns the PEM exactly once) curl -sS -X POST "$SANDBOX_URL/api/ssh-keys/generate" \ -H "Authorization: Bearer $SANDBOX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name":"my-laptop","key_type":"ed25519"}' \ | jq -r .private_key_pem > ~/Downloads/my-laptop.pem chmod 600 ~/Downloads/my-laptop.pem # 2. Create a sandbox SBX=$(curl -sS -X POST "$SANDBOX_URL/api/sandboxes" \ -H "Authorization: Bearer $SANDBOX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name":"demo","instance_type":"ab0t.micro"}') SID=$(echo "$SBX" | jq -r .sandbox_id) # 3. Wait for RUNNING and grab the IP while :; do STATUS=$(curl -sS "$SANDBOX_URL/api/sandboxes/$SID" \ -H "Authorization: Bearer $SANDBOX_API_KEY") STATE=$(echo "$STATUS" | jq -r .status) IP=$(echo "$STATUS" | jq -r .instance_ip) [ "$STATE" = "running" ] && break sleep 5 done # 4. SSH in ssh -i ~/Downloads/my-laptop.pem ec2-user@$IP
Full API reference at /documentation#api-ssh.
What's next
You can SSH in. What you do once you're there is up to you:
Generate your first key
Open the dashboard, click Add Key, download the .pem. Two minutes.