Skip to main content

CLI — pc

pc is the command-line tool for Proxima Console. It provides native terminal access, host management, and file transfer from your local shell — no browser needed.

Installation

curl -sSL https://install.prxm.uz/pc | bash

Detects your OS and architecture, downloads the latest binary, verifies the SHA-256 checksum, and installs to /usr/local/bin/pc.

Build from Source

cd cli && go build -o pc ./cmd/pc
sudo mv pc /usr/local/bin/

Authentication

pc login requires you to choose an authentication method with --auth. There is no default — running bare pc login errors and lists the methods this proxy supports.

pc login --auth <google|local|apikey> [--user <email>] [--no-browser]
[--proxy <addr>] [--insecure] [--ca-cert <path>] [--json|--quiet]
MethodCommandUse it for
googlepc login --auth googleGoogle SSO. Delivery (browser vs. paste-the-code) is auto-detected. MFA is handled in the browser/paste flow.
localpc login --auth localEmail + password (+ TOTP/recovery code if enrolled), entered in the terminal.
apikeypc login --auth apikeyA service-account API key, for automation and CI.

The --user flag pre-fills the email for --auth local; --no-browser forces the paste-the-code delivery for --auth google. Each is only valid with its own method — using --user with a non-local method, or --no-browser with a non-google method, is a usage error.

The TLS cert-pinning / trust prompt (see below) runs before method dispatch, exactly as on every login.

--auth google — Google SSO

pc login --auth google

Authenticates via Google SSO. The CLI auto-detects how to deliver the login:

  • Browser + loopback callback when a display is available (macOS, Windows, or Linux/BSD with $DISPLAY or $WAYLAND_DISPLAY set). pc opens your browser and listens on localhost for the callback. If the browser can't open or the listener can't bind, pc automatically falls back to the paste-the-code flow.
  • Paste-the-code on a headless or remote shell (no display), or whenever you pass --no-browser. pc prints the SSO URL — open it on any device, sign in, and paste the one-time code back:
pc login --auth google --no-browser
Open this URL in a browser on any device to log in:
https://api-console.prxm.uz/api/v1/auth/google?cli_manual=1

Paste the code shown in your browser: A1B2C3D4
Logged in as [email protected]

The code is single-use and expires after 5 minutes. If your account has TOTP MFA enabled, the second-factor page is presented in the browser (or the device you opened the paste URL on) right after Google sign-in — enter your authenticator or recovery code there, and the CLI is logged in once it passes.

--no-browser is the single delivery override; you rarely need it, because the headless case is detected automatically.

--auth local — Email + Password

pc login --auth local                       # prompts for email
pc login --auth local --user [email protected] # pre-fill the email

Prompts for the email (unless --user is given) and password. If TOTP MFA is enabled, prompts for a 2FA code (or recovery code) in the terminal:

Email: [email protected]
Password: ********
MFA required (methods: totp)
Enter 2FA code (or recovery code): 123456
Logged in as [email protected]

This flow works fully headless — no browser or display required.

--auth apikey — Service Account Key

pc login --auth apikey

Authenticates with a service-account API key. The key is read from, in order:

  1. the PC_API_KEY environment variable,
  2. piped stdin (echo "$KEY" | pc login --auth apikey), or
  3. an interactive hidden prompt (input is not echoed).
export PC_API_KEY=prxm_your_api_key
pc login --auth apikey

# or pipe it (CI):
echo "$PC_API_KEY" | pc login --auth apikey

API keys bypass MFA and don't expire. This is the method to use in CI and automation.

Logout

pc logout            # revoke the server session, then delete local creds
pc logout --local # delete local creds only (offline / already-revoked)

For token-based logins (google / local), pc logout first calls POST /api/v1/auth/logout to revoke the server session and refresh token, then deletes the stored credentials (the pinned server certificate is kept for TOFU continuity). The server call is best-effort: if it fails (offline, network error), pc prints a warning and still deletes the local credentials, so you're never stranded.

For apikey logins there is no server session to revoke, so pc logout deletes locally only.

Pass --local to skip the server call entirely — useful when you're offline or the session is already revoked.

Migration from the old flags

The old pc login flags (--browser, --manual, --email, --api-key) have been removed in favor of the --auth method selector. Map your old command as follows:

Old flagNew command
pc login --browserpc login --auth google
pc login --manualpc login --auth google --no-browser
pc login --email <e>pc login --auth local --user <e>
pc login --api-key <k>pc login --auth apikey (key via $PC_API_KEY or the hidden prompt)

Bare pc login no longer defaults to an API-key prompt — --auth is now required.

TLS Certificate Pinning

On first login, pc fetches the server's TLS certificate and asks you to verify it:

