Replaces Sunshine's self-signed cert with one minted from a private root CA
whose key material lives in 1Password. Every host running install.sh fetches
the CA via 'op read', mints itself a host cert with SANs for <hostname>.lan
and the current LAN IP, and installs the CA into the system trust store.
Bootstrap (run once, anywhere)
- scripts/cert-bootstrap.sh: generates a 4096-bit RSA root CA (10y validity),
uploads it as a Secure Note titled "Omarchy-Stream Root CA" in the Private
vault with two fields: cert (text) and key (concealed). Refuses to overwrite
an existing item without --force.
Per-host (lib/certs.sh)
- fetch_and_install_certs: reads op://Private/Omarchy-Stream Root CA/{cert,key}
to a tmpfs-staged temp dir (XDG_RUNTIME_DIR), mints a host cert via openssl
with serverAuth + clientAuth EKU, drops cert/key at ~/.config/sunshine/
credentials/{cacert,cakey}.pem, installs the CA at
/etc/ca-certificates/trust-source/anchors/omarchy-stream-ca.pem and runs
update-ca-trust.
- Idempotent: skips re-mint when on-disk cert is signed by the current CA,
has the expected SANs, and isn't within 30 days of expiry. Override with
FORCE_CERTS=1 or --force-certs.
install.sh
- Adds --no-certs, --force-certs flags; sources lib/certs.sh; runs cert step
after permissions/config and before firewall so the service restart at the
end of install picks up the new cert.
client/install-macos.sh
- After installing Moonlight, if `op` is available and signed in, fetches the
CA and adds it as a trusted root to /Library/Keychains/System.keychain via
`security add-trusted-cert -d -r trustRoot`. Skips cleanly when op isn't
ready.
uninstall.sh
- Adds --remove-ca-trust to delete the system trust anchor. By default the
CA is left in place since other tools may rely on it.
verify.sh
- Adds checks for: cert signed by omarchy-stream CA, cert >30 days from
expiry, CA present in system trust store.
Docs
- README "Trusted TLS certs via 1Password" section: bootstrap flow, per-host
flow, client trust matrix (Linux / macOS / iOS / Android / Apple TV),
re-pairing note (first cert install on a host invalidates pinned Moonlight
fingerprints), config env vars.
- client/README gains per-platform CA-trust install steps with concrete
`op read` + platform-specific commands.
176 lines
6.0 KiB
Markdown
176 lines
6.0 KiB
Markdown
# Moonlight clients
|
|
|
|
This directory covers installing Moonlight on the devices that connect to your
|
|
Sunshine host. The host side (Sunshine on Linux) is documented in the
|
|
[top-level README](../README.md).
|
|
|
|
## Install
|
|
|
|
### macOS
|
|
|
|
From a Mac that has this repo checked out:
|
|
|
|
```sh
|
|
./client/install-macos.sh
|
|
```
|
|
|
|
The script verifies Homebrew is present and runs `brew install --cask moonlight`.
|
|
It will not install Homebrew for you.
|
|
|
|
Manual equivalent:
|
|
|
|
```sh
|
|
brew install --cask moonlight
|
|
```
|
|
|
|
Fallback (no Homebrew): download a signed DMG from the official releases page
|
|
and drag `Moonlight.app` into `/Applications`:
|
|
|
|
- https://github.com/moonlight-stream/moonlight-qt/releases
|
|
|
|
### Android
|
|
|
|
Install "Moonlight Game Streaming" from the Play Store:
|
|
|
|
- https://play.google.com/store/apps/details?id=com.limelight
|
|
|
|
### iOS / iPadOS
|
|
|
|
Install "Moonlight Game Streaming" from the App Store:
|
|
|
|
- https://apps.apple.com/us/app/moonlight-game-streaming/id1000551566
|
|
|
|
### Apple TV
|
|
|
|
Same listing on the tvOS App Store. Search "Moonlight Game Streaming" on the
|
|
Apple TV itself, or use the App Store link above from an iOS device signed in
|
|
to the same Apple ID.
|
|
|
|
### Steam Deck / Linux
|
|
|
|
Use `moonlight-qt` from your distro's package manager (Flatpak on Steam Deck,
|
|
`pacman`/`apt` elsewhere). It is the same Qt-based client as macOS and Windows.
|
|
|
|
## Trusting the omarchy-stream CA
|
|
|
|
If the host was installed with the cert step (default), its Sunshine web UI
|
|
uses a cert signed by a private root CA whose key is stored in 1Password. To
|
|
avoid the browser warning on `https://<host>.lan:47990`, install the CA on each
|
|
client device. This is a one-time step per device.
|
|
|
|
### macOS
|
|
|
|
`./install-macos.sh` handles this automatically if `op` is signed in. To do it
|
|
manually:
|
|
|
|
```bash
|
|
op read --no-newline "op://Private/Omarchy-Stream Root CA/cert" > /tmp/ca.pem
|
|
sudo security add-trusted-cert -d -r trustRoot \
|
|
-k /Library/Keychains/System.keychain /tmp/ca.pem
|
|
rm /tmp/ca.pem
|
|
```
|
|
|
|
### iOS / iPadOS
|
|
|
|
1. In the 1Password app, open the **Omarchy-Stream Root CA** item in the
|
|
**Private** vault. Copy the `cert` field into the body of an email and send
|
|
it to yourself with the file extension `.crt` or `.pem` (Mail handles inline
|
|
PEM oddly; an attachment is cleanest).
|
|
2. Open the email on the iOS device and tap the attachment. iOS will offer to
|
|
install a configuration profile.
|
|
3. Settings → General → VPN & Device Management → Downloaded Profile → Install.
|
|
4. **Important second step**: Settings → General → About → Certificate Trust
|
|
Settings → enable full trust for the omarchy-stream Root CA. Without this,
|
|
Safari still warns.
|
|
|
|
### Android
|
|
|
|
1. From 1Password, save the CA `cert` field to a `.crt` file on the device
|
|
(Downloads folder is fine).
|
|
2. Settings → Security → Encryption & credentials → Install a certificate → CA
|
|
certificate.
|
|
3. Acknowledge the warning, select the file, give it a name.
|
|
|
|
(Path varies slightly by Android version / OEM skin — search Settings for "CA
|
|
certificate" if the path above doesn't match.)
|
|
|
|
### Apple TV
|
|
|
|
Cert profiles can be installed by emailing the cert to an account on the
|
|
Apple TV and tapping it, or by using Apple Configurator from a Mac. For
|
|
LAN-only use the self-signed warning is also tolerable — Apple TV has no
|
|
browser, so the cert is only relevant if you ever inspect the host directly.
|
|
|
|
## First pair
|
|
|
|
The pairing flow is the same on every client.
|
|
|
|
1. Confirm the host is healthy. On the host machine:
|
|
|
|
```sh
|
|
./install.sh --doctor
|
|
```
|
|
|
|
Sunshine must be running and reachable on the LAN.
|
|
|
|
2. On the client, open Moonlight. The host should appear automatically via
|
|
mDNS as long as the client is on the same LAN. If it does not appear, use
|
|
"Add Host Manually" and enter the host's LAN IP address.
|
|
|
|
3. Click or tap the host. Moonlight displays a 4-digit PIN.
|
|
|
|
4. On the host machine, open the Sunshine web UI:
|
|
|
|
```
|
|
https://localhost:47990
|
|
```
|
|
|
|
Accept the self-signed certificate. Log in (credentials are set the first
|
|
time you visit). Go to the PIN tab, enter the 4-digit PIN from the client,
|
|
and submit. Pairing typically completes within about 5 seconds.
|
|
|
|
5. Back in Moonlight, the host now shows as "Paired". Click it to see the
|
|
available apps. The default app is "Desktop", which is either a full-screen
|
|
mirror of the host's display or a headless virtual display, depending on
|
|
how the host is configured.
|
|
|
|
## Streaming notes
|
|
|
|
- **Resolution.** Moonlight lets you pick a target stream resolution per host.
|
|
On a headless-configured host, that resolution is what the host actually
|
|
renders at. On a mirror-configured host, the host renders at its native
|
|
resolution and Moonlight downscales on the client.
|
|
|
|
- **Bitrate.** Moonlight's defaults are conservative for general internet use.
|
|
For LAN streaming, bump it to 50-100 Mbps.
|
|
|
|
- **Audio.** By default, Moonlight captures audio from the host and plays it
|
|
on the client. If you want the host's speakers/monitor to keep playing
|
|
audio while you stream, enable "Play audio on host" in Moonlight's stream
|
|
settings.
|
|
|
|
## Troubleshooting
|
|
|
|
- **"Host not found" / host does not appear automatically.** Confirm both
|
|
machines are on the same LAN/VLAN. mDNS does not traverse VLANs without an
|
|
mDNS reflector. Fall back to "Add Host Manually" with the host's IP.
|
|
|
|
- **Pairing PIN does not work.** Two common causes: the Sunshine web UI
|
|
session has timed out (log in again and re-enter the PIN), or the PIN is
|
|
being entered into the wrong host (multiple Sunshine instances on the LAN).
|
|
Cancel and re-trigger pair from the client to get a fresh PIN.
|
|
|
|
- **Black screen after pairing.** This is a host-side problem. On the host:
|
|
|
|
```sh
|
|
./install.sh --doctor
|
|
```
|
|
|
|
- **Mac cannot reach `brew`.** Homebrew is not installed (or not on `PATH`).
|
|
Install it per https://brew.sh, open a fresh shell, then re-run
|
|
`./client/install-macos.sh`.
|
|
|
|
- **Android sees the host but cannot connect.** The Android device is
|
|
probably on a guest WiFi SSID that is isolated from the main LAN. Move it
|
|
to the main LAN, or use "Add Host Manually" with the host IP.
|