- docs/recovery-and-images.md: where Valve's Frame images are (not linked from the SteamOS download page), file names, sizes and our checksums, the GPT layout with exact start sectors, what's in rootfs-A (btrfs, SteamOS 0.3.0 build 20260922.5152327, users, sudo and sshd config), extracting it, running it without the headset, and Valve/Collabora's Holo Core aarch64 preview. - how-the-frame-works.md: correct the recovery image file names; add verified facts on the SSH server, tools on the image (no adb), Lepton instances as podman containers, going off the network when asleep, and the battery reading at full charge. - ssh.md: pairing from an iPhone and why devkit RSA pairing doesn't fit it. - open-questions.md, README.md and the steam-frame skill point to the new pages. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
10 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).
From an iPhone or iPad
The iPhone app (iphone.md) makes its own ed25519 key and adds it
with the Developer Mode password, once, over a password login; the Frame's sshd
offers publickey,password (OpenSSH 9.7p1, keyboard-interactive off). It can't
use the devkit pairing above: that installs an RSA key, and the Swift SSH
library signs RSA only with SHA-1, which OpenSSH 8.8 and later refuse by default.
The app pins the Frame's host key on first use and asks you to pair again if it
changes. Verified 2026-09-27 against the Frame's recovery image
(recovery-and-images.md); on the headset, the add-the-key-yourself
route was used.
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.