Start here
Getting started
Connect inside the disposable guest: identities, authorization, host trust, and a first session.
60c72c2bView original On this page
This walkthrough connects to a daemon on guest loopback. Build and install in the disposable VM first. Run the following commands inside the guest, reached with:
tools/vm/vm.sh ssh
The SSH session is root in the disposable guest. Start in /work/psh.
Prepare the server
Install the binaries and configuration template:
cd /work/psh
make install
The default template authorizes alice. It must be an existing non-root Linux
account with a usable home, executable login shell, and no account or password
expiry. UID 65534 is reserved for network workers and cannot log in. If this
test account does not yet exist, create it in the disposable guest:
useradd --create-home --shell /bin/bash alice
Generate a dedicated host identity explicitly:
psh keygen -f /etc/psh/host_psh_hybrid.pem --no-passphrase
The daemon never generates keys on startup. Host keys are unencrypted for
unattended startup and must remain root-owned with mode 0600 or stricter.
The public companion is /etc/psh/host_psh_hybrid.pem.pub. Each identity
contains an Ed25519 and an ML-DSA-87 key; no X.509 certificate is needed.
Key generation refuses to overwrite existing files.
Create and authorize a client identity
For this walkthrough the guest’s root account runs the client and logs in
remotely as alice. The client identity is separate from the host identity:
install -d -m 0700 "$HOME/.config/psh"
psh keygen -f "$HOME/.config/psh/id_psh_hybrid.pem"
install -m 0644 "$HOME/.config/psh/id_psh_hybrid.pem.pub" \
/etc/psh/authorized/alice.pub.pem
Key generation prompts twice for a nonempty local passphrase. Use
--no-passphrase explicitly for an unattended test client. Only the public
companion belongs in the authorized directory. These are paired SPKI PEM
files, not OpenSSH authorized_keys lines.
Check /etc/psh/pshd.yaml; the installed template contains:
version: 1
listen:
- '0.0.0.0:2222'
- '[::]:2222'
host_key: host_psh_hybrid.pem
users:
alice:
authorized_keys:
- authorized/alice.pub.pem
Paths in YAML resolve relative to that configuration file. Keep the server configuration and public authorization files root-owned and not group/world writable, and their directories administrator-controlled. Neither password authentication nor root login has an enabling option.
Validate the configuration and all referenced keys, then start the daemon:
/usr/local/sbin/pshd -f /etc/psh/pshd.yaml --check-config
/usr/local/sbin/pshd -f /etc/psh/pshd.yaml
The daemon stays in the foreground and logs to stderr. Leave this session open. It needs Linux 5.14 or newer and writable cgroup v2 support; see runtime requirements .
Configure and connect
Open a second guest SSH session with tools/vm/vm.sh ssh. Create
~/.config/psh/config.yaml, owned by the client account and not group/world
writable:
version: 1
identity: id_psh_hybrid.pem
known_hosts: known_hosts
hosts:
lab:
hostname: 127.0.0.1
user: alice
port: 2222
chmod 0600 "$HOME/.config/psh/config.yaml"
Before connecting, obtain the server fingerprint through a trusted channel. In this single-guest walkthrough, inspect the public file directly:
psh fingerprint /etc/psh/host_psh_hybrid.pem.pub
psh lab
Compare the displayed complete-pair fingerprint and type yes at the first-use
prompt only after verification. Subsequent connections use the saved pin.
Changed host keys always fail.
psh lab -- 'uname -a; id'
psh -t lab -- 'stty size'
psh -T lab -- cat < input.bin > output.bin
Command arguments are joined with spaces and interpreted by the remote
account’s shell using -c; quote a complete command when it contains shell
syntax. With no command, psh requests a login shell. PTYs are automatic only
when no command is supplied and local stdin is a terminal. Use -t to force
one or -T for separate binary streams.
Provision trust for automation
Obtain the complete host public identity through a trusted channel. Pin the actual hostname and port used by the connection; host-management commands do not expand YAML aliases or read the client configuration.
Batch mode needs an unencrypted client identity generated with the explicit
--no-passphrase option earlier, plus either a saved pin or a matching
--host-key file. It cannot unlock an encrypted identity or prompt for unknown
hosts. With that prerequisite:
psh host trust 127.0.0.1 --host-key /etc/psh/host_psh_hybrid.pem.pub
psh --batch lab -- 'id -u'
On a separate machine, copy only the verified host public file to a client-owned protected location.
To deliberately rotate a pin, verify the replacement independently, then use:
psh host forget 127.0.0.1 -p 2222
psh host trust 127.0.0.1 -p 2222 --host-key replacement-host.pem.pub
If configuration uses a custom trust store, supply that same --known-hosts
path to both commands. Keep its protected .lock sidecar alongside the store.
Operate a session and daemon
Keepalives run every 15 seconds. After 120 seconds without authenticated peer
traffic, the connection expires and its remote process group is terminated.
Client address changes preserve a live session after encrypted return-path
validation. SIGUSR1 requests a fresh client UDP socket; SIGWINCH forwards
terminal dimensions. Reinvoking psh starts a new session, not a reattachment.
Send SIGHUP to the daemon to validate and reload configuration and keys.
Invalid replacements leave the old configuration active. Removed identities
terminate their sessions, pending authentications are cancelled, and host-key
rotation closes existing sessions. Listener changes require a restart.
SIGINT or SIGTERM shuts down the daemon and its sessions.
For the complete option, signal, and exit-status reference, see psh(1) , pshd(8) , and psh_config(5) .