op is the client. It logs in to obtain a short-lived certificate and connects to
nodes through the proxy — analogous to tsh.
A global --insecure flag skips TLS verification of the proxy (for a self-signed
development control plane). A global --debug flag logs the routed TLS
connection — address, TLS version, negotiated ALPN and certificate fingerprint —
to stderr. See Troubleshooting.
op login [proxy-host:port]
Authenticate and store a profile + certificate. Reuses your saved profile when flags are omitted.
| Flag | Default | Purpose |
|---|---|---|
--proxy |
— | proxy address host:port (or pass positionally) |
--user |
$USER |
username |
--oidc |
— | log in via an OIDC connector instead of a password |
--grpc |
auto | gRPC address for login: the proxy address itself when the server supports TLS routing, else proxy host :3025 |
--ttl |
8h |
requested certificate lifetime |
op login gate.acme.com:8443 --user alice
op login gate.acme.com:8443 --oidc okta # browser-based OIDC
op ls
List the nodes you can reach with your current certificate.
op ssh [login@]node [command...]
Open an SSH session to a node through the proxy, or run a single command.
When op ssh is run without a target in an interactive terminal, it opens a
picker for a reachable node and then a permitted login.
op ssh deploy@web-1
op ssh deploy@web-1 -- systemctl status nginx
op ssh # pick a node, then a login
op cp [-r] <src> <dst>
Copy files to or from a node. Exactly one side is remote, written
[login@]node:path; a relative remote path resolves against the login's home
directory.
| Flag | Default | Purpose |
|---|---|---|
-r, --recursive |
false |
copy directories recursively |
op cp ./app.tar deploy@web-1:/opt/app/app.tar
op cp deploy@db-1:/var/log/db.log ./db.log
op cp -r ./build deploy@web-1:/opt/app
Transfers use the node's SFTP subsystem, so the node agent must be recent enough
to serve it. For anything beyond a straight copy — resumption, deltas, excludes —
set up op ssh-config and use rsync or scp
directly.
op forward [login@]node [bind:]localport:host:remoteport ...
Forward local TCP ports to addresses reachable from a node, the equivalent of
ssh -N -L. Listens on 127.0.0.1 unless a bind address is given, and runs
until interrupted. Several forwards can share one connection.
op forward web-1 8080:localhost:80
op forward db-1 5432:localhost:5432 6379:cache.internal:6379
Local forwarding only — the node never listens on your behalf, so there is no
remote (-R) or SOCKS (-D) equivalent. Forwarding requires a certificate
carrying the port-forwarding permission; if yours predates it, run op login
again.
op ssh-config [host-pattern...]
Generate an OpenSSH client configuration so ordinary tools connect through
overpass: ssh, scp, rsync, Ansible, and editors that speak SSH such as
VS Code Remote.
| Flag | Default | Purpose |
|---|---|---|
--write |
false |
write ~/.overpass/ssh_config and include it from ~/.ssh/config |
op ssh-config # print the block for the nodes you can see
op ssh-config --write # install it
op ssh-config 'web-*' db-1 # explicit patterns instead of querying the proxy
ssh web-1 # then just use ssh
rsync -a ./build/ web-1:/opt/app/
The generated block points ProxyCommand at op proxy
and reuses the certificate op login already stored, so nothing is duplicated.
It also writes a known_hosts file trusting the cluster Host CA, which is how
ssh verifies node identity without prompting.
The configuration keeps working across logins, but the certificate inside it
expires: when ssh starts refusing to authenticate, run op login again. Re-run
op ssh-config --write after enrolling nodes so the new names are covered (or
install a wildcard pattern once).
op proxy [login@]node
Bridge stdin/stdout to a node's SSH server. This exists to be used as a
ProxyCommand — see op ssh-config, which writes
it for you — and is rarely run by hand. stdout carries the SSH byte stream, so
all diagnostics go to stderr.
op status
Show the current login: proxy, user, roles, logins and certificate expiry.
op update
Replace this binary with the latest published release. The download is verified against the release checksums before anything is installed, so a failed or tampered download leaves the existing binary untouched.
| Flag | Default | Purpose |
|---|---|---|
--check |
false |
report whether a newer release exists, install nothing |
--to |
latest | install an exact version instead of the latest |
--force |
false |
reinstall even when already up to date |
--base-url |
built-in | override the release download URL |
op update --check
op update
op update --to 0.1.9 # pin, or roll back
opctl update works identically.
Binaries are fetched from the release they were published to. The repository
URL is baked into each release build; --base-url or OVERPASS_UPDATE_BASE_URL
override it for a private mirror. Only https is accepted (loopback aside),
because the checksum that guards the download travels the same channel.
If the binary lives somewhere you cannot write — /usr/bin from a package, for
example — the update fails with that explanation rather than a bare permission
error. Use your package manager, or re-run with sudo.
Once a day, an interactive op or opctl also notes on stderr when a newer
release exists. Set OVERPASS_NO_UPDATE_NOTIFIER=1 to turn that off; it is
already skipped for development builds and when output is not a terminal.
op logout
Remove the local certificate and profile.