Steam Deck Secure Boot (Deck SB)

X-Post from https://github.com/downthecrop/DeckSecureBoot


Status: Beta 1.6

Arch-based live ISO for Enabling Secure Boot the Steam Deck (LCD and OLED)

Download

Features

  • Easy to use menu on the Deck (D-Pad navigation)
  • Enables Secure Boot without the UEFI exposing the toggle
  • Optional disk install to always have access to change SecureBoot status
  • Keeps SteamOS fully launchable while Secure Boot stays enabled
  • Supports every Steam Deck hardware revision (LCD and OLED)
  • Compatible with Clover Bootloader and Dual Boot setups (Windows/SteamOS)
  • Key safety baked in: you cannot lock yourself out of disabling Secure Boot
  • No tricks. This is valid Secure Boot, Windows anti-cheat software treats the deck as compliant
  • Fully reversible

This is heavily inspired by / a practical follow-up to:
👉 https://github.com/ryanrudolfoba/SecureBootForSteamDeck
His work showed the steps. This repo automates them into an ISO.

How to use it

  1. Get the ISO – Grab the latest release artifact or build it yourself with build.sh (see “Building it yourself”).
  2. Flash to USB – Use Balena Etcher (recommended) or any dd-like tool to write the image to a USB drive.
  3. Boot From USB – Plug in the USB, hold Vol- + Power, and pick the USB device from the boot selector.
  4. Run the menu – the ISO boots into a menu where you can enroll keys, sign loaders, rerun the EFI installer, or disable Secure Boot later.

How this works

The Deck never shows a “turn on Secure Boot” toggle inside its UEFI UI, but Valve ships it in setup mode. Setup mode means the firmware happily accepts new Platform Keys (PK), Key Exchange Keys (KEK), and db signatures without user prompts. When you pick the enrollment/enable option in the menu, we drop our baked keys (plus Microsoft’s) into the firmware variables. As soon as the PK lands, the firmware automatically flips Secure Boot to enabled. Later, if you use the unenroll/disable option, we clear those vars; once the PK is gone the Deck re-enters setup mode and Secure Boot is automatically disabled. No hidden switches involved—just key presence or absence.

Helpful information & FAQ

  • Clover note: Clover removes the Deck SB Jump loader entry from the Deck’s Boot Manager (Vol- + Power). Use Vol+ + Power, pick Boot From File, then load /efi/deck-sb/jump.efi to load it manually if you get stuck.
  • Signing other OSes: Any EFI loader or kernel you want to boot with Secure Boot enabled must be signed. Use the Signing Utility to add signatures for every distro you keep on the internal drive.
  • GRUB Secure Boot policy warnings: Some distros ship GRUB with grubshim (SteamOS GRUB has this too), which complains under Secure Boot. That’s why we rely on our custom jump loader instead.

Does this modify SteamOS? We drop a tiny systemd service whose only job is to ensure the Deck SB bootloader entry gets re-added if SteamOS updates wipe it. The OS rootfs, kernel, and userspace remain untouched. If you choose to install the ISO to disk from the menu, we also drop a copy of the live ISO environment on SteamOS (~400MB) so you can easily toggle SecureBoot in the future without the USB.

Will updates still work under Secure Boot? Yes. SteamOS keeps its original GRUB entry and kernel images in the EFI partition. We install an additional boot option without overwriting any existing bootloaders.

SteamOS stopped booting under Secure Boot! A recent SteamOS update probably bumped the kernel or initrd filenames. Re-run the EFI installer option from the menu; it re-parses the official SteamOS GRUB config and refreshes the arguments so the Deck SB loader tracks the new assets automatically.


Repo layout

  • build.sh – Entry point that prepares an Archiso workdir, copies our profile, injects payload + keys, and calls the resigner on output ISO.
  • profile/ – Trimmed Archiso baseline overrides (mainly profiledef.sh, EFI bits, pacman.conf).
  • payload/ – Everything that lands inside the live image. payload/root/menu.sh drives the ncurses UI, the deck-*.sh helpers enroll/unenroll/sign, and payload/etc/systemd/system/deck-startup.service re-adds the Deck SB boot entry if updates wipe it.
  • keys/ – the baked Secure Boot keys (PK.pem/PK.key). build.sh mirrors them to /usr/share/deck-sb/keys and /var/lib/sbctl/ during the image build.
  • resigner.sh – Post-build helper that re-signs the hidden ISO EFI image so the ISO still boots after the Deck trusts these keys.

