OSH

Documentation

Short-lived SSH certificates — requested by your laptop, approved on your iPhone, signed by a YubiKey-backed CA. Nothing static to steal.

zero-trust-flow
  [ OSH - Signer · iPhone ]            [ OSH - CA · YubiKey ]
            │ approve (Face ID)                │ sign cert (PIV 9c)
            ▼                                  ▼
  [ OSH - Client ] ──▶ [ Gateway ] ────────────┘
       │  request cert    │  issue short-lived cert
       └──────────────────┴──────────▶ [ Target server ]  # TrustedUserCAKey

Download

Pick your role in the system

macOS builds are signed & notarized — open them directly. Always download through a browser to keep the signature intact.

GatewayAvailable

Server (Linux) — the broker that issues certs

Ubuntu / Debian · amd64.debDownload
Ubuntu / Debian · arm64.debDownload
RHEL / Rocky · x86_64.rpmDownload
RHEL / Rocky · aarch64.rpmDownload

OSH - SignerApp Store

iOS — approves & signs requests (Secure Enclave + Face ID)

iPhone · App StoreApp Store ↗

Live on the App Store — tap the button, or search “OSH Zero Trust SSH” on your iPhone. No invitation needed.

OSH - CACross-platform

Signs certificates with a YubiKey (PIV slot 9c)

macOS · Apple Silicon.dmgDownload
Windows.exeDownload
Ubuntu · amd64.debDownload

OSH - ClientCross-platform

Workstation — where you run ssh to get a cert

macOS · Apple Silicon.pkg · GUI + CLIDownload
Windows.exeDownload
Ubuntu · amd64.debDownload

OSH CLITerminal

Just the osh command — servers / headless / scripts (no GUI)

Ubuntu/Debian · amd64.debDownload
Ubuntu/Debian · arm64.debDownload
RHEL/Rocky · x86_64.rpmDownload
RHEL/Rocky · aarch64.rpmDownload
Windows · x64.zipDownload

macOS: osh is already inside the OSH - Client .pkg above.

OSH - Web AdminHosted

Browser — manage signers, servers, groups and rules

Any browser · nothing to installOpen console ↗

Sign in by scanning the code with OSH - Signer. The browser holds no trusted key: every change waits for a Secure Enclave signature from your phone, and the Audit tab records who approved what.

Compatibility

Which versions go together

Each piece ships on its own cadence — the newest of each always works together, so the Recommended row is the one to install. Pinned to an older build? The rows below are known-good snapshots — every download link is live.

SetGatewayLinux serverOSH - ClientLaptopOSH - CAYubiKey hostOSH - SigneriPhoneOSH CLIHeadless
11 Sep 2026RecommendedCA keeps signing after its window closes on macOS, with a menu bar item showing whether it is unlocked; rotating the CA token no longer locks it. The client remembers the gateway URL across restarts.0.9.01.3.21.3.0App StoreApp Store ↗0.2.5
31 Jul 2026forgetting the YubiKey no longer keeps the console closed0.9.01.3.11.2.2App StoreApp Store ↗0.2.5
30 Jul 2026an opening let in by mistake can be taken back0.9.01.3.11.2.1App StoreApp Store ↗0.2.5
29 Jul 2026an approver can open the firewall from their phone0.7.01.3.01.2.1App StoreApp Store ↗0.2.4
29 Jul 2026machines now record who they belong to0.6.01.3.01.2.1App StoreApp Store ↗0.2.4
29 Jul 2026the gateway refuses an admin write that names no signer0.5.11.3.01.2.0App StoreApp Store ↗0.2.4
28 Jul 2026one gateway package that starts, request traces you can follow, both desktop apps on a runtime macOS accepts0.5.01.3.01.2.0App StoreApp Store ↗0.2.4
27 Jul 2026per-step approval clocks, signer approval history0.4.01.2.191.1.21App StoreApp Store ↗0.2.4
24 Jul 2026CORS for the console header, installer default0.3.151.2.191.1.21App StoreApp Store ↗0.2.4
23 Jul 2026gateway learns who's calling0.3.141.2.161.1.17App StoreApp Store ↗0.2.3
23 Jul 2026every component self-reports its version0.3.131.2.151.1.16App StoreApp Store ↗0.2.2
22 Jul 2026SQLite single-node baseline0.3.121.2.141.1.15App StoreApp Store ↗0.2.1

