Tested on a Frame (BUILD_ID 20260922.6101926): - Devkit pairing: the service runs and answers properties.json, but /register refuses with "please put the Steam client in pairing mode" unless Steam is on Settings > Developer > Pair new host. Both setup paths now say so and keep asking for 2 minutes before falling back to the password. - Sideloading: registering, launching, quoted start paths and Remove work; a Windows exe runs under Proton 11 through FEX. An aarch64 build starts but outside the runtime container, and an x86-64 Linux build doesn't start because the x86-64 Steam Linux Runtime 4.0 isn't installed. The inspect note for x86-64 Linux builds now says so. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
9.4 KiB
SSH into the Steam Frame
Confidence labels:
- Confirmed (Frame): Valve's Steam Frame docs or a Frame-specific source.
- Inferred (Deck/SteamOS): true on Steam Deck or SteamOS generally, but not checked on a Frame.
- Guess: reasoned, with no source.
How access is turned on
| Claim | Confidence | Source |
|---|---|---|
| Steam Settings → System → Enable Developer Mode enables SSH, ADB, and RDP | Confirmed (Frame) | setup, debugging |
| A password is set in Developer → Set User Password. There is no default password. | Confirmed (Frame) | setup |
The default user is steamos, not deck |
Confirmed (Frame) | debugging: ssh steamos@frame |
The default hostname is frame, and can be changed in Steam Settings → System → Hostname |
Confirmed (Frame) | setup, adb_lepton |
| The IP address is shown in Quick Settings or Steam Settings → Internet | Confirmed (Frame) | adb_lepton |
The rootfs is read-only. sudo steamos-readonly disable makes it writable. |
Confirmed (Frame) | debugging |
sudo pacman works. Helper aliases cdd (Frame scripts dir), cdl (Steam logs), and lepton exist. |
Confirmed (Frame) | debugging |
| There's a full KDE Plasma Linux desktop inside the headset, reachable from the SteamVR dashboard | Confirmed (Frame, press) | Road to VR review, UploadVR |
On Deck, the manual route is Desktop Mode → Konsole → passwd → sudo systemctl enable --now sshd |
Inferred (Deck) | pimylifeup, gist |
On Deck, SSH needs the manual terminal steps. On the Frame, the Developer Mode UI handles both the password and the SSH service. That's why the headset-side checklist in the README involves no terminal at all.
Name resolution from a Mac
Valve's examples use a bare frame. That works on Windows through
LLMNR/NetBIOS. On macOS, a bare single-label name usually doesn't resolve
unless your router's DNS registers DHCP client names.
- Verified on device (2026-09-25):
avahi-daemonis running on the Frame andframe.localresolves from the Mac over mDNS. scripts/connect.shtriesframe.local, thenframe, then an mDNS browse for the devkit service (below). If none works, it tells you to re-run it with the IP. Once you have a working address, theHost framealias means you just typessh frame.- To check discovery yourself:
dns-sd -G v4 frame.local(Ctrl-C to stop), ordscacheutil -q host -a name frame.local. - A DHCP reservation for the headset on your router makes the IP stable. That's the most reliable fallback.
Key-based login (done by scripts/connect.sh)
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_frame -N '' -C "mac->steam-frame"
ssh-copy-id -i ~/.ssh/id_ed25519_frame.pub steamos@frame.local
~/.ssh/config block (managed between marker lines by the script):
Host frame
HostName frame.local
User steamos
IdentityFile ~/.ssh/id_ed25519_frame
IdentityFile ~/.ssh/id_rsa_frame_devkit
IdentitiesOnly yes
ServerAliveInterval 30
The script only asks for the password if the pairing below doesn't work.
Password-free pairing (SteamOS devkit service)
From Valve's source (steamos-devkit-service,
steamos-devkit client). Verified on a
Frame 2026-09-26 (BUILD_ID 20260922.6101926): the service runs with Developer Mode
on, properties.json answers with "login": "steamos", the headset advertises
_steamos-devkit._tcp as frame, and /register needs pairing mode (below). The
approve prompt and key install are not verified yet. SteamOS's devkit service is
what Valve's Devkit Client uses to pair. scripts/connect.sh and
ui/frame_connect.py try it first:
- The headset serves HTTP on port 32000 and advertises mDNS
_steamos-devkit._tcp.GET /properties.jsongives theloginuser; the script uses it asUser(unless you setFRAME_USER, or it saysroot), for the password fallback too, and keeps it on re-runs. - Open Steam Settings → Developer → Pair new host in the headset first.
Otherwise
/registeranswers at once with403"please put the Steam client in pairing mode: Settings -> Developer -> Pair new host"(verified). The scripts say so and keep asking for 2 minutes while you open it. POST /registerwithssh-rsa <key> <comment> 900b919520e4cf601998a71eec318fec(a fixed token from Valve's client) shows an approve prompt inside the headset naming the comment (frame-control@<your computer>). It waits 30 s, then installs the key for the device user and turnssshdon. The reply is200 Registered, or403with{"error": ...}(declined, timed out, Steam not running).- It only accepts RSA keys, hence the second key,
~/.ssh/id_rsa_frame_devkit(3072-bit). - A host counts as found if port 22 or 32000 answers. With no host given,
and
frame.local/frameunreachable, it browses_steamos-devkit._tcpwithdns-sd(macOS) oravahi-browse(Linux) for a few seconds if installed. - Port 32000 closed, a timeout, or an error: the script says why and falls back to copying the ed25519 key with the Developer Mode password, as before.
Anyone on your network can send the request, so only approve a prompt you
started. curl http://<frame-ip>:32000/properties.json shows whether the service is up.
~/.ssh/authorized_keys lives under /home, which SteamOS keeps across OS
updates (inferred from Deck; the Frame uses the same A/B image scheme).
Keeping sshd enabled across updates
- Frame: SSH is tied to the Developer Mode toggle, so it should survive updates as long as Developer Mode stays on. (Inferred: Valve doesn't say how the toggle is implemented.)
- Deck (for comparison):
systemctl enable sshdusually persists because/etcis an overlay that survives updates. Changes under/usrdo not. - Don't
pacman -Sanything you depend on for access. Packages installed into the read-only rootfs are wiped by OS updates on SteamOS. Use Flatpaks (--user) or~/for anything that needs to persist.
Hardening (optional: ./scripts/connect.sh --harden)
The script writes /etc/ssh/sshd_config.d/01-frame-keys-only.conf with
PasswordAuthentication no and KbdInteractiveAuthentication no, then reloads
sshd. First, it checks that key login works in BatchMode, so you can't lock
yourself out.
- Needs
sudo(Developer Mode password), entered on the Mac. - It assumes
/etc/ssh/sshd_configincludessshd_config.d/*.conf, which is the Arch default. The script checks for this and stops if the include is missing. /etcdrop-ins normally persist across SteamOS updates (inferred from Deck).- It doesn't affect RDP (xrdp) or
sudo, which still use the password. - Undo:
ssh frame 'sudo rm /etc/ssh/sshd_config.d/01-frame-keys-only.conf && sudo systemctl reload sshd'.
Other shells
- ADB over USB-C to the native Linux OS:
adb shell. Plug the headset into the Mac. Valve notes that USB power may be insufficient. Install withbrew install android-platform-tools. This is useful if Wi-Fi SSH is broken. (Confirmed (Frame): debugging) - ADB over Wi-Fi reaches the Lepton (Android) container, not Linux:
adb connect frame:5555. It only works while "Lepton Development" or an Android app is running. (Confirmed (Frame): adb_lepton) - RDP: xrdp with user
steamosand the Developer Mode password (see streaming.md).
Fallback bootstrap one-liner
Use this only if the Developer Mode toggle doesn't give you SSH (for example, an OS build without it).
- On the Mac:
./scripts/serve-bootstrap.sh. It servesbootstrap-on-frame.sh, with your~/.ssh/id_ed25519_frame.pubembedded, on port 8765, and prints the exact one-liner. - On the Frame's Linux desktop, open Konsole and type the printed line,
roughly
curl -fsS mac.local:8765|bash(~30 characters). Ifmac.localdoesn't resolve, the script prints an IP form instead. - The bootstrap installs the key into
~steamos/.ssh/authorized_keys, and then runssudo systemctl enable --now sshd.sudoasks for a password, and if none is set yet, it tells you to runpasswdfirst. That means typing the password on the headset one more time. - Stop the server on the Mac with Ctrl-C.
This is plain HTTP on your LAN, and it only serves a public key, so the content isn't secret. Anyone on the LAN who can spoof your Mac's address could serve a different script, though, so use it only on a trusted network.