What you get

  • A live ISO that understands the Deck’s UEFI
  • A ncurses menu with:
    1. Check Boot Status (UEFI? efivars? secureboot?)
    2. Enroll / Enable Secure Boot (runs sbctl enroll-keys -m with our baked keys)
    3. Signing Utility, EFI Dropper and ISO Installer (sign SteamOS or any other EFI loader in one place)
    4. Root shell
    5. Reboot / Poweroff
    6. Unenroll / Disable Secure Boot
  • Keys baked into the image, we all use the same keys so it's impossibel to lock yourself out of toggling SecureBoot (you can never lose the signing keys).
  • A fixed sbctl GUID so the layout is stable:
    • decdecde-dec0-4dec-adec-decdecdecdec

Why you need need to sign EFI's (or other OSes)

Secure Boot is simple but strict: the firmware will only run binaries signed by keys it trusts.

What this ISO does when you pick “Enroll / Enable Secure Boot”:

  1. Installs our key set (the ones below)
  2. Installs Microsoft production UEFI keys (so Windows and lots of vendor stuff still works)
  3. Tells firmware “we’re done, leave setup mode”

After that, when booting the UEFI checks the signature on EFI files:

  • anything signed by Microsoft → OK
  • anything signed by our keys → OK
  • anything not signed → blocked

SteamOS and other Linux installs often ship unsigned or signed with somebody else’s key, so the firmware doesn’t trust it. The Signing Utility entry takes the EFI binary you point at (SteamOS or anything else) and adds our signature so it passes Secure Boot with our key.

Important: if later you disable Secure Boot or clear vars, you do not have to “unsign” SteamOS or anything else. Signatures are just extra data. If Secure Boot is off, the firmware ignores them.


Keys we use (baked, public on purpose)

We all use the same keys so nobody bricks themselves permanently. These are the same ones we embed into the ISO:

Show baked keys

**PK.key**
```text
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDAiQ+44gfMGScB
XrKOF8smb+IbcvMzZaZJNYfngTr12ZfLcuGBXKA7JF5sssFMaRA7oQ/lYW4hT99q
acyRpSN3VFWbzZlrU3hq/SH+X1EEkoLfjmRaTjT5Zecuf7RGmf+VqCYvv6L73l/c
VwXnuX70kNkE82XmHGnX9wsmrMKH762lmS80NQS91Sl1jGKt3ylUZHHD7A68pSSR
JcLu2rFtqgaE9xt+V996QZvExD/nJQ/LvoVapB2z29dmdX4JidaK3hmUFseH2wYk
pbEuQB9JxhZZGHxwOiz50uctFiyUGXFJBkkS2yykuVtvDYYSzvPdpfFzqLw9+DGX
bWzrRwqJAgMBAAECggEADCB6e79dcFyIEEPh9u6iJ3pWAV+82E95u11LpfFhZS3w
9PMcueRyXOdFGGq/DToGAUt7UB5SLMBkJsa0CEj8DZnsrC5HtRdLQDwrY9DvriVU
1lsGWa3GgdUu3llT8/J1MNgVwMtPGNuSqdd7Eipb2kvrk/eJQxkBn/LVWR1DHSfQ
12xdq5jO/wxkeifPwwNSZ8QRIhorOV4jUZkBPJSYaaZDSNu3cDyeo7fVVXc5QVgm
ep5Iu8ntLiFcQkKkqsUuPGTre+Z1bjBhjFAqAK0+zJJ7xDF5Pfflwuj7W+AL0FZY
GxGTrZkIX/4Rg0Fe3H4pCAMZ311PlcemvMuH10BatQKBgQDfL/qqGLWh/gEW2Vb2
POMFe+YSttKuWNp8Kwj9h+ZFcSp+IW0T8vzklciUwJ8dqZNhqQ7KdNqpaJYZviHD
73oZoMuOqj1N0TGbsh/C2G76kgYlGhm8f1dBjZatHiMGrREpBO9m9+0A7o6TBP3T
RzMxmnMVLpML15KyYpBSrBPV5wKBgQDc13GRrnw0Kkwmi79LQUwJgB2jjW4re2gh
lsIqK88ok18ubdxRPe+gVak9DOq/hr4RuT6bE/nJIXKnJqLyGswjaV4GkfKN6u2C
gKnPjsl1jATHV5nq4gdpX/Z8C5EeEIDlmMxxOyl6ocVw95D2aXNsePf38fX5ftWg
z2LcmyIuDwKBgQC3sLJ7GrkrKXZWCu1C3tvuYIn8rxH5QtIXzgepOxev4bMaeoJf
H+c6b3jVzS9oZ3AQueadhM2PDrAzYcRCkjAJNckzkzO/f0R4I4N2h1HX0yVRlgjG
lnwHTPRNaXdkgD6WZyRut/ENiko4AKy0Hm6pDbhYH6wQ3A012l90W4I70wKBgQCC
mbJjCgIPw3fXT8uoEIyMDcT5ZPljI474VjSrRc8z2rtuNLAXJ36fnikAnrPw4hlj
V96rTUvp4yrvqMyySqCwzG47inIb9XPSOo6x3WpMZqqozKiMnHDvoz2cLCb81Zu0
rAEzcV5dVG/0F6QV5VTKMFvMuL3Td2uUtzBq8B9thwKBgQCwA6kAcdmfvtT87WM7
0xHkDUlPfJMt1ZiL9QdDPIR/AvDuQtiNBHUoaqDDJcwYwFe42URkBbitksXPTAtG
I6fHURi0C4xrR5XAFHdFz5pm3w3+1gTf8rj/NdPNOjlx+oheZaGGL6Gni8oF8S0L
gAleN/5iX9x9Htpi80o4N/kY3w==
-----END PRIVATE KEY-----
```