OSH - Signer is iPhone-only and always the latest App Store build — it can’t be pinned to a past version. Gateway is Linux-only (server); OSH - Client & OSH - CA ship macOS (Apple Silicon), Windows, and Ubuntu amd64.

Install

Get running in a few steps

Four pieces on four machines. The Gateway is a Linux server (the broker); the OSH - Client, OSH - CA & OSH - Signer are cross-platform — see the apps block below. Copy-paste per platform.

gateway — linux server
# ═══ Option A · ONE COMMAND (recommended) — installs, writes a SQLite config, auto-generates a CA token, starts ═══
#   YOUR_SERVER = the address clients reach this box at — its LAN or public IP, or a domain. NOT localhost / 127.0.0.1.
#   e.g.  http://192.168.1.50:8443  (same LAN)   ·   http://203.0.113.10:8443  (public IP)   ·   https://gw.example.com  (domain)
curl -fsSL https://osh.openlay.com/downloads/install.sh | sudo sh -s -- --public-url http://YOUR_SERVER:8443
#   SQLite single-node · no PostgreSQL. Public HTTPS domain? add:  --autocert gw.example.com --public-url https://gw.example.com
#   (bare IP = http only — Let's Encrypt needs a domain.) Then watch for the root-enroll QR:
sudo journalctl -u pam-zta-gateway -o cat --no-pager -n 300   # full QR: -o cat strips the log prefix, -n 300 shows past lines (-f alone tails only 10 → QR cut off) · widen the window if it wraps

# ═══ Updating · already installed? Just re-run the installer (no flags needed) ═══
curl -fsSL https://osh.openlay.com/downloads/install.sh | sudo sh   # upgrades the package in place — your config, CA token, database and keys are kept, the service restarts on the new version

# ═══ Option B · MANUAL (advanced) — if you downloaded the .deb / .rpm above ═══
# 1) Install — --no-install-recommends keeps PostgreSQL OUT (SQLite needs no DB server)
sudo apt install --no-install-recommends ./pam-zta-gateway_0.9.0_amd64.deb            # Ubuntu / Debian
sudo dnf install --setopt=install_weak_deps=False ./pam-zta-gateway-0.9.0-1.x86_64.rpm  # RHEL / Rocky / Fedora

# 2) Point it at SQLite + set a CA token (writes the whole config)
sudo sh -c 'cat > /etc/pam-zta/gateway.toml' <<EOF
[server]
listen_addr = ":8443"
public_url  = "http://YOUR_SERVER:8443"   # IP or domain clients reach (NOT localhost) — goes into the enroll QR
[ca]
token = "$(openssl rand -hex 32)"
[database]
driver = "sqlite"
path   = "/var/lib/pam-zta/gateway.db"
[cert]
ttl = 300
[gateway_key]
key_path = "/var/lib/pam-zta/gateway_key.pem"
EOF
sudo chown root:pam-zta /etc/pam-zta/gateway.toml && sudo chmod 640 /etc/pam-zta/gateway.toml

# 3) Start + watch for the root-enroll QR
sudo systemctl enable --now pam-zta-gateway
sudo journalctl -u pam-zta-gateway -o cat --no-pager -n 300   # full QR: -o cat strips the log prefix, -n 300 shows past lines (-f alone tails only 10 → QR cut off) · widen the window if it wraps
grep token /etc/pam-zta/gateway.toml   # hand this CA token to the CA operator

# ─── Connect to this gateway ───
#   OSH client  →  osh enroll --force --gateway http://SERVER:8443    (first time: register the device)
#                  osh connect root@SERVER --gateway http://SERVER:8443
#   OSH - CA    →  ws://SERVER:8443/v1/ca/ws   +  the [ca].token above
# HA / multi-node? Use PostgreSQL instead of SQLite  ([database] host/user/... ).
desktop & ios — apps
# macOS — OSH - CA (.dmg)
Open the .dmg ▸ drag "OSH - CA" to Applications ▸ open ▸ plug YubiKey ▸ enter PIN (PIV 9c) ▸ "Save PIN to Keychain".