Server certificate fingerprint:
SHA256:2e:26:57:3b:aa:e0:22:6a:...
Issuer: E7
Subject: api-console.prxm.uz
Expires: 2026-07-09
Trust this server? [y/N]: y
Certificate pinned.

After pinning, every connection — both the HTTPS API calls and the gRPC terminal/port-forward streams — verifies against the stored certificate, not just pc login. If the certificate changes unexpectedly, pc fails closed and refuses to connect, protecting against man-in-the-middle attacks.

If the certificate is rotated legitimately (for example, a renewed certificate), delete the pinned copy and log in again to re-pin the new one:

rm ~/.config/pc/trusted-certs/api-console.prxm.uz.pem
pc login --auth google

For enterprise deployments with private CAs:

pc login --auth google --ca-cert /etc/ssl/corp-ca.pem

The --insecure flag skips TLS verification (prints a warning every time — use only for development).

Commands

pc ls — List Hosts

pc ls                          # all accessible hosts
pc ls --env production # filter by environment
pc ls --project acme-corp # filter by project
pc ls --status connected # filter by agent status
pc ls --wide # show project + environment columns
pc ls --format json # machine-readable output

Output includes live CPU, RAM, and disk metrics. Kubernetes nodes are marked with [k8s]:

ID          HOSTNAME                       STATUS    CPU    RAM    DISK    LAST SEEN
f006bdbb master01-rke2.prxm.uz [k8s] online 25% 70% 9% 5s ago
5d03345f web-prod-01 online 26% 43% 77% 13s ago

Use --wide for project and environment columns:

ID          HOSTNAME                       PROJECT     ENV            STATUS    CPU    RAM    DISK    LAST SEEN
f006bdbb master01-rke2.prxm.uz [k8s] Proxima Production online 25% 70% 9% 5s ago
5d03345f web-prod-01 Internal Development online 26% 43% 77% 13s ago

Host IDs are truncated to 8 characters in table view (full UUIDs in --format json).

pc ssh — Terminal Access

pc ssh root@hostname
pc ssh deploy@web-01
pc ssh hostname # logs in as your local OS user (like ssh)
pc ssh root@5d03345f-xxxx-xxxx-xxxx-xxxxxxxxxxxx # by host ID

Opens an interactive terminal session via gRPC bidirectional streaming. All standard terminal features work: colors, vim, tmux, Ctrl+C, tab completion, window resizing.

If you omit user@, pc uses your local OS username as the remote login, mirroring ssh. The login you request must be permitted by your role's terminal policy — see RBAC. If it isn't, the session is rejected:

Error: session rejected: access denied: role "Team Lead" cannot use login "root" on this host

Running a single command (non-interactive):

Pass a command after the host (like ssh host cmd) to run it without a PTY and exit with the command's remote exit code — ideal for scripts and automation:

pc ssh web-01 uptime
pc ssh root@web-01 -- systemctl is-active nginx # use -- for commands with their own flags
echo "$DATA" | pc ssh web-01 -- 'cat > /tmp/out' # stdin is forwarded
pc ssh web-01 -- 'ls /etc' 2>/dev/null # remote stdout/stderr stay separate
  • Remote stdout and stderr are kept separate, so redirects and pipes behave like real ssh.
  • pc exits with the remote command's exit code — a non-zero remote exit is not a pc error (pc ssh host -- false; echo $? prints 1).
  • stdin is forwarded and closed at EOF, so echo … | pc ssh host -- cat works.
  • Use -- before the command when it contains its own flags (pc ssh host -- ls -la).
  • Combine with -q to suppress the connection banner for clean script output.
  • The same RBAC applies as interactive sessions — your role must permit the login on the host.
  • Interactive programs that need a TTY (e.g. top, vim) aren't supported in command mode yet; open an interactive shell (pc ssh host) for those.

Duplicate hostnames: When multiple hosts share the same hostname, pc ssh shows an interactive picker:

Multiple hosts match "web-01":
1) 3a7f2b1c production connected
2) 9c4e1d8f staging connected
Select [1-2]: 1

In non-interactive mode (piped input), it prints the matching host IDs and exits.

Port forwarding:

pc ssh -L 8080:localhost:80 root@web-server              # terminal + forward
pc ssh -L 5432:localhost:5432 -N root@db-server # forward only, no terminal
pc ssh -L 8080:localhost:80 -L 5432:localhost:5432 root@host # multiple tunnels

The -L localPort:remoteHost:remotePort flag forwards a local port to a remote endpoint through the agent. Multiple -L flags can be combined. The -N flag disables the terminal (forward-only mode — stays alive until Ctrl+C).

Each tunnel supports multiple concurrent TCP connections (e.g. a browser opening parallel HTTP connections).