**PK.pem**
```text
-----BEGIN CERTIFICATE-----
MIIDETCCAfmgAwIBAgIUQBx1w+uTUKr7H2jtDG2rHfL4ZuowDQYJKoZIhvcNAQEL
BQAwGDEWMBQGA1UEAwwNU3RlYW0gRGVjayBQSzAeFw0yNTExMDcwMDE4MTJaFw0z
NTExMDUwMDE4MTJaMBgxFjAUBgNVBAMMDVN0ZWFtIERlY2sgUEswggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAiQ+44gfMGScBXrKOF8smb+IbcvMzZaZJ
NYfngTr12ZfLcuGBXKA7JF5sssFMaRA7oQ/lYW4hT99qacyRpSN3VFWbzZlrU3hq
/SH+X1EEkoLfjmRaTjT5Zecuf7RGmf+VqCYvv6L73l/cVwXnuX70kNkE82XmHGnX
9wsmrMKH762lmS80NQS91Sl1jGKt3ylUZHHD7A68pSSRJcLu2rFtqgaE9xt+V996
QZvExD/nJQ/LvoVapB2z29dmdX4JidaK3hmUFseH2wYkpbEuQB9JxhZZGHxwOiz5
0uctFiyUGXFJBkkS2yykuVtvDYYSzvPdpfFzqLw9+DGXbWzrRwqJAgMBAAGjUzBR
MB0GA1UdDgQWBBSb3Ivqxe6awsRvL4HUvn7I45RgrTAfBgNVHSMEGDAWgBSb3Ivq
xe6awsRvL4HUvn7I45RgrTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
A4IBAQARr6ABa4JHjW8/jbTjo7RZpobkaR523BhXvPc3U4j19jKvOLygRT68QYF3
XWAMVeMcFROs06tcSubxqdAKa4INMyVVklGslIT/z3CkLR5q9QV5SgI4Z3sRzAmL
PUKOoWc4x6op2heyxujlLwwiZouXWHqaklSaUymae9mCPUtwPg135WNc+E2BC4Ep
eU5IzhUe8nLj4wlWQoxdBsKWhuvsVJVEWs/HkzPrwulIAHQSb/divYe3eTrYKfib
gXnR8BtFo0R8QGTtodx6d7nu1QO3275yvHAZTr3bfygs5AkSHF9oqpaUPAOyPM4c
OyHXIWSLcl2GuAJnBoSR3rKgFvvr
-----END CERTIFICATE-----
```

These are embedded inside the ISO to KEK/db so we can also *clear* secure boot later.


The resigner (important)

Utility: resigner.sh patches the hidden EFI image inside the ISO:

  1. Find the El Torito UEFI image
  2. Extract it
  3. Sign EFI/BOOT/BOOTx64.EFI (and IA32 if present) with the baked keys
  4. Write it back at the same offset
  5. Outputs *-signed.iso

Usage:

./resigner.sh archlinux-steamdeck-sb-latest-x86_64.iso
# -> archlinux-steamdeck-sb-latest-x86_64-signed.iso

The main builder will auto-run resigner.sh on the generated ISO.

You can also point the resigner at other ISOs to make them bootable under these keys (Ubuntu etc.).

Heads-up: resigner.sh rewrites the hidden EFI boot image inside the ISO at its original byte offset. On rare ISOs that pack data immediately after that blob, the rewrite can corrupt the image. If it happens, try adding a little extra data to the ISO to shift around the structure and try again.


