CLI Reference
pcmd is the only executable PocketCmd Server ships. With no arguments it runs the first-run flow or the server itself; subcommands manage the service, devices, team members, vaults, metric requests, configuration and backups. Commands that read or change state work directly on ~/.pocketcmd/state.db, so run them as the user that owns the server.
pcmd [flags]pcmd <command> [arguments] [flags]Every command answers --help with a description and examples, and pcmd help <command> does the same. Commands that list things accept --json for scripts. Commands that change or remove something ask for confirmation and accept -y / --yes to skip it; with --non-interactive, or when stdin is not a terminal, they refuse unless --yes is given.
Global flags
Section titled “Global flags”These flags are accepted by every command.
| Flag | Default | Description |
|---|---|---|
-p, --port <n> |
8767 |
Server port. The value chosen on the first run is saved in config.json and used by every later command unless you pass the flag again. |
--host <addr> |
the interface picked on the first run | Bind address: an IP address of this machine, or 0.0.0.0 (:: to include IPv6) for every interface; hostnames are refused. The server never listens on every interface unless you pass it. Saved on the first run like --port. |
--no-qr |
false |
Never draw QR codes; print the URL only. |
--qr-invert |
false |
Swap the dark and light modules of the QR code, for a dark terminal without color support. |
-v, --version |
Print pcmd <version> (commit <hash>, built <date>). |
|
-h, --help |
Print help for the command. |
--foreground, --pair-timeout, --advertise-ip, --debug-log, --non-interactive, --json and --yes belong to the commands that use them and are listed with each command.
Exit codes
Section titled “Exit codes”| Code | Meaning |
|---|---|
0 |
Success. |
1 |
Error, printed on stderr as Error: .... |
2 |
pcmd status: the service is installed but not running. |
3 |
pcmd status: no service is installed. |
130 |
Interrupted with Ctrl+C while waiting (first run, sync, logs -f, audit -f). |
Server
Section titled “Server”On the first run (no ~/.pocketcmd/config.json), creates the server identity, prints a pairing QR code, waits for the first device, then starts the server the way you chose: as a background service, or in the foreground with --foreground. A new code replaces the old one every 60 seconds while it waits. If no device pairs, nothing is saved and the next pcmd starts the first run again. Server Installation describes each step.
This is the only way to pair the first device. Further devices and people join with pcmd invite or from the app.
On later runs in background mode, pcmd installs the service if it is missing, starts it if it is stopped, and prints its status:
╭────────────╮ │ >_ │ ╰────────────╯ POCKETCMD 1.0.0
Status: running (pid 48213) Port: 8767 Devices: 2 paired
pcmd invite Invite a device or team member pcmd status Detailed status pcmd uninstall Stop and remove serviceIf the service cannot be installed or started, pcmd says why and runs the server in the foreground instead. In foreground mode (chosen on the first run, or --foreground), it runs the server until you press Ctrl+C. The installed service starts the binary this way.
On systemd and macOS, pcmd refuses to run as root or under sudo, because the service and ~/.pocketcmd belong to the user who installs them.
| Flag | Default | Description |
|---|---|---|
--foreground |
false |
Run the server in the foreground instead of installing a service. |
--pair-timeout <duration> |
5m |
How long the first run waits for a device. 0 waits forever. |
--advertise-ip <addr> |
the bind address | IP address or hostname to put in the QR code and URL instead, for example a public address or a dynamic-DNS name behind NAT. Saved for later commands. |
--debug-log <path> |
none | Also write the structured JSON log to this file, rotated at 100 MB. |
--non-interactive |
false |
Never prompt: listen on the first detected address and wait for a device. |
pcmdpcmd --port 9000 --host 10.8.0.1pcmd --non-interactive --no-qr --pair-timeout 0pcmd status
Section titled “pcmd status”Prints the service status: running (with its PID) or stopped, the port, the number of active paired devices, the service mode, on Linux whether lingering is enabled, and the paths of config.json, the data directory and, on macOS, server.log. With no service installed it prints Status: not installed. The exit code says the same for scripts: 0 running, 2 stopped, 3 not installed.
| Flag | Default | Description |
|---|---|---|
--json |
false |
Print one JSON object with version, installed, running, pid, port, paired_devices, service_mode, config_path, data_dir, stdout_log, linger and service_state. |
pcmd install
Section titled “pcmd install”Writes the service definition for the detected init system and starts it. The first run calls this for you; run it by hand after pcmd uninstall or on a restored data directory. See Background service for the files it writes. With OpenRC it needs sudo; on systemd and macOS it refuses to run as root or under sudo.
On a host where no device has paired yet it refuses with pair a device first. Run pcmd first.
| Flag | Default | Description |
|---|---|---|
--allow-unpaired |
false |
Install even though no device has paired yet. |
pcmd uninstall
Section titled “pcmd uninstall”Stops the service and removes its definition. Asks for confirmation. The binary and ~/.pocketcmd are left in place. In background mode, the next pcmd installs the service again.
| Flag | Default | Description |
|---|---|---|
-y, --yes |
false |
Skip the confirmation. |
--non-interactive |
false |
Never prompt; refuse unless --yes is given. |
pcmd start
Section titled “pcmd start”Starts the installed service and waits for it to answer /api/health. Fails if no service is installed.
pcmd stop
Section titled “pcmd stop”Stops the service and keeps it installed. pcmd start, or the next plain pcmd, brings it back. Stop the service before pcmd restore.
pcmd restart
Section titled “pcmd restart”Stops and starts the service. The service definition is rewritten only when it no longer matches this binary, port and bind address, for example after the binary moved or --port or --host changed.
pcmd logs
Section titled “pcmd logs”Prints the server’s structured log, the same entries the owner sees in the app under Server logs. The log is kept in state.db (roughly the last 10,000 entries) and printed oldest first as time LEVEL message key=value ....
The process’s own output (crash messages, startup errors) is not part of it. On systemd it goes to the journal (journalctl --user -u pocketcmd), on macOS to ~/.pocketcmd/server.log.
| Flag | Default | Description |
|---|---|---|
-f, --follow |
false |
Keep printing new entries until Ctrl+C. |
-n, --lines <n> |
50 |
Number of recent entries to show. |
--since <duration> |
none | Only entries newer than this, for example 30m or 2h. |
--level <level> |
none | Only entries at this level or above: debug, info, warn or error. |
--json |
false |
Print the raw JSON entries, one per line. |
--file <path> |
none | Read a --debug-log file instead of the store; -f follows it across rotation. |
pcmd logs -f --level warnpcmd logs --since 2h --jsonDevices and pairing
Section titled “Devices and pairing”pcmd list-devices
Section titled “pcmd list-devices”Lists the active devices under a header row: the start of the public-key fingerprint, the owning user, the device name, its status and when it paired. Prints No active devices. when there are none.
FINGERPRINT USER DEVICE STATUS PAIRED REMOVEDa1b2c3d4e5f6... alice iPhone 15 active 2026-03-15T10:30:00Z -f6e5d4c3b2a1... bob iPad Pro left 2026-03-20T14:45:00Z 2026-04-02T09:12:00Z| Flag | Default | Description |
|---|---|---|
--all |
false |
Also list revoked and left devices, with the time they were removed. |
--json |
false |
Print a JSON array with the full fingerprint, user, names, status and timestamps. |
Status is active, left or revoked. See Device states.
pcmd revoke-device [fingerprint-prefix]
Section titled “pcmd revoke-device [fingerprint-prefix]”Sets a device to revoked. The argument matches the start of the public-key fingerprint or the device ID and must be at least 8 characters. Without an argument, a numbered picker lists the devices; with --non-interactive the argument is required. Asks for confirmation.
Revocation takes effect on the device’s next request, and the key can never pair again: the app has to be reinstalled before the device can join with a new invite. Revoking a left device makes its removal permanent the same way, which is what you want for a lost or stolen device. The command reminds you that removing a device in the app also publishes a signed revocation that stops your other devices from sharing vault keys with it, which the server cannot do on its own.
| Flag | Default | Description |
|---|---|---|
-y, --yes |
false |
Skip the confirmation. |
--non-interactive |
false |
Never prompt; requires the argument and --yes. |
pcmd rename-device <fingerprint-prefix>
Section titled “pcmd rename-device <fingerprint-prefix>”Changes a device’s name, label or both. The prefix is resolved like revoke-device. Each value is at most 128 characters; longer values are refused. The app shows the new name on its next device listing.
| Flag | Default | Description |
|---|---|---|
--name <text> |
none | New device name. Must not be empty. |
--label <text> |
none | New device label. An empty value clears it. |
pcmd rename-device a1b2c3d4 --name "Alice's iPhone"pcmd rotate-server-cert
Section titled “pcmd rotate-server-cert”Replaces the server’s identity certificate and gets every device onto the new one. Asks for confirmation, then:
- Deletes
tls/server.crtandtls/server.key, generates a new certificate and prints the start of its fingerprint. - Restarts the background service so it serves the new certificate. Without a service, it tells you to stop your foreground server with Ctrl+C and start it again with
pcmd. - Prints a re-pair QR code and
pocketcmd://URL with the new fingerprint:Scan this code in the app on every paired device to re-pair it:.
Every device pinned the old fingerprint and cannot connect until it scans the code. The code does not expire and can be scanned by every device. It only works for devices that are registered and active: the server re-issues their certificate after checking that the request is signed with the device’s registered key, and they keep their user and role. It pairs no new devices. See QR Code Pairing.
| Flag | Default | Description |
|---|---|---|
-y, --yes |
false |
Skip the confirmation. |
--non-interactive |
false |
Never prompt; refuse unless --yes is given. |
--advertise-ip <addr> |
saved address | IP address or hostname for the re-pair URL. |
pcmd sync
Section titled “pcmd sync”One-shot export of the SSH hosts in this user’s ~/.ssh/config, with the key files they reference, to one device. The command starts a temporary server on a random free port (or --port if given), prints a QR code, serves the configuration once to the device that scans it and exits. The hosts are read with ssh_config rules; see SSH Config Import.
The app accepts this QR code on the free plan; pairing a server does not apply here.
| Flag | Default | Description |
|---|---|---|
--timeout <duration> |
60s |
How long to wait for a device to fetch. |
--advertise-ip <addr> |
saved address | IP address or hostname for the sync URL. |
--debug-log <path> |
none | Also write the temporary server’s structured log to this file. |
The first device to pair is the team owner. The owner alone can transfer ownership, create admin invites and read the audit and server logs in the app. Approving custom metric commands needs metrics.commands.create: the owner holds it, and admins and members hold it only when the owner grants it. The CLI acts with operator authority: whoever can run it on the host already controls state.db.
pcmd invite
Section titled “pcmd invite”Creates an invite and prints its code, expiry, a QR code and a pocketcmd://invite?... URL. The invitee joins in the app under Teams › Join a team or pastes the URL under Paste URL. Refused until the owner’s device has paired.
| Flag | Default | Description |
|---|---|---|
--role admin|member |
member |
Role of the new user. Ignored with --for. |
--for <user> |
none | Add a device for an existing user (user ID or display name). The device inherits that user’s role. |
--expires <duration> |
24h |
Validity, for example 48h. Clamped to 30 days, with a note when it was reduced. |
--pin-device <fingerprint> |
none | Accept the invite only from the device with this public-key fingerprint. The app shows a device’s fingerprint on the Join a team screen. |
--label <text> |
none | Note for your own records; not sent to the invitee. |
--advertise-ip <addr> |
saved address | IP address or hostname for the invite URL. |
pcmd invite --expires 48h --label "Alice's phone"pcmd invite --role admin --label "Backup admin"pcmd invite --for alice --label "Alice's tablet"Invite codes are 16 lowercase base32 characters (80 bits) and can be redeemed once.
pcmd list-invites
Section titled “pcmd list-invites”Lists outstanding invites with their code, role, expiry, target (the user ID for a second-device invite, otherwise (new user)) and label. invites is an alias. Accepts --json.
pcmd revoke-invite <code-or-prefix>
Section titled “pcmd revoke-invite <code-or-prefix>”Cancels an unredeemed invite. A prefix must match exactly one invite. Asks for confirmation; accepts --yes and --non-interactive.
pcmd list-users
Section titled “pcmd list-users”Lists users with their ID, display name, role (owner, admin or member), status and number of active devices. Accepts --json, which also lists each user’s permissions.
pcmd revoke-user <user> [--purge-data]
Section titled “pcmd revoke-user <user> [--purge-data]”Revokes a user, every active device of that user, the invites addressed to them and the invites they created. <user> is a user ID or a display name. The owner cannot be revoked, and neither can the last active admin. Asks for confirmation; accepts --yes and --non-interactive.
--purge-data also deletes the sync records the user’s devices wrote: they become tombstones immediately, so your remaining devices sync the deletion, and the tombstones are removed by the regular cleanup after 365 days. Records in team vaults and permanent device-revocation records are kept.
pcmd promote <user>
Section titled “pcmd promote <user>”Makes a member an admin. Admins hold every permission except metrics.commands.create, which they keep only if it was granted; the member’s other grants no longer apply.
pcmd demote <user>
Section titled “pcmd demote <user>”Makes an admin a member. Refused for the owner and for the last active admin. Asks for confirmation; accepts --yes and --non-interactive.
pcmd transfer-ownership <user>
Section titled “pcmd transfer-ownership <user>”Makes another active member the owner. The new owner becomes an admin with the owner role, and the previous owner stays an admin. The change is atomic and takes effect at once. This is the same operation as Transfer ownership on a member’s page in the app. Asks for confirmation; accepts --yes and --non-interactive.
pcmd rename-user <user> <new-display-name>
Section titled “pcmd rename-user <user> <new-display-name>”Changes a member’s display name as shown in the app and the CLI.
pcmd grant <user> <permission>
Section titled “pcmd grant <user> <permission>”Grants one permission. The owner holds every permission and admins hold every one except metrics.commands.create, so the other keys can be granted to members only; anything granted to the owner is refused. metrics.commands.create can be granted to admins and members. The keys correspond to the switches on a member’s page in the app:
| Key | In the app |
|---|---|
metrics.read |
View metrics |
metrics.commands.read |
View command definitions |
metrics.commands.create |
Add monitoring commands. Approve and decline metric-command requests, and see every member’s requests. Only the owner grants it in the app, to admins or members. Granting it lets that person run code on the host. |
metrics.commands.update |
Edit monitoring commands |
metrics.commands.delete |
Remove monitoring commands |
alerts.read |
Receive alerts. Limit it to some metrics with --metrics. |
alerts.create, alerts.update, alerts.delete |
Add alert rules, Edit alert rules, Remove alert rules |
sshkeys.sync |
Sync SSH keys |
users.manage |
Manage members |
invites.manage |
Manage invites |
invites.device |
Additional devices |
New members start with metrics.read, metrics.commands.read, alerts.read (all metrics), sshkeys.sync and invites.device.
| Flag | Default | Description |
|---|---|---|
--metrics <ids> |
all metrics | For alerts.read only: comma-separated metric IDs the member receives alerts for. |
pcmd grant bob invites.managepcmd grant bob alerts.read --metrics custom:req-1a2b3c4d5e6f7a8bpcmd revoke-permission <user> <permission>
Section titled “pcmd revoke-permission <user> <permission>”Takes one permission away from a member, or metrics.commands.create from an admin. The keys are the same as for grant.
Vaults
Section titled “Vaults”pcmd vaults
Section titled “pcmd vaults”Lists the shared vaults the server knows from their access lists, with the number of members who hold access. The contents are ciphertext the server cannot read. Accepts --json.
pcmd vault-members <vault-id>
Section titled “pcmd vault-members <vault-id>”Lists who has access to a vault and at which level: view, edit or manage. Accepts --json.
Metric requests
Section titled “Metric requests”A metric request proposes a shell command for the server to run on a schedule. Members file requests from the app; nothing runs until a request is approved. See Monitoring.
pcmd requests
Section titled “pcmd requests”Lists the requests with their ID, status (pending, approved or declined), label, requester, creation time and command. Accepts --json.
pcmd requests approve <request-id>
Section titled “pcmd requests approve <request-id>”Approves a pending request. The command is added to the monitoring configuration and the running server starts collecting it. In the app, approving needs metrics.commands.create; the CLI acts as the operator. The decision is written to the audit log.
pcmd requests decline <request-id>
Section titled “pcmd requests decline <request-id>”Declines a pending request. The requester sees the decision in the app, and nothing runs.
Configuration and audit
Section titled “Configuration and audit”pcmd config get
Section titled “pcmd config get”Prints the saved configuration: service_mode, port, host and advertise_ip, with defaults for unset values. Accepts --json. See Configuration file.
pcmd audit
Section titled “pcmd audit”Prints the newest entries of ~/.pocketcmd/audit.log: pairings, invites, role and permission changes, ownership transfers, device removals and renames, metric-command decisions and SSH config exports. Prints No audit log yet. when nothing has been recorded.
| Flag | Default | Description |
|---|---|---|
-f, --follow |
false |
Keep printing new entries until Ctrl+C. |
-n, --lines <n> |
50 |
Number of recent entries to show. |
--since <duration> |
none | Only entries newer than this. |
--json |
false |
Print the raw JSON entries, one per line. |
Backup and restore
Section titled “Backup and restore”pcmd backup [output-path]
Section titled “pcmd backup [output-path]”Writes a gzip-compressed tar archive of ~/.pocketcmd: configuration, server certificate and key, both databases and the audit log. The databases are snapshotted consistently, so the server may keep running. The default file name is pocketcmd-backup-<date>.tar.gz in the current directory.
The archive contains the server’s private key and the device registry, so it is written with mode 0600. Store it accordingly.
pcmd restore <backup-path>
Section titled “pcmd restore <backup-path>”Restores ~/.pocketcmd from a backup. In this order, it:
- Validates the archive: a gzip tarball whose entries all sit under
.pocketcmd/(no../, no symlinks) and that containsstate.dbandtls/server.key. - Refuses while the background service runs (
pcmd stopfirst) or while any server answers/api/healthon the configured port. - Asks before overwriting an existing data directory.
Every restored file gets mode 0600.
| Flag | Default | Description |
|---|---|---|
-y, --yes |
false |
Overwrite an existing ~/.pocketcmd without asking. |
--force |
false |
Same as --yes. |
--non-interactive |
false |
Never prompt; refuse to overwrite unless --yes is given. |
pcmd stoppcmd restore ~/pocketcmd-backup.tar.gz --yespcmd startpcmd completion bash|zsh|fish|powershell prints a shell completion script.
Pairing URL format
Section titled “Pairing URL format”pocketcmd://<host>:<port>/<pairing_token>#<server_fingerprint>host: the advertised address. IPv6 addresses are bracketed, for example[fd00::1].pairing_token: 32 hex characters, single use, valid for 60 seconds, held only in memory.server_fingerprint: SHA-256 of the server certificate. The app pins it at pairing time and rejects any other certificate afterwards.
Invites use pocketcmd://invite?host=<host>&port=<port>&code=<code>#<server_fingerprint>, and pcmd sync uses pocketcmd://ssh-sync?host=<host>&port=<port>&token=<token>#<server_fingerprint>.
Configuration file
Section titled “Configuration file”~/.pocketcmd/config.json (mode 0600) is written when the first device pairs:
{ "config_version": 1, "service_mode": "background", "port": 8767, "host": "192.168.1.5", "advertise_ip": "myhome.example.net"}| Field | Description |
|---|---|
config_version |
Managed by the server. A server refuses a config.json written by a newer release. |
service_mode |
background (pcmd keeps a service installed and running) or foreground (pcmd runs the server in the foreground). |
port, host |
The port and bind address from the first run, reused by later commands unless you pass --port or --host. host is the interface picked on the first run, or 0.0.0.0 / :: when you chose every interface with --host. |
advertise_ip |
The address put into pairing, invite, sync and re-pair codes, set with --advertise-ip on the first run. Empty means the bind address. |