pc scp — File Transfer

pc scp localfile.conf host:/etc/app/config.conf        # upload
pc scp host:/var/log/app.log ./app.log # download
pc scp localfile.conf user@host:/tmp/file # with login
pc scp localfile.conf 5d03345f-xxxx:/etc/app/config # by host ID

Maximum file size: 768 KB. For larger files, use scp or rsync directly.

Uploaded nginx.conf → host:/etc/nginx/nginx.conf (2.3 KB, sha256:a4f28b...)
Downloaded host:/var/log/app.log → ./app.log (156 KB, sha256:7de13a...)

pc sessions — Session Management

pc sessions ls                 # list active sessions
pc sessions ls --all # include ended sessions
pc sessions kill <session-id> # terminate a session
SESSION ID  LOGIN   STARTED   PEERS  RECORDING
47fa6c5a root 5m ago 0 recording
b2c3d4e5 deploy 22m ago 1 recording

pc sessions join — Join Active Session

pc sessions join <session-id>                     # observe (read-only)
pc sessions join <session-id> --mode participate # can type
pc sessions join 47fa6c5a # short ID prefix

Join an active terminal session as an observer or participant via gRPC. Observers see all terminal output but cannot type. Participants can send input to the session's shell.

Short session ID prefixes work — pc resolves them to the full UUID automatically (errors if ambiguous).

Press q or Ctrl+C to leave the session.

pc play — Play Session Recording

pc play <session-id>                    # play at 1x
pc play <session-id> --speed 2 # start at 2x speed
pc play <session-id> --skip-idle # compress idle gaps > 2s

Downloads and plays back a terminal session recording in the terminal with interactive controls:

KeyAction
spacePause / resume
+ or ]Speed up (1x → 2x → 4x → 8x)
- or [Slow down (8x → 4x → 2x → 1x → 0.5x)
q or Ctrl+CQuit

A status bar at the bottom shows elapsed time, total duration, current speed, and playback state.

Recordings must be in uploaded status (check with pc sessions ls --all). The recording is downloaded from R2 storage, decompressed, and played locally — no streaming required.

pc status — Login Status

pc status

Shows current authentication state: user email, proxy address, auth method, token expiry, and pinned certificate info.

pc version

pc version
# pc v0.5.1 (commit: abc1234, built: 2026-05-27)

Scripting & machine-readable output

Two global flags (available on every command) make pc automation-friendly:

FlagBehaviour
--jsonEmit machine-readable JSON to stdout. On failure, prints {"error":"…"} to stderr and exits non-zero.
--quiet / -qSuppress banners, progress, and prompts (all on stderr). If a command would prompt — an ambiguous hostname or missing credentials — it errors instead of waiting, so commands stay fully non-interactive.
pc ls --json | jq -r '.[] | select(.status=="online") | .hostname'
pc status --json | jq -r .auth_method
pc sessions ls --json
pc version --json

JSON output is a bare value — an array for list commands, an object for single results (the existing pc ls --format json output is unchanged). Failures are uniform and carry a non-zero exit code:

$ pc ls --json        # when not logged in
{
"error": "not logged in — run 'pc login' first"
}
$ echo $?
1

For running remote commands in scripts, pc ssh host -- <cmd> (see the pc ssh section above) streams raw stdout/stderr and propagates the remote exit code, so it composes directly with shell pipelines.

Configuration

Flag / EnvDefaultDescription
--proxy / PC_PROXYapi-console.prxm.uz:443Proxy address (gRPC + HTTP via nginx)
--insecure / PC_INSECUREfalseSkip TLS verification (dev only)
--ca-certPath to CA certificate for TLS (login only)
--jsonfalseMachine-readable JSON output (global)
--quiet / -qfalseSuppress chrome/prompts; never prompt (global)

Credentials stored at ~/.config/pc/credentials.json (mode 0600). Pinned certificates stored at ~/.config/pc/trusted-certs/.

Token Refresh

For SSO and password-authenticated users, pc automatically refreshes expired JWT tokens before each command. No manual re-login needed unless the refresh token itself expires.

API key users are not affected — API keys don't expire.

Architecture

pc CLI ──gRPC/TLS──▶ nginx (:443) ──▶ Backend gRPC (:9090) ──NATS──▶ Agent ──PTY
pc CLI ──HTTPS──────▶ nginx (:443) ──▶ Backend HTTP (:8080)

Both gRPC (terminal streaming) and HTTP (host listing, metrics, file transfer, auth) go through nginx on port 443 with TLS. Nginx terminates TLS and proxies plaintext to the backend.

Session recording, RBAC, audit logging, and host user creation all work identically to the browser terminal.