Building it yourself

  1. Boot an Arch x86_64 container
  2. sudo su
  3. Clone the repo and navigate to it
  4. ./build.sh will install all required dependencies and generate a new ISO. Finished ISOs are placed in ./out/ (or /out if that directory exists).

The builder writes ISOs to /out when that directory exists (handy inside containers) or ./out/.

Building from source (quickstart)

# optional: prep an output directory the container can write to
mkdir -p ./iso-out

# launch an Arch Linux build shell
docker run --rm -it \
  --platform=linux/amd64 \
  --privileged \
  -v $(pwd):/work \
  -v $(pwd)/iso-out:/out \
  archlinux:latest \
  /bin/bash

# Inside container
git clone https://github.com/downthecrop/DeckSecureBoot.git
cd DeckSecureBoot
./build.sh

Booting it on the Deck

  1. Power off Deck
  2. Hold Volume - and press Power
  3. Pick the USB you flashed the ISO to

If you choose to install the ISO to disk in the menu (optional) it will appear in the DeckSB Jumploader (jump.efi)

Credits

How to Change Ollama’s Model Directory with a Symlink

Ollama stores AI models in ~/.ollama by default, but if you want to use an external drive or a different location, you can change this with a symbolic link. This guide shows you how.

Step 1: Install Ollama

First, install Ollama by following the official instructions:

Alternatively you can install via homebrew: brew install ollama

Step 2: Remove the Existing .ollama Directory

Ollama stores its models in ~/.ollama. If you've already downloaded models, back them up before creating the symlink.

mv ~/.ollama ~/.ollama_backup

Step 3: Create a Symlink to Your New Location

Choose the location where you want Ollama to store models. If you have an external SSD or another directory, point the symlink there.

ln -s /Volumes/SSD/Ollama ~/.ollama

Step 4: Verify the Symlink

Check that ~/.ollama now points to your chosen directory:

ls -l ~/.ollama

If the output shows ~/.ollama -> /Volumes/SSD/Ollama, it's working.

Step 5: Test Ollama

Run a model to confirm it's downloading to the new location:

ollama run deepseek-r1:32b

Then check the model directory:

ls -l /Volumes/SSD/Ollama/models

If you see the downloaded model there, the setup is complete! Now, Ollama will store all models in your preferred location.

Unity Control Schemes – No Events Firing

I ran into a problem when working with the action map in my Unity game. I wanted to break the controls into their own separate schemes for rebinding, but there was a problem! When I added the control schemes, my code seemed to stop working. I checked the C# callback and Default Map on my PlayerInput component, toggling autoswitch didn't do anything, and I double-checked to make sure there were no other scripts consuming the input events.

After inspecting the InputAsset as JSON, I noticed something! The devices (although in the editor it says "All Devices") were empty!

"controlSchemes": [
    {
        "name": "Gamepad",
        "bindingGroup": "Gamepad",
        "devices": []
    },
    {
        "name": "Keyboard",
        "bindingGroup": "Keyboard",
        "devices": []
    },
    {
        "name": "GCC",
        "bindingGroup": "GCC",
        "devices": []
    }
]

Aha! I remembered a little tick box I didn't know the use of when creating the control schemes!
Image 1 Image 2

Here's what it looked like after I assigned them:

"controlSchemes": [
    {
        "name": "Gamepad",
        "bindingGroup": "Gamepad",
        "devices": [
            {
                "devicePath": "",
                "isOptional": false,
                "isOR": false
            }
        ]
    },
    {
        "name": "Keyboard",
        "bindingGroup": "Keyboard",
        "devices": [
            {
                "devicePath": "",
                "isOptional": false,
                "isOR": false
            }
        ]
    },
    {
        "name": "GCC",
        "bindingGroup": "GCC",
        "devices": [
            {
                "devicePath": "",
                "isOptional": false,
                "isOR": false
            }
        ]
    }
]

So remember to assign the device (although you already assigned the buttons in the action map) to the scheme!

How to Install Sunshine on macOS: A Step-by-Step Guide

Setting up Sunshine on macOS requires some preliminary steps to ensure all dependencies and configurations are in place. In this blog post, we will walk through the entire installation process, step by step, so you can easily get Sunshine running on your Mac system.

Install MacPorts

The first thing we need to do is install MacPorts, a package management system for macOS. Follow the instructions on the official MacPorts installation page.

https://www.macports.org/install.php

Add Sunshine to MacPorts

Before we can install Sunshine, we need to add it to our MacPorts source list.

Update MacPorts Sources:

echo "file:///Users/$USER/ports" | sudo tee -a /opt/local/etc/macports/sources.conf

Download Portfile for Sunshine

First, navigate to the folder where you'll place the Portfile. Create it if it doesn't exist

