Encrypted signing key management for Roku development. One device, multiple companies, zero crosstalk.
brew install agescoop install age
# or
winget install FiloSottile.agemkdir -p ~/.age
age-keygen -o ~/.age/key.txtThis creates two things:
- Public key (
age1...) — used to encrypt. Safe to share. - Secret key (
AGE-SECRET-KEY-...) — used to decrypt. Never share this.
macOS (Keychain):
security add-generic-password -a "$USER" -s "age-secret-key" -w "$(grep 'AGE-SECRET-KEY' ~/.age/key.txt)" -UTo retrieve later:
security find-generic-password -s "age-secret-key" -wWindows (Credential Manager):
# Store
cmdkey /generic:age-secret-key /user:age /pass:"AGE-SECRET-KEY-YOUR-KEY-HERE"
# Retrieve
cmdkey /list:age-secret-keyOr store it in any password manager. This one string is the only thing you need to recover everything in this repo.
Run the one-shot setup script:
./setup.shThis installs age, generates your encryption key, backs it up to the system keychain, and adds shell aliases to all detected shells (zsh, bash, PowerShell). If run again, it offers to abort, overwrite, or repair.
device-keys/
├── devices/
│ ├── default.age # Default Roku device (IP + password)
│ └── <name>.age # Additional devices
├── keys/
│ └── <company>/
│ ├── <company>-credentials.age # Encrypted DevID + signing password
│ └── <company>-rekey.pkg.age # Encrypted .pkg for restoring key to device
├── scripts/
│ ├── common.sh # Shared utilities (device resolution, decryption)
│ ├── add-device.sh # Register a new Roku device
│ ├── capture-key.sh # Full setup for a new company
│ ├── rekey.sh # Switch device to a company's key
│ ├── genkey.sh # Generate a signing key (standalone)
│ ├── extract-key.sh # Extract existing key from device
│ ├── package.sh # Build a .pkg for store submission
│ ���── init.sh # Generate bsconfig.local.json for a project
├── stub-app/ # Minimal Roku app used for key capture
└── .gitignore # *.dec, *.pkg, *.tmp
All .age files are encrypted with your public key. Without the secret key, they're unreadable.
All commands support --device <name> to target a specific Roku device. If omitted, the default device is used.
roku-add-device <name> <ip> <password>Registers a new Roku device so you can target it with --device.
Example:
roku-add-device stick-2 192.168.1.100 mypassword
roku-add-device office 10.0.0.50 devpasswordThen use it:
roku-rekey wvw --device stick-2
roku-capture acme --device officeroku-capture <company-name>This runs the full setup:
- Generates a new signing key on the Roku device (via telnet)
- Sideloads the stub app
- Packages it to create a signed
.pkg - Encrypts and saves both the credentials and
.pkg
Example:
roku-capture acmeCreates:
keys/acme/
├── acme-credentials.age
└── acme-rekey.pkg.age
Commit both files after.
roku-rekey <company-name>Restores that company's signing key to the device using their saved .pkg. Takes a few seconds.
Example:
roku-rekey wvw # Device is now keyed to WVW
roku-rekey untamed # Device is now keyed to UntamedThe script verifies the switch by checking the DevID on the device after rekeying.
cd /path/to/roku/project
roku-initDecrypts the device IP and password, writes bsconfig.local.json into the current directory. Only needed once per project (or when the device IP changes). The file is gitignored.
cd /path/to/roku/project
roku-package <company-name>Loads the correct signing key, builds the app, sideloads it, packages it, and downloads the .pkg to out/.
Example:
cd ~/Development/pin/untamed/untamed_roku
roku-package untamed
# → out/untamed-20260410-170000.pkgroku-genkey <company-name>Generates a new key via telnet and saves the encrypted credentials. Does not capture a .pkg — use capture-key.sh instead for the full flow.
roku-extract <company-name>Reads the current DevID from the device and saves it. Useful if you already have a key on the device and want to back it up. You'll be prompted for the keygen password if you have it.
Each genkey creates a cryptographic signing key in the Roku's hardware and returns:
- DevID — identifies the key (submitted to the Channel Store)
- Password — used to sign apps and rekey the device
Only one key exists on the device at a time. Running genkey again destroys the previous key.
To restore a previous key, upload a .pkg that was signed with that key plus the password. The device extracts the key from the .pkg signature and restores it. This is why we capture a .pkg immediately after generating each key.
All credentials and .pkg files are encrypted with age before committing. The repo is safe to push to a private GitHub repo — nothing is readable without your secret key.
- Install
age - Retrieve your secret key from Keychain / password manager
- Save it to
~/.age/key.txt - Clone this repo
- Add the shell aliases to your profile
- Run
roku-initin each project directory
Everything works exactly as before.
- Never commit decrypted files. The
.gitignoreexcludes*.dec,*.pkg(unencrypted), and*.tmp. - One device, many companies. Rekey before packaging. Daily sideloading doesn't need rekeying.
- Back up your age secret key. It's the single point of recovery. Lose it and the encrypted files are gone.
- genkey destroys the current key. Always capture the existing key before generating a new one.