# macOS — OSH - Client (.pkg · all-in-one: GUI app + `osh` command)
Double-click OSH-1.3.2-arm64.pkg ▸ Install.   # notarized · installs the OSH app + the `osh` CLI into PATH
osh connect dev@your-server   # or open the OSH app and click Connect · (new terminal: run `rehash`)

# Windows — OSH - CA / OSH - Client (.exe · code-signed)
Run CA-1.3.0.exe / OSH-1.3.2.exe ▸ if SmartScreen still appears (new OV cert still building reputation) ▸ More info ▸ Run anyway.

# Ubuntu/Debian — OSH - CA / OSH - Client (.deb · amd64)
sudo apt install ./CA-1.3.0-amd64.deb     # OSH - CA (Operator Console)
sudo apt install ./OSH-1.3.2-amd64.deb    # OSH - Client

# iOS (OSH - Signer)
1. Get OSH - Zero Trust SSH from the App Store (button above, or search "OSH Zero Trust SSH").
2. Open OSH ▸ scan the enrollment QR from the Gateway ▸ approve with Face ID.
target server — trust the CA (TrustedUserCAKeys)
# ONE command from your laptop — curl runs LOCALLY and pipes the script over ssh (target needs no internet).
# Fill in 2 things:
#   ubuntu@YOUR_TARGET = the ssh login you ALREADY use on that server (needs sudo) — root@... works too
#   BASE64_FROM_GUI    = OSH - CA ▸ Settings ▸ Export Public Key ▸ Copy — paste as-is (no ssh-ed25519 prefix)
curl -fsSL https://osh.openlay.com/downloads/install-pam-zta-ca.sh | ssh ubuntu@YOUR_TARGET 'sudo bash -s BASE64_FROM_GUI'

# e.g. — server 203.0.113.10, you normally ssh in as root, key copied from the GUI:
curl -fsSL https://osh.openlay.com/downloads/install-pam-zta-ca.sh | ssh root@203.0.113.10 'sudo bash -s AAAAC3NzaC1lZDI1NTE5AAAAIG43Man3DUHiddGccOJM897B/BaSm3tgSh09z3blL/hz'
# Two CA YubiKeys for redundancy (either one signs)? Pass BOTH keys — the target trusts both:
curl -fsSL https://osh.openlay.com/downloads/install-pam-zta-ca.sh | ssh root@203.0.113.10 'sudo bash -s BASE64_CA1 BASE64_CA2'
#   Installs /etc/ssh/pam-zta-ca.pub + a sshd drop-in, validates (sshd -t), then RELOADS — no restart,
#   no dropped sessions, authorized_keys untouched. Idempotent — safe to re-run.
#   Expect:  trustedusercakeys /etc/ssh/pam-zta-ca.pub  ·  the CA fingerprint (match it in the GUI)  ·  INSTALL_OK
# To log in, the cert user must EXIST on the target (e.g. ubuntu) and match an access rule — and port 22 reachable.
# Undo:  sudo rm -f /etc/ssh/sshd_config.d/50-pam-zta.conf /etc/ssh/pam-zta-ca.pub  &&  sudo systemctl reload ssh
using osh — enroll once, then connect
# 1) First time on a gateway: point osh at it, then enroll this device (an admin approves it once).
export OSH_GATEWAY=https://your-gateway:8443   # add to ~/.bashrc → skip --gateway every time
osh enroll --force                          # registers this device · an admin approves it in the App Signer
osh status                                  # optional: expect status:ok, ca_connected:true

# 2) Log in — ONE command. The login user must MATCH an access rule (e.g. root — not just any name):
osh root@10.0.0.5                           # short for:  osh connect root@10.0.0.5
#     • osh asks the Gateway for a certificate for  root@10.0.0.5
#     • your iPhone buzzes → review who / where / what → Approve with Face ID
#     • the CA (YubiKey) signs a short-lived cert → you land in the SSH session ✅
#       (session recorded to ~/.osh/sessions/ · cert expires in minutes · nothing static left behind)

# Rejected?  401 = device not enrolled on THIS gateway → re-run  osh enroll --force
#            403 = device not approved yet, or the login user doesn't match a rule
OSH - CA requires a YubiKey (hardware-only, fail-closed). Pull the YubiKey and signing stops — exactly by zero-trust design.
© 2026 Open layer technologies L.L.C S.O.C · v0.1.0
Need help? ad@openlay.com