mkdir -p ~/ports/multimedia/sunshine

Download the Portfile using curl

curl -L -o ~/ports/multimedia/sunshine/Portfile https://github.com/LizardByte/Sunshine/releases/latest/download/Portfile

Update Port Index and Install Sunshine

Navigate to the ports directory and update the port index:

cd ~/ports
sudo portindex

Finally, install Sunshine:

sudo port install sunshine

Run Sunshine

Launch Sunshine by running the following command in the Terminal:

sunshine

The first time you start Sunshine, you will be asked to grant access to screen recording and your microphone.

Sunshine will start a web server, which you can access at https://localhost:47990. Remember to accept the self-signed certificate warning.

Configure macOS Audio

Install BlackHole Audio Sink

BlackHole is an audio sink that will help us in redirecting audio. Open your Terminal and run:

sudo port install BlackHole

For audio, make sure you set "BlackHole 2ch" as your input device in the Sunshine configuration. Even though "BlackHole 2ch" appears as the placeholder text, you'll have to manually type it into the field to set it.

And that's it! You have successfully installed and configured Sunshine on your macOS system. Enjoy streaming your macOS desktop and audio to other devices!

CI/CD macOS Application Bundling .app Artifacts

CI/CD macOS Application Bundling .app Artifacts

1. macOS Application Bundle Structure

1.1 The .app Bundle Format

In macOS, applications are distributed as bundles, a directory structure that contains executable code and related resources. Here's the typical structure of a .app bundle:

MyApp.app/
  Contents/
    Info.plist
    MacOS/
      MyApp
    Resources/
      MyApp.icns
  • Info.plist: Metadata and configuration information.
  • MacOS/: Main executable file.
  • Resources/: Stores resource files, such as images and localized content.
1.2 Understanding the Info.plist File

A .app bundle is really just a directory. The Info.plist file is critical to the .app bundle as it is what defines how to use this folder as a launcher instead of a folder. It contains key-value pairs that macOS uses to index the application. Key elements include:

  • CFBundleExecutable: Specifies the main executable file.
  • CFBundleIconFile: Defines the icon file.

Here's an example snippet:

<dict>
    <key>CFBundleExecutable</key>
    <string>MyApp</string>
    <key>CFBundleIconFile</key>
    <string>MyApp.icns</string>
</dict>

2. ICNS Conversion

macOS uses the ICNS file format for icons. A convenient conversion tool is png-to-icns-right-click-converter. This tool allows for seamless conversion from PNG to ICNS, suitable for inclusion in the application bundle's Resources directory.

3. CI/CD Integration

3.1 YAML Configuration for Packaging macOS Application

Continuous Integration and Continuous Deployment (CI/CD) streamline the development workflow. Below is a YAML script that demonstrates how to package a macOS application binary:

pack_macos_app:
    stage: package_stage
    dependencies:
        - build_stage
    script:
        - INFO_PLIST="$APP_NAME.app/Contents/Info.plist"
        - echo "Packing self-contained build for macOS x64..."
        - mkdir -p $APP_NAME.app/Contents/MacOS
        - mkdir -p $APP_NAME.app/Contents/Resources
        - cp Resources/Icons/icon.icns $APP_NAME.app/Contents/Resources/
        - cp $BUILD_DIR/$BINARY_NAME $APP_NAME.app/Contents/MacOS/
        - chmod +x $APP_NAME.app/Contents/MacOS/$BINARY_NAME
        - echo '<?xml version="1.0" encoding="UTF-8"?>' > $INFO_PLIST
        - echo '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' >> $INFO_PLIST
        - echo '<plist version="1.0">' >> $INFO_PLIST
        - echo '<dict>' >> $INFO_PLIST
        - echo '    <key>CFBundleExecutable</key>' >> $INFO_PLIST
        - echo "    <string>$BINARY_NAME</string>" >> $INFO_PLIST
        - echo '    <key>CFBundleIconFile</key>' >> $INFO_PLIST
        - echo '    <string>icon.icns</string>' >> $INFO_PLIST
        - echo '</dict>' >> $INFO_PLIST
        - echo '</plist>' >> $INFO_PLIST
    artifacts:
        name: "macOS x64 (self-contained)"
        paths:
            - $BUILD_DIR/$APP_NAME.app
        expire_in: 1 week

TL;DR

macOS applications rely on a bundle structure, where an application is organized as a directory with the .app extension. This structure includes executable code, resources, and metadata, all defined within specific subdirectories and files such as .plist and ICNS icon files. Understanding these aspects allows developers to create CI/CD bundling stages for any macOS app.