Using native SSH tools through pc
This guide shows how to connect to managed hosts with native OpenSSH tools —
ssh, one-shot remote commands, ansible, and (in later phases) scp/rsync
and VS Code / Cursor Remote-SSH — routed through pc with the same guarantees as
pc ssh: no host sshd, no host SSH credentials, RBAC-gated, session-recorded.
For how it works under the hood (the SSH CA, the transparent relay, why the agent terminates SSH), see OpenSSH Integration — Architecture.
Run pc login then pc config once. After that, ssh <login>@<host>.<proxy>
(and ansible, remote commands) Just Work — gated by your terminal RBAC and
recorded for replay with pc play. Nothing is installed or exposed on the target.
One-time setup
# 1. Authenticate (Google SSO, local, or a service-account API key)
pc login --auth google
# 2. Emit the OpenSSH config + trust + a short-lived user certificate
pc config --write
pc config --write does three things:
| It writes | Purpose |
|---|---|
~/.ssh/config.d/pc (a Match exec hook + a Host *.<proxy> block) | refreshes your short-lived cert before each connection, routes matching hosts through pc proxy ssh as an OpenSSH ProxyCommand, and points ssh at your user cert |
~/.config/pc/known_hosts (@cert-authority *.<proxy>) | trusts the Proxima Host CA, so there are no host-key prompts and no per-host known_hosts churn |
~/.config/pc/keys/id_pc-cert.pub | your short-lived user certificate (principals = your allowed logins; embeds your per-host RBAC) |
The user cert is short-lived (1 hour) and auto-refreshed — you don't manage it. The emitted config begins with a line like:
Match host *.<proxy> exec "pc proxy refresh-cert"
OpenSSH runs that hook before it loads your certificate for a connection, so
pc re-issues the cert just-in-time and the fresh one is what's offered to the
agent. (It must run there: a refresh inside the ProxyCommand happens after
OpenSSH has already loaded the cert, so it can't help the current connection.)
The hook is a quiet no-op — no network call — when the cert is still fresh.
<proxy> is your Console host (e.g. api-console.prxm.uz); ensure your OpenSSH
~/.ssh/config includes the config.d directory (Include ~/.ssh/config.d/*),
which pc config sets up.
If you set up native SSH before this auto-refresh hook existed, your cert will
expire after an hour and ssh will fail with Permission denied (publickey).
Re-run pc config --write (or pc config >> ~/.ssh/config) once to install
the Match exec line — after that, refresh is automatic.
What happens on connect
Use cases
✅ Interactive shell — built
You get a full PTY as deploy on web01, recorded end-to-end. The login
(deploy) must be in your cert's principals and permitted on that host by
your terminal RBAC (see Who can log in where).
✅ One-shot remote command — built
ssh [email protected] 'systemctl status nginx'
ssh [email protected] 'pg_isready'
Non-interactive exec — ideal for scripts and health probes. Output and exit
code propagate normally; the command is recorded too.
✅ Ansible (raw / command / shell modules) — built
Point Ansible at the proxied hosts and it uses your pc-issued cert via the
ProxyCommand automatically:
# inventory.ini
[web]
web01.api-console.prxm.uz ansible_user=deploy
ansible web -i inventory.ini -m raw -a 'uptime'
ansible web -i inventory.ini -m command -a 'systemctl is-active nginx'
Module types that run a command over an exec channel work today. Modules that
copy files (copy, template, synchronize, fetch) need the SFTP subsystem —
see below.
⏳ scp / rsync (file copy) — Phase 3 (SFTP subsystem)
scp ./app.tar.gz [email protected]:/tmp/ # planned
rsync -az ./site/ [email protected]:/var/www/ # planned
These need the agent's SSH SFTP subsystem, which lands in Phase 3. The recorded, RBAC-gated model is identical — only the channel type is new.
⏳ VS Code / Cursor Remote-SSH, DB GUIs — Phase 4 (direct-tcpip)
Remote-SSH and TCP port-forwarding (e.g. a DB client tunnelled over SSH) need
direct-tcpip channels, which land in Phase 4. After that, "Connect to Host →
web01.api-console.prxm.uz" works with no extra setup.
Who can log in where
Access is decided in two places, both from your terminal RBAC (the
terminal_spec on your roles):
- Cert principals = your role-wide allowed logins (e.g.
deploy,readonly). You can only requestssh <login>@…for a login in this set. - Per-host check (at the agent): the agent re-evaluates the spec embedded in
your cert against the target host's tags — so a role can allow
rootonly onenv=staginghosts,deployeverywhere, etc. This is the authoritative check and exactly matchespc ssh.
An unauthorized login or host is denied at the agent, even if the cert is
otherwise valid. Cert issuance itself requires terminal:write (super admins and
wildcard service accounts included).
Recordings
Every shell and command session is recorded as an asciicast and uploaded, just
like pc ssh. Replay with:
pc play <session-id>
Recording is fail-closed: if your role requires recording but the agent can't record, the session is refused rather than run unrecorded.
pc play) — in progressSSH-CA sessions are captured on the agent today, but surfacing them in the
console for pc play replay is still being wired up (the recording pipeline is
shared with pc ssh, which pre-creates a session record the native-SSH path does
not yet). Until then, native-SSH sessions are recorded but not yet replayable from
the console.
Automation with a service account
Use a service-account API key (with a role that grants terminal:write + a
terminal spec) for non-interactive automation:
PC_API_KEY=… pc login --auth apikey
pc config --write
ansible all -i inventory.ini -m raw -a 'uptime'
Troubleshooting
| Symptom | Cause / fix |
|---|---|
you do not have terminal access on pc config | Your account/role lacks terminal:write, or its terminal spec resolves no logins. Grant terminal access on the relevant client. |
Permission denied (publickey) | Either the login you used isn't in your cert principals / isn't allowed on that host by your spec (check ssh-keygen -L -f ~/.config/pc/keys/id_pc-cert.pub), or your cert expired and your config predates the auto-refresh hook — re-run pc config --write once to install the Match exec line (see Upgrading above). |
proxima: failed to start session (unknown user …) | The login you connected as isn't an OS user on that host. Native SSH does not auto-create users — use a login that exists there (e.g. root, or a provisioned account). |
| Host-key / certificate warning | Re-run pc config to refresh the @cert-authority line; ensure you connect via the *.<proxy> name, not a raw IP. |
agent did not respond / host offline | The target host's agent isn't connected. Check pc ls. |
server certificate has changed on any pc command | The Console's TLS cert rotated. Remove the stale pin (~/.config/pc/trusted-certs/<host>.pem) and pc login again to re-verify. |
What's built today
| Capability | State |
|---|---|
Interactive shell · one-shot command · ansible (raw/command/shell) | ✅ built |
Per-host RBAC + session recording (replay with pc play) | ✅ built |
scp / rsync (SFTP subsystem) | ⏳ Phase 3 |
| VS Code Remote-SSH · DB-GUI tunnels (direct-tcpip) | ⏳ Phase 4 |