# 3pm CLI reference

Generated from `3pm --help` and every subcommand's `--help` (3pm 0.64.0).
Every command accepts `--json` (machine output on stdout, errors on stderr) unless its help says otherwise.
Targets (`<name|id|pattern|all|ecosystem file>`) are described in features.md → Target patterns.

## Index

- [`3pm start`](#3pm-start): Start an app (script, binary, "command line", or ecosystem file: .toml, .json, .yaml, .config.js; `-` = stdin)
- [`3pm bench`](#3pm-bench): Start a demo fleet (4 node, 2 python, 2 shell apps) for testing; rerun to restart it, --delete to remove it
- [`3pm validate`](#3pm-validate): Check ecosystem files (syntax, fields, profiles, dependencies) without starting anything; exit 1 on any error
- [`3pm apply`](#3pm-apply): Reconcile the daemon with ecosystem files: create missing apps, restart changed ones, leave unchanged ones running (idempotent)
- [`3pm scale`](#3pm-scale): Resize an app to N instances (never restarts running ones)
- [`3pm stop`](#3pm-stop): Stop one or more processes by name, id, pattern, 'all', or every app of an ecosystem file
- [`3pm restart`](#3pm-restart): Restart one or more processes by name, id, pattern, 'all', or every app of an ecosystem file
- [`3pm rename`](#3pm-rename): Rename an app in place, without restarting it
- [`3pm delete`](#3pm-delete): Stop and remove one or more processes by name, id, pattern, 'all', or every app of an ecosystem file
- [`3pm reset`](#3pm-reset): Zero the restart counters of one or more processes (name, id, pattern, 'all', or an ecosystem file)
- [`3pm sendSignal`](#3pm-sendsignal): Send a signal to a running process (name, id, pattern or 'all'), e.g. `3pm sendSignal SIGUSR2 api`
- [`3pm ls`](#3pm-ls): List managed processes
- [`3pm show`](#3pm-show): Show detailed information about a process
- [`3pm attach`](#3pm-attach): Attach to the terminal of an app started with --tty: its screen, your keys; Ctrl-Z detaches. Piped, stdin goes to the app and its output comes out; --peek prints the screen
- [`3pm cd`](#3pm-cd): Go to an app's working directory: a shell there on a terminal (exit to return), the path otherwise (`cd "$(3pm cd api)"`)
- [`3pm trigger`](#3pm-trigger): Run an action the app declared (`io.action`) on every instance of a name, or on one; without an action, list what each source declares
- [`3pm call`](#3pm-call): Call an action with bytes attached, on this host or on a node of the bucket (`--node`): one instance, one answer, `--file` in and `--out` out
- [`3pm profile`](#3pm-profile): Profile an app's CPU or heap (its SDK's `3pm:profile` action): the file is kept under ~/.3pm/actions/ and the hot frames are printed
- [`3pm internals`](#3pm-internals): Show 3pm daemon internal metrics (memory, load, fds, supervision, its own processes)
- [`3pm hostmetrics`](#3pm-hostmetrics): Show host-wide OS metrics (CPU incl. steal, PSI pressure, memory, cgroup limits, disks, RAID, network, TCP health, sensors, NTP offset)
- [`3pm logs`](#3pm-logs): Show process logs (last N lines, then stream)
- [`3pm events`](#3pm-events): Stream the daemon event bus (lifecycle, logs, rotations, shutdown); filtered server-side, one line per event
- [`3pm runtime`](#3pm-runtime): Container entrypoint (PID 1): run the daemon in the foreground, stream app output, exit when there is nothing left to supervise
- [`3pm flush`](#3pm-flush): Empty log files in place (all apps + the daemon's, or one target)
- [`3pm reloadLogs`](#3pm-reloadlogs): Reopen every log file by path (after an external logrotate/mv); SIGUSR2 to the daemon does the same
- [`3pm logrotate`](#3pm-logrotate): Install a logrotate(8) config for the 3pm logs (root; `--print` to just show it). Built-in rotation is usually enough — this is for hosts that centralize rotation in /etc/logrotate.d
- [`3pm rotate`](#3pm-rotate): Rotate log files now, regardless of size
- [`3pm save`](#3pm-save): Save the current process list for `resurrect`
- [`3pm resurrect`](#3pm-resurrect): Restore the last saved process list
- [`3pm kill`](#3pm-kill): Stop the daemon and all managed processes
- [`3pm update`](#3pm-update): Update the daemon to this binary without restarting the apps (in-place handoff; falls back to save → kill → respawn → resurrect)
- [`3pm startup`](#3pm-startup): Configure 3pm to start on boot (prints the command to run as root)
- [`3pm unstartup`](#3pm-unstartup): Remove the 3pm boot service (prints the command to run as root)
- [`3pm install`](#3pm-install): Install a module (a bundled name or a directory holding a module.toml) and start its apps; without argument, list modules
- [`3pm uninstall`](#3pm-uninstall): Uninstall a module: delete its apps and remove its files
- [`3pm link`](#3pm-link): Link this 3pm to a fleet backend: the line pasted from its bucket page; alone, show the link and exit 0 only if it is established; `link stop` / `link start` pause and resume it, `link delete` forgets it (`3pm link --help` lists the agent's flags)
  - [`3pm link stop`](#3pm-link-stop): Stop the agent app and keep the enrollment (`link start` resumes)
  - [`3pm link start`](#3pm-link-start): Start the agent app again on an existing enrollment (registers it again if it was deleted)
  - [`3pm link status`](#3pm-link-status): Same as `3pm agent status`
  - [`3pm link delete`](#3pm-link-delete): Disconnect and forget the enrollment (key, link.toml) — same as `3pm unlink`
  - [`3pm link authorize`](#3pm-link-authorize): Let the caller whose key this is open `3pm fleet ssh` sessions here (`3pm fleet key` prints it on the caller's machine). Local: the fleet cannot add a key
  - [`3pm link deauthorize`](#3pm-link-deauthorize): Stop admitting a caller's key, named by its fingerprint or its comment
- [`3pm unlink`](#3pm-unlink): Unlink from the fleet backend and remove the agent app
- [`3pm fleet`](#3pm-fleet): The nodes of this bucket, from this host's own link: `fleet nodes` (alias `fleet ls`), `fleet apps [node]`, `fleet inspect <node> [app]`, `fleet call …`, `fleet top`, `fleet ssh <node>`, `fleet cp`; alone, print this list
- [`3pm module`](#3pm-module): Modules: optional features bundled with 3pm (`3pm module list`); alone, lists them and prints this help
  - [`3pm module list`](#3pm-module-list): List installed and bundled modules
- [`3pm completions`](#3pm-completions): Generate shell completions
- [`3pm agent`](#3pm-agent): Fleet link: enroll this host and stream its processes to a 3pm backend
  - [`3pm agent link`](#3pm-agent-link): Enroll this host and start the agent app (the pasted line); alone, show the link and exit 0 only if it is established; `link stop` / `link start` / `link status` / `link delete` act on an existing link
    - [`3pm agent link stop`](#3pm-agent-link-stop): Stop the agent app and keep the enrollment (`link start` resumes)
    - [`3pm agent link start`](#3pm-agent-link-start): Start the agent app again on an existing enrollment (registers it again if it was deleted)
    - [`3pm agent link status`](#3pm-agent-link-status): Same as `3pm agent status`
    - [`3pm agent link delete`](#3pm-agent-link-delete): Disconnect and forget the enrollment (key, link.toml) — same as `3pm unlink`
    - [`3pm agent link authorize`](#3pm-agent-link-authorize): Let the caller whose key this is open `3pm fleet ssh` sessions here (`3pm fleet key` prints it on the caller's machine). Local: the fleet cannot add a key
    - [`3pm agent link deauthorize`](#3pm-agent-link-deauthorize): Stop admitting a caller's key, named by its fingerprint or its comment
  - [`3pm agent unlink`](#3pm-agent-unlink): Stop and delete the agent app, forget the enrollment
  - [`3pm agent status`](#3pm-agent-status): What this host is linked to, and how the agent app is doing
  - [`3pm agent fleet`](#3pm-agent-fleet): Read this node's own bucket with this node's own identity: `fleet nodes`, `fleet apps [node]`, `fleet inspect <node> [app]`, `fleet top`, `fleet call`, `fleet ssh <node>`, `fleet cp`
    - [`3pm agent fleet nodes`](#3pm-agent-fleet-nodes): Every node of the bucket: host, os, state, how long since it was seen, how many apps it runs, whose account runs it and where it is reached
    - [`3pm agent fleet apps`](#3pm-agent-fleet-apps): Every process of the bucket, or of one node (a `3pmn_…` id, a hostname, or an unambiguous start of one)
    - [`3pm agent fleet inspect`](#3pm-agent-fleet-inspect): One node in full — and one of its apps, when named
    - [`3pm agent fleet top`](#3pm-agent-fleet-top): The bucket live, in two panels, until `q`: nodes on top, processes below, redrawn once a second from the event stream
    - [`3pm agent fleet call`](#3pm-agent-fleet-call): Place a call on another node of the bucket, with this node's own identity, and wait for it: `--file` sends bytes, `--out` keeps what came back
    - [`3pm agent fleet ssh`](#3pm-agent-fleet-ssh): An interactive login shell on another node of the bucket, like ssh (`~.` after Enter to leave) — or one command, after `--`
    - [`3pm agent fleet attach`](#3pm-agent-fleet-attach): The terminal of a `tty = true` app on another node of the bucket, like `3pm attach` (`~.` after Enter to detach: the app runs on)
    - [`3pm agent fleet cp`](#3pm-agent-fleet-cp): Copy files to or from another node of the bucket, like scp: one side is `<node>:<path>`
    - [`3pm agent fleet known-nodes`](#3pm-agent-fleet-known-nodes): The shell keys of the nodes this host has met, pinned at the first session (`~/.3pm/known_nodes`, like ssh's `known_hosts`)
      - [`3pm agent fleet known-nodes ls`](#3pm-agent-fleet-known-nodes-ls): The pinned nodes: id, fingerprint, name, date
      - [`3pm agent fleet known-nodes rm`](#3pm-agent-fleet-known-nodes-rm): Forget a node's key (its `3pmn_…` id, or its name as pinned), like `ssh-keygen -R`: the next session asks again
    - [`3pm agent fleet key`](#3pm-agent-fleet-key): This host's caller key (`cli.key`, or THREEPM_CLI_KEY): its fingerprint and the line a node needs to admit it; `key new`, `key push`, `key revoke`
      - [`3pm agent fleet key new`](#3pm-agent-fleet-key-new): A new caller key in a file of its own, offline (a rescue key: keep it off this host; use it with THREEPM_CLI_KEY=<file>)
      - [`3pm agent fleet key push`](#3pm-agent-fleet-key-push): Admit a key on nodes, through a `fleet ssh` session on each (this host's key must already be admitted there)
      - [`3pm agent fleet key revoke`](#3pm-agent-fleet-key-revoke): Stop admitting a key on nodes, the same way
- [`3pm serve`](#3pm-serve): Static HTTP file server (pm2 serve): --spa, --ftp, basic auth

## 3pm

```text
3pm — a fast, robust process manager

Usage: 3pm [OPTIONS] <COMMAND>

Commands:
  start        Start an app (script, binary, "command line", or ecosystem file: .toml, .json, .yaml, .config.js; `-` = stdin)
  bench        Start a demo fleet (4 node, 2 python, 2 shell apps) for testing; rerun to restart it, --delete to remove it
  validate     Check ecosystem files (syntax, fields, profiles, dependencies) without starting anything; exit 1 on any error
  apply        Reconcile the daemon with ecosystem files: create missing apps, restart changed ones, leave unchanged ones running (idempotent)
  scale        Resize an app to N instances (never restarts running ones)
  stop         Stop one or more processes by name, id, pattern, 'all', or every app of an ecosystem file
  restart      Restart one or more processes by name, id, pattern, 'all', or every app of an ecosystem file
  rename       Rename an app in place, without restarting it
  delete       Stop and remove one or more processes by name, id, pattern, 'all', or every app of an ecosystem file
  reset        Zero the restart counters of one or more processes (name, id, pattern, 'all', or an ecosystem file)
  sendSignal   Send a signal to a running process (name, id, pattern or 'all'), e.g. `3pm sendSignal SIGUSR2 api`
  ls           List managed processes
  show         Show detailed information about a process
  attach       Attach to the terminal of an app started with --tty: its screen, your keys; Ctrl-Z detaches. Piped, stdin goes to the app and its output comes out; --peek prints the screen
  cd           Go to an app's working directory: a shell there on a terminal (exit to return), the path otherwise (`cd "$(3pm cd api)"`)
  trigger      Run an action the app declared (`io.action`) on every instance of a name, or on one; without an action, list what each source declares
  call         Call an action with bytes attached, on this host or on a node of the bucket (`--node`): one instance, one answer, `--file` in and `--out` out
  profile      Profile an app's CPU or heap (its SDK's `3pm:profile` action): the file is kept under ~/.3pm/actions/ and the hot frames are printed
  internals    Show 3pm daemon internal metrics (memory, load, fds, supervision, its own processes)
  hostmetrics  Show host-wide OS metrics (CPU incl. steal, PSI pressure, memory, cgroup limits, disks, RAID, network, TCP health, sensors, NTP offset)
  logs         Show process logs (last N lines, then stream)
  events       Stream the daemon event bus (lifecycle, logs, rotations, shutdown); filtered server-side, one line per event
  runtime      Container entrypoint (PID 1): run the daemon in the foreground, stream app output, exit when there is nothing left to supervise
  flush        Empty log files in place (all apps + the daemon's, or one target)
  reloadLogs   Reopen every log file by path (after an external logrotate/mv); SIGUSR2 to the daemon does the same
  logrotate    Install a logrotate(8) config for the 3pm logs (root; `--print` to just show it). Built-in rotation is usually enough — this is for hosts that centralize rotation in /etc/logrotate.d
  rotate       Rotate log files now, regardless of size
  save         Save the current process list for `resurrect`
  resurrect    Restore the last saved process list
  kill         Stop the daemon and all managed processes
  update       Update the daemon to this binary without restarting the apps (in-place handoff; falls back to save → kill → respawn → resurrect)
  startup      Configure 3pm to start on boot (prints the command to run as root)
  unstartup    Remove the 3pm boot service (prints the command to run as root)
  install      Install a module (a bundled name or a directory holding a module.toml) and start its apps; without argument, list modules
  uninstall    Uninstall a module: delete its apps and remove its files
  link         Link this 3pm to a fleet backend: the line pasted from its bucket page; alone, show the link and exit 0 only if it is established; `link stop` / `link start` pause and resume it, `link delete` forgets it (`3pm link --help` lists the agent's flags)
  unlink       Unlink from the fleet backend and remove the agent app
  fleet        The nodes of this bucket, from this host's own link: `fleet nodes` (alias `fleet ls`), `fleet apps [node]`, `fleet inspect <node> [app]`, `fleet call …`, `fleet top`, `fleet ssh <node>`, `fleet cp`; alone, print this list
  module       Modules: optional features bundled with 3pm (`3pm module list`); alone, lists them and prints this help
  completions  Generate shell completions
  help         Print this message or the help of the given subcommand(s)

Options:
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm start

```text
Start an app (script, binary, "command line", or ecosystem file: .toml, .json, .yaml, .config.js; `-` = stdin)

Usage: 3pm start [OPTIONS] [TARGET] [-- <ARGS>...]

Arguments:
  [TARGET]   Script/binary to run, a quoted command line ("npm run dev", run by bash -c), an ecosystem file (.toml, .json, .yaml, .config.js), or `-` for a config on stdin. Default: the ecosystem file of the current directory
  [ARGS]...  Arguments passed to the app (everything after --)

Options:
      --attach                          After starting, stream the logs of the started apps (Ctrl-C to leave)
      --json                            Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
      --only <APPS>                     With an ecosystem file: start only these apps (comma-separated)
      --name <NAME>                     Process name (defaults to the script's file stem)
      --interpreter <INTERPRETER>       Interpreter to run the script with (default: inferred from the extension); `none` runs the script itself
      --interpreter-args <ARGS>         The interpreter's own flags, before the script (pm2's --node-args), split on blanks: --interpreter-args "--inspect --max-old-space-size=64"; "" clears them [alias: --node-args]
      --cwd <CWD>                       Working directory (default: current directory)
      --env <KEY=VALUE|PROFILE>         Environment variables, KEY=VALUE (repeatable). With an ecosystem file: the profile to apply (`--env production` selects [apps.profiles.production] and [apps.env_production])
      --namespace <NAMESPACE>           Namespace grouping label
      --user <NAME>                     Unix account to run the app as (root daemon, user declared in `[users] allow`); "" runs it as the daemon again
      --persist <BOOL>                  Keep the app in dump.json (`3pm save`, `3pm update`); false makes it ephemeral: it survives restarts, never a reboot [possible values: true, false]
  -i, --instances <N>                   Number of instances to run (cluster mode); 0 = one per CPU
      --depends-on <APP>                App that must be online before this one starts (repeatable)
      --start-delay <MS>                Wait this long before the first spawn (ms); crash restarts use --restart-delay / --exp-backoff-restart-delay instead
      --restart-delay <MS>              Fixed pause before every crash restart (ms; default 0: restart at once, like pm2)
      --exp-backoff-restart-delay <MS>  Exponential backoff between crash restarts, starting at this delay (ms) and growing ×1.5 up to 15s; off by default (pm2)
      --stop-exit-codes <CODES>         Exit codes that stop the app instead of restarting it (e.g. 0,3)
      --cron-restart <CRON>             Restart on a cron schedule, e.g. "0 2 * * *" (5 fields, or 6 with seconds first)
      --no-autorestart                  Do not restart the app on exit (same as --restart-policy never)
      --restart-policy <POLICY>         When an unrequested exit respawns the app: always (default), on-failure (non-zero exit or signal only), never [possible values: always, on-failure, never]
      --wait-ready                      Hold `launching` until the app signals ready on the control socket
      --shutdown-with-message           Graceful stop via a control-socket message instead of a signal
      --max-memory-restart <SIZE>       Restart when memory exceeds this amount (e.g. 200M, 1G)
      --max-cpu-percent <PERCENT>       Restart when CPU usage exceeds this percent of one core (may be above 100 on multi-core hosts)
      --cgroup-enforce                  Linux only: confine the app in a cgroup v2 whose cpu.max / memory.max are hard limits derived from --max-cpu-percent / --max-memory-restart
      --deny-gpu                        Hide GPUs from the app (CUDA / ROCm visibility env vars)
      --ulimit-rtprio <PRIO>            Linux only: the app's RLIMIT_RTPRIO (0-99), so its threads may run SCHED_FIFO / SCHED_RR up to that priority
      --ulimit-memlock <SIZE>           The app's RLIMIT_MEMLOCK: how much it may lock in RAM (e.g. 64M, 4G, unlimited)
      --ulimit-nice <N>                 Linux only: the app's RLIMIT_NICE (0-40), so its threads may lower their nice down to 20 minus this (28 for -8)
      --ulimit-rttime <US>              Linux only: the app's RLIMIT_RTTIME, in microseconds: the CPU a real-time thread may take without blocking before the kernel kills it
      --nice <N>                        The app's nice value, -20 (most CPU) to 19
      --ioprio <CLASS>                  Linux only: the app's I/O class: idle, best-effort[:0-7] or realtime[:0-7]
      --oom-score-adj <N>               Linux only: the app's oom_score_adj, -1000 (never killed when memory runs out) to 1000 (killed first)
      --cpu-affinity <CPUS>             Linux only: the CPUs the app may run on (e.g. 0-3,6)
      --kill-signal <KILL_SIGNAL>       Signal sent on stop before SIGKILL escalation (default: SIGINT)
      --watch[=<PATH>]                  Restart on file changes: `--watch` for the working directory, or `--watch=PATH` (repeatable) for specific dirs/files
      --ignore-watch <GLOB>             Glob to exclude from watching, added to the built-in ones (dotfiles, node_modules); repeatable
      --watch-delay <MS>                Debounce before a watch-triggered restart, in milliseconds
  -o, --output <PATH>                   stdout log file (default: ~/.3pm/logs/<name>-out.log); NULL to disable the file. May contain {name}, {instance}, {namespace}
  -e, --error <PATH>                    stderr log file (default: ~/.3pm/logs/<name>-error.log); NULL to disable the file
  -l, --log <PATH>                      One file for both streams (implies --merge-logs)
      --merge-logs                      Write stdout and stderr to a single file
      --log-date-format <FMT>           Prefix each persisted log line with a strftime timestamp
      --time                            Shorthand for --log-date-format '%Y-%m-%dT%H:%M:%S' (pm2 --time)
      --disable-logs                    No log files, no log stream, no crash tail
      --tty                             Run on a terminal (pty) instead of pipes, for interactive programs; one output stream, logged without escape sequences
  -h, --help                            Print help
  -V, --version                         Print version
```

## 3pm bench

```text
Start a demo fleet (4 node, 2 python, 2 shell apps) for testing; rerun to restart it, --delete to remove it

Usage: 3pm bench [OPTIONS]

Options:
      --dir <DIR>  Where the demo scripts are written (default: <home>/bench)
      --json       Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
      --delete     Delete the bench apps instead of starting them
  -h, --help       Print help
  -V, --version    Print version
```

## 3pm validate

```text
Check ecosystem files (syntax, fields, profiles, dependencies) without starting anything; exit 1 on any error

Usage: 3pm validate [OPTIONS] [FILES]...

Arguments:
  [FILES]...  Ecosystem file(s) (.toml, .json, .yaml, .config.js), `-` for stdin. Default: the ecosystem file of the current directory

Options:
      --env <PROFILE>  Profile to resolve (as `start --env <PROFILE>` would)
      --json           Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
      --only <APPS>    Only these apps (comma-separated)
      --check-managed  Ask the daemon whether external `depends_on` names are managed
  -h, --help           Print help
  -V, --version        Print version
```

## 3pm apply

```text
Reconcile the daemon with ecosystem files: create missing apps, restart changed ones, leave unchanged ones running (idempotent)

Usage: 3pm apply [OPTIONS] [FILES]...

Arguments:
  [FILES]...  Ecosystem file(s) (.toml, .json, .yaml, .config.js). Default: the ecosystem file of the current directory

Options:
      --env <PROFILE>  Profile to apply (as `start --env <PROFILE>` would)
      --json           Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
      --only <APPS>    Only these apps (comma-separated)
      --prune          Delete managed apps that came from these files but are gone from them
      --prune-all      Delete every managed app absent from these files (CLI-started too)
      --dry-run        Print the plan and exit without changing anything
  -h, --help           Print help
  -V, --version        Print version
```

## 3pm scale

```text
Resize an app to N instances (never restarts running ones)

Usage: 3pm scale [OPTIONS] <NAME> <INSTANCES>

Arguments:
  <NAME>       App name (all its instances share it)
  <INSTANCES>  Desired instance count (1 or more)

Options:
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm stop

```text
Stop one or more processes by name, id, pattern, 'all', or every app of an ecosystem file

Usage: 3pm stop [OPTIONS] <TARGET>...

Arguments:
  <TARGET>...  Processes to act on: names, ids, 'victoria*' patterns, 'all', or ecosystem files

Options:
      --attach   After stopping, stream the logs of the affected apps (Ctrl-C to leave)
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm restart

```text
Restart one or more processes by name, id, pattern, 'all', or every app of an ecosystem file

Every `start` flag is accepted and reconfigures the apps on the way (pm2's attribute update): what you type is folded into each app's configuration, which then restarts on it — `3pm restart api --name web -i 4` renames the app in place, keeping its ids and counters, and runs it with four instances. `--watch` alone still toggles watch mode; `--watch=PATH` sets the watched paths.

Usage: 3pm restart [OPTIONS] <TARGET>...

Arguments:
  <TARGET>...
          Processes to act on: names, ids, 'victoria*' patterns, 'all', or ecosystem files

Options:
      --attach
          After restarting, stream the logs of the affected apps (Ctrl-C to leave)

      --json
          Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)

      --update-env
          Re-read the app's ecosystem file (same profile) and restart with the fresh spec
          
          The new values come from the file, so no configuration flag may be typed alongside it.

      --name <NAME>
          Process name (defaults to the script's file stem)

      --interpreter <INTERPRETER>
          Interpreter to run the script with (default: inferred from the extension); `none` runs the script itself

      --interpreter-args <ARGS>
          The interpreter's own flags, before the script (pm2's --node-args), split on blanks: --interpreter-args "--inspect --max-old-space-size=64"; "" clears them
          
          [alias: --node-args]

      --cwd <CWD>
          Working directory (default: current directory)

      --env <KEY=VALUE|PROFILE>
          Environment variables, KEY=VALUE (repeatable). With an ecosystem file: the profile to apply (`--env production` selects [apps.profiles.production] and [apps.env_production])

      --namespace <NAMESPACE>
          Namespace grouping label

      --user <NAME>
          Unix account to run the app as (root daemon, user declared in `[users] allow`); "" runs it as the daemon again

      --persist <BOOL>
          Keep the app in dump.json (`3pm save`, `3pm update`); false makes it ephemeral: it survives restarts, never a reboot
          
          [possible values: true, false]

  -i, --instances <N>
          Number of instances to run (cluster mode); 0 = one per CPU

      --depends-on <APP>
          App that must be online before this one starts (repeatable)

      --start-delay <MS>
          Wait this long before the first spawn (ms); crash restarts use --restart-delay / --exp-backoff-restart-delay instead

      --restart-delay <MS>
          Fixed pause before every crash restart (ms; default 0: restart at once, like pm2)

      --exp-backoff-restart-delay <MS>
          Exponential backoff between crash restarts, starting at this delay (ms) and growing ×1.5 up to 15s; off by default (pm2)

      --stop-exit-codes <CODES>
          Exit codes that stop the app instead of restarting it (e.g. 0,3)

      --cron-restart <CRON>
          Restart on a cron schedule, e.g. "0 2 * * *" (5 fields, or 6 with seconds first)

      --no-autorestart
          Do not restart the app on exit (same as --restart-policy never)

      --restart-policy <POLICY>
          When an unrequested exit respawns the app: always (default), on-failure (non-zero exit or signal only), never
          
          [possible values: always, on-failure, never]

      --wait-ready
          Hold `launching` until the app signals ready on the control socket

      --shutdown-with-message
          Graceful stop via a control-socket message instead of a signal

      --max-memory-restart <SIZE>
          Restart when memory exceeds this amount (e.g. 200M, 1G)

      --max-cpu-percent <PERCENT>
          Restart when CPU usage exceeds this percent of one core (may be above 100 on multi-core hosts)

      --cgroup-enforce
          Linux only: confine the app in a cgroup v2 whose cpu.max / memory.max are hard limits derived from --max-cpu-percent / --max-memory-restart

      --deny-gpu
          Hide GPUs from the app (CUDA / ROCm visibility env vars)

      --ulimit-rtprio <PRIO>
          Linux only: the app's RLIMIT_RTPRIO (0-99), so its threads may run SCHED_FIFO / SCHED_RR up to that priority

      --ulimit-memlock <SIZE>
          The app's RLIMIT_MEMLOCK: how much it may lock in RAM (e.g. 64M, 4G, unlimited)

      --ulimit-nice <N>
          Linux only: the app's RLIMIT_NICE (0-40), so its threads may lower their nice down to 20 minus this (28 for -8)

      --ulimit-rttime <US>
          Linux only: the app's RLIMIT_RTTIME, in microseconds: the CPU a real-time thread may take without blocking before the kernel kills it

      --nice <N>
          The app's nice value, -20 (most CPU) to 19

      --ioprio <CLASS>
          Linux only: the app's I/O class: idle, best-effort[:0-7] or realtime[:0-7]

      --oom-score-adj <N>
          Linux only: the app's oom_score_adj, -1000 (never killed when memory runs out) to 1000 (killed first)

      --cpu-affinity <CPUS>
          Linux only: the CPUs the app may run on (e.g. 0-3,6)

      --kill-signal <KILL_SIGNAL>
          Signal sent on stop before SIGKILL escalation (default: SIGINT)

      --watch[=<PATH>]
          Restart on file changes: `--watch` for the working directory, or `--watch=PATH` (repeatable) for specific dirs/files

      --ignore-watch <GLOB>
          Glob to exclude from watching, added to the built-in ones (dotfiles, node_modules); repeatable

      --watch-delay <MS>
          Debounce before a watch-triggered restart, in milliseconds

  -o, --output <PATH>
          stdout log file (default: ~/.3pm/logs/<name>-out.log); NULL to disable the file. May contain {name}, {instance}, {namespace}

  -e, --error <PATH>
          stderr log file (default: ~/.3pm/logs/<name>-error.log); NULL to disable the file

  -l, --log <PATH>
          One file for both streams (implies --merge-logs)

      --merge-logs
          Write stdout and stderr to a single file

      --log-date-format <FMT>
          Prefix each persisted log line with a strftime timestamp

      --time
          Shorthand for --log-date-format '%Y-%m-%dT%H:%M:%S' (pm2 --time)

      --disable-logs
          No log files, no log stream, no crash tail

      --tty
          Run on a terminal (pty) instead of pipes, for interactive programs; one output stream, logged without escape sequences

  -h, --help
          Print help (see a summary with '-h')

  -V, --version
          Print version
```

## 3pm rename

```text
Rename an app in place, without restarting it

Same ids, pids and restart counters; the pid files and the log files named after the app (live and rotated) move to the new name, and the app writes on into them. The running process keeps the `name` variable it was started with until its next restart. `3pm restart <app> --name <new>` renames and restarts.

Usage: 3pm rename [OPTIONS] <APP> <NEW_NAME>

Arguments:
  <APP>
          The app to rename: its name, or the id of one of its instances

  <NEW_NAME>
          Its new name

Options:
      --json
          Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)

  -h, --help
          Print help (see a summary with '-h')

  -V, --version
          Print version
```

## 3pm delete

```text
Stop and remove one or more processes by name, id, pattern, 'all', or every app of an ecosystem file

Usage: 3pm delete [OPTIONS] <TARGET>...

Arguments:
  <TARGET>...  Processes to act on: names, ids, 'victoria*' patterns, 'all', or ecosystem files

Options:
      --flush    Also delete their log files (live and rotated)
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm reset

```text
Zero the restart counters of one or more processes (name, id, pattern, 'all', or an ecosystem file)

Usage: 3pm reset [OPTIONS] <TARGET>...

Arguments:
  <TARGET>...  Processes to act on: names, ids, 'victoria*' patterns, 'all', or ecosystem files

Options:
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm sendSignal

```text
Send a signal to a running process (name, id, pattern or 'all'), e.g. `3pm sendSignal SIGUSR2 api`

Usage: 3pm sendSignal [OPTIONS] <SIGNAL> <TARGET>

Arguments:
  <SIGNAL>  POSIX signal name (SIGUSR1, SIGHUP, …)
  <TARGET>  

Options:
      --group    Signal the whole process group instead of the process alone
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm ls

```text
List managed processes

Usage: 3pm ls [OPTIONS]

Options:
      --json           Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
      --watch          Redraw the table in place every --interval ms until Ctrl-C (pm2's `ls --watch`); with --json, one document per tick
      --interval <MS>  Refresh period of --watch in milliseconds (default 300, or 1000 with --tree: the daemon samples trees once a second; min 100)
  -t, --tree           Draw each app's process tree under its row: chains of single children folded (`npm › node`), twins grouped (`worker ×4`), `⚠` on a process that left the group (`3pm stop` will not reach it); with --json, a `tree` object per app
      --all            With --tree: one row per process, nothing folded or grouped
  -h, --help           Print help
  -V, --version        Print version
```

## 3pm show

```text
Show detailed information about a process

Usage: 3pm show [OPTIONS] <TARGET>

Arguments:
  <TARGET>  

Options:
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm attach

```text
Attach to the terminal of an app started with --tty: its screen, your keys; Ctrl-Z detaches. Piped, stdin goes to the app and its output comes out; --peek prints the screen

Usage: 3pm attach [OPTIONS] <TARGET>

Arguments:
  <TARGET>  App name, or id

Options:
      --instance <N>  Which instance of the app (default 0)
      --json          Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
      --read-only     Watch only: neither your keys nor your terminal's size reach the app
      --raw           The app's bytes as it writes them instead of the screen the daemon renders: everything the app draws, at your terminal's risk
      --peek          Print the screen as text and exit (--json: a JSON document)
      --paste         Send stdin as one bracketed paste when the app turned pasting on (a prompt then takes several lines as one message)
  -h, --help          Print help
  -V, --version       Print version
```

## 3pm cd

```text
Go to an app's working directory: a shell there on a terminal (exit to return), the path otherwise (`cd "$(3pm cd api)"`)

Usage: 3pm cd [OPTIONS] <TARGET>

Arguments:
  <TARGET>  Process name or id

Options:
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm trigger

```text
Run an action the app declared (`io.action`) on every instance of a name, or on one; without an action, list what each source declares

Usage: 3pm trigger [OPTIONS] <TARGET> [ACTION] [PARAMS]

Arguments:
  <TARGET>  Process name or id (`all` is refused)
  [ACTION]  Action name, as declared; omitted: list the actions of each source
  [PARAMS]  Parameters, as JSON (default null)

Options:
      --instance <N>        One instance of the app (default: every instance that declares the action, at most 16)
      --json                Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
      --src <SOURCE>        The source (process of the app) to call, when several declare it; without an action, list that source's actions only
      --timeout <DURATION>  How long to wait (`30s`, `2m`, `1500ms`); the declaration's own timeout bounds it (default 30s, ceiling 10m)
      --file <PATH>         A file to send with the call: the action must declare an `input` that accepts its size and extension. Nothing is copied — the handler is given this very path — and the call then reaches one instance (`--instance`, 0 by default) instead of every one
      --out <DIR>           Directory to put the file the action produced into (it stays under the home's `actions/` too, until retention sweeps it)
  -h, --help                Print help
  -V, --version             Print version
```

## 3pm call

```text
Call an action with bytes attached, on this host or on a node of the bucket (`--node`): one instance, one answer, `--file` in and `--out` out

Usage: 3pm call [OPTIONS] <APP> <ACTION> [PARAMS]

Arguments:
  <APP>     App name (never `all`: a call names one app)
  <ACTION>  Action name, as declared
  [PARAMS]  Parameters, as JSON (default null)

Options:
      --json                Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
      --node <NODE>         A node of the bucket (`3pmn_…` id, or a hostname unique in it); omitted, the call runs on this host. A call that names a node leaves through the fleet agent and carries no file (`3pm fleet call` does that)
      --instance <N>        One instance of the app (default 0)
      --src <SOURCE>        The source (process of the app) to call, when several declare it
      --timeout <DURATION>  How long to wait (`30s`, `2m`, `1500ms`); the declaration's own timeout bounds it (default 30s, ceiling 10m)
      --file <PATH>         A file to send with the call (an absolute or relative path of this host; the action must declare an `input`)
      --out <DIR>           Directory to put the file the action produced into
  -h, --help                Print help
  -V, --version             Print version
```

## 3pm profile

```text
Profile an app's CPU or heap (its SDK's `3pm:profile` action): the file is kept under ~/.3pm/actions/ and the hot frames are printed

Usage: 3pm profile [OPTIONS] <TARGET>

Arguments:
  <TARGET>  Process name or id

Options:
      --instance <N>         One instance of the app (default: every instance)
      --json                 Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
      --src <SOURCE>         The source (process of the app) to profile; required when several declare `3pm:profile` (an Electron app: the main and each renderer)
      --heap                 A heap sampling profile instead of a CPU one
      --duration <DURATION>  How long to sample (`10s`, `1500ms`; 1s to 60s) [default: 10s]
      --sampling <N>         Sampling interval: microseconds for CPU (default 1000), bytes for heap (default 32768)
      --top <N>              Hot frames to print [default: 15]
  -h, --help                 Print help
  -V, --version              Print version
```

## 3pm internals

```text
Show 3pm daemon internal metrics (memory, load, fds, supervision, its own processes)

Usage: 3pm internals [OPTIONS]

Options:
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm hostmetrics

```text
Show host-wide OS metrics (CPU incl. steal, PSI pressure, memory, cgroup limits, disks, RAID, network, TCP health, sensors, NTP offset)

Usage: 3pm hostmetrics [OPTIONS]

Options:
      --extended  Extended Monitoring instead of the overview: every kernel counter family (`[hostmetrics] extended`), one line per field, then the sources that could not be read and why
      --json      Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help      Print help
  -V, --version   Print version
```

## 3pm logs

```text
Show process logs (last N lines, then stream)

Usage: 3pm logs [OPTIONS] [TARGET]...

Arguments:
  [TARGET]...  Process names, ids or 'victoria*' patterns (default: all)

Options:
      --json               Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
      --lines <LINES>      Number of history lines to print per log file [default: 30]
      --err                Only error output
      --out                Only standard output
      --raw                No prefixes or colors
      --nostream           Print the tail and exit without streaming (`--no-stream` too)
      --timestamp[=<FMT>]  Prefix streamed lines with their arrival time (strftime; default "%Y-%m-%d %H:%M:%S"). History lines have no time and get padding
      --format             logfmt output: ts= app= id= instance= type= message=
      --highlight <TEXT>   Highlight this text in streamed and history lines
      --rotated            Continue the history into rotated generations (.1, .2, .gz) when the live file has fewer than --lines
      --size               Print log file sizes and rotated generations, then exit
  -h, --help               Print help
  -V, --version            Print version
```

## 3pm events

```text
Stream the daemon event bus (lifecycle, logs, rotations, shutdown); filtered server-side, one line per event

Usage: 3pm events [OPTIONS]

Options:
  -f, --filter <GLOB>    Event glob to subscribe to (repeatable): `process`, `process:exit`, `log`, `log:err`, `log*`, `log_rotated`, `app`, `lagged`, `daemon_shutdown` (`log_out`/`log_err`/`log_*` still accepted for one release). Default: all
      --json             Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -p, --process <NAME>   Only events about this app, exact or a 'web-*' pattern (daemon-level events still pass)
      --raw              No time column, prefixes or colors: `<event> <detail>` only
      --timestamp <FMT>  Time column format (strftime; default "%H:%M:%S"). Log events use their daemon receive time, other events the arrival time [default: %H:%M:%S]
  -h, --help             Print help
  -V, --version          Print version
```

## 3pm runtime

```text
Container entrypoint (PID 1): run the daemon in the foreground, stream app output, exit when there is nothing left to supervise

Usage: 3pm runtime [OPTIONS] [TARGET] [-- <ARGS>...]

Arguments:
  [TARGET]   Script/binary to run, a quoted command line ("npm run dev", run by bash -c), an ecosystem file (.toml, .json, .yaml, .config.js), or `-` for a config on stdin. Default: the ecosystem file of the current directory
  [ARGS]...  Arguments passed to the app (everything after --)

Options:
      --attach                          After starting, stream the logs of the started apps (Ctrl-C to leave)
      --json                            Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
      --only <APPS>                     With an ecosystem file: start only these apps (comma-separated)
      --name <NAME>                     Process name (defaults to the script's file stem)
      --interpreter <INTERPRETER>       Interpreter to run the script with (default: inferred from the extension); `none` runs the script itself
      --interpreter-args <ARGS>         The interpreter's own flags, before the script (pm2's --node-args), split on blanks: --interpreter-args "--inspect --max-old-space-size=64"; "" clears them [alias: --node-args]
      --cwd <CWD>                       Working directory (default: current directory)
      --env <KEY=VALUE|PROFILE>         Environment variables, KEY=VALUE (repeatable). With an ecosystem file: the profile to apply (`--env production` selects [apps.profiles.production] and [apps.env_production])
      --namespace <NAMESPACE>           Namespace grouping label
      --user <NAME>                     Unix account to run the app as (root daemon, user declared in `[users] allow`); "" runs it as the daemon again
      --persist <BOOL>                  Keep the app in dump.json (`3pm save`, `3pm update`); false makes it ephemeral: it survives restarts, never a reboot [possible values: true, false]
  -i, --instances <N>                   Number of instances to run (cluster mode); 0 = one per CPU
      --depends-on <APP>                App that must be online before this one starts (repeatable)
      --start-delay <MS>                Wait this long before the first spawn (ms); crash restarts use --restart-delay / --exp-backoff-restart-delay instead
      --restart-delay <MS>              Fixed pause before every crash restart (ms; default 0: restart at once, like pm2)
      --exp-backoff-restart-delay <MS>  Exponential backoff between crash restarts, starting at this delay (ms) and growing ×1.5 up to 15s; off by default (pm2)
      --stop-exit-codes <CODES>         Exit codes that stop the app instead of restarting it (e.g. 0,3)
      --cron-restart <CRON>             Restart on a cron schedule, e.g. "0 2 * * *" (5 fields, or 6 with seconds first)
      --no-autorestart                  Do not restart the app on exit (same as --restart-policy never)
      --restart-policy <POLICY>         When an unrequested exit respawns the app: always (default), on-failure (non-zero exit or signal only), never [possible values: always, on-failure, never]
      --wait-ready                      Hold `launching` until the app signals ready on the control socket
      --shutdown-with-message           Graceful stop via a control-socket message instead of a signal
      --max-memory-restart <SIZE>       Restart when memory exceeds this amount (e.g. 200M, 1G)
      --max-cpu-percent <PERCENT>       Restart when CPU usage exceeds this percent of one core (may be above 100 on multi-core hosts)
      --cgroup-enforce                  Linux only: confine the app in a cgroup v2 whose cpu.max / memory.max are hard limits derived from --max-cpu-percent / --max-memory-restart
      --deny-gpu                        Hide GPUs from the app (CUDA / ROCm visibility env vars)
      --ulimit-rtprio <PRIO>            Linux only: the app's RLIMIT_RTPRIO (0-99), so its threads may run SCHED_FIFO / SCHED_RR up to that priority
      --ulimit-memlock <SIZE>           The app's RLIMIT_MEMLOCK: how much it may lock in RAM (e.g. 64M, 4G, unlimited)
      --ulimit-nice <N>                 Linux only: the app's RLIMIT_NICE (0-40), so its threads may lower their nice down to 20 minus this (28 for -8)
      --ulimit-rttime <US>              Linux only: the app's RLIMIT_RTTIME, in microseconds: the CPU a real-time thread may take without blocking before the kernel kills it
      --nice <N>                        The app's nice value, -20 (most CPU) to 19
      --ioprio <CLASS>                  Linux only: the app's I/O class: idle, best-effort[:0-7] or realtime[:0-7]
      --oom-score-adj <N>               Linux only: the app's oom_score_adj, -1000 (never killed when memory runs out) to 1000 (killed first)
      --cpu-affinity <CPUS>             Linux only: the CPUs the app may run on (e.g. 0-3,6)
      --kill-signal <KILL_SIGNAL>       Signal sent on stop before SIGKILL escalation (default: SIGINT)
      --watch[=<PATH>]                  Restart on file changes: `--watch` for the working directory, or `--watch=PATH` (repeatable) for specific dirs/files
      --ignore-watch <GLOB>             Glob to exclude from watching, added to the built-in ones (dotfiles, node_modules); repeatable
      --watch-delay <MS>                Debounce before a watch-triggered restart, in milliseconds
  -o, --output <PATH>                   stdout log file (default: ~/.3pm/logs/<name>-out.log); NULL to disable the file. May contain {name}, {instance}, {namespace}
  -e, --error <PATH>                    stderr log file (default: ~/.3pm/logs/<name>-error.log); NULL to disable the file
  -l, --log <PATH>                      One file for both streams (implies --merge-logs)
      --merge-logs                      Write stdout and stderr to a single file
      --log-date-format <FMT>           Prefix each persisted log line with a strftime timestamp
      --time                            Shorthand for --log-date-format '%Y-%m-%dT%H:%M:%S' (pm2 --time)
      --disable-logs                    No log files, no log stream, no crash tail
      --tty                             Run on a terminal (pty) instead of pipes, for interactive programs; one output stream, logged without escape sequences
      --no-auto-exit                    Keep running after every app is stopped or errored (default: exit 2)
      --delay <SECS>                    Wait this many seconds before starting the apps [default: 0]
      --log-files                       Also write per-app log files (default: output goes to the container's stdout/stderr only, unless -o/-e/-l are given)
      --raw                             Bare lines, no `id|name |` prefix (pm2-runtime's default)
      --format                          logfmt output: ts= app= id= instance= type= message=
      --timestamp[=<FMT>]               Prefix streamed lines with their arrival time (strftime; default "%Y-%m-%d %H:%M:%S")
  -h, --help                            Print help
  -V, --version                         Print version
```

## 3pm flush

```text
Empty log files in place (all apps + the daemon's, or one target)

Usage: 3pm flush [OPTIONS] [TARGET]

Arguments:
  [TARGET]  Process name, id, namespace or pattern (default: everything)

Options:
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm reloadLogs

```text
Reopen every log file by path (after an external logrotate/mv); SIGUSR2 to the daemon does the same

Usage: 3pm reloadLogs [OPTIONS]

Options:
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm logrotate

```text
Install a logrotate(8) config for the 3pm logs (root; `--print` to just show it). Built-in rotation is usually enough — this is for hosts that centralize rotation in /etc/logrotate.d

Usage: 3pm logrotate [OPTIONS]

Options:
      --json         Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
      --print        Print the logrotate(8) config instead of installing it
      --user <USER>  User owning the 3pm home (default: $SUDO_USER, then the caller)
  -h, --help         Print help
  -V, --version      Print version
```

## 3pm rotate

```text
Rotate log files now, regardless of size

Usage: 3pm rotate [OPTIONS] [TARGET]

Arguments:
  [TARGET]  Process name, id, namespace or pattern (default: all)

Options:
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm save

```text
Save the current process list for `resurrect`

Usage: 3pm save [OPTIONS]

Options:
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm resurrect

```text
Restore the last saved process list

Usage: 3pm resurrect [OPTIONS]

Options:
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm kill

```text
Stop the daemon and all managed processes

Usage: 3pm kill [OPTIONS]

Options:
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm update

```text
Update the daemon to this binary without restarting the apps (in-place handoff; falls back to save → kill → respawn → resurrect)

The apps keep running; 3pm's own processes are restarted every time: the host agent, and the module apps (the fleet agent, `3pm serve` sites) unless `--skip-modules`.

Usage: 3pm update [OPTIONS]

Options:
      --cold
          Cold restart (save → kill → respawn → resurrect): every app restarts

      --json
          Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)

      --no-fallback
          When the in-place handoff is refused, fail (exit 3) instead of falling back to a cold restart; the apps are left untouched

      --skip-modules
          Leave the modules alone: no reinstall of the installed copies whose bundled version changed, no restart of module apps (the fleet link stays up, `3pm serve` sites keep serving)

  -h, --help
          Print help (see a summary with '-h')

  -V, --version
          Print version
```

## 3pm startup

```text
Configure 3pm to start on boot (prints the command to run as root)

Usage: 3pm startup [OPTIONS] [PLATFORM]

Arguments:
  [PLATFORM]  Init system (launchd, systemd, openrc, rcd); auto-detected if omitted

Options:
      --json                         Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
      --level <LEVEL>                Service level: `system` (root installs, daemon runs as --user) or `user` (installed in your session without root: a LaunchAgent in the GUI session on macOS, `systemctl --user` on Linux) [default: system] [possible values: system, user]
  -u, --user <USER>                  User the service runs as (default: $SUDO_USER or the current user)
      --hp <HOME_PATH>               Home directory used to locate the 3pm home (default: $HOME) [alias: --home]
      --service-name <SERVICE_NAME>  Service name / label (default: io.3pm.daemon)
  -h, --help                         Print help
  -V, --version                      Print version
```

## 3pm unstartup

```text
Remove the 3pm boot service (prints the command to run as root)

Usage: 3pm unstartup [OPTIONS] [PLATFORM]

Arguments:
  [PLATFORM]  Init system (launchd, systemd, openrc, rcd); auto-detected if omitted

Options:
      --json                         Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
      --service-name <SERVICE_NAME>  Service name / label (default: io.3pm.daemon)
      --level <LEVEL>                Service level the service was installed at (system, user) [default: system] [possible values: system, user]
  -h, --help                         Print help
  -V, --version                      Print version
```

## 3pm install

```text
Install a module (a bundled name or a directory holding a module.toml) and start its apps; without argument, list modules

Usage: 3pm install [OPTIONS] [SOURCE]

Arguments:
  [SOURCE]  Module name (bundled, installed, else downloaded from the site), `<name>@<version>` (from the site), or a directory holding a module.toml (omit to list what is installed and bundled)

Options:
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm uninstall

```text
Uninstall a module: delete its apps and remove its files

Usage: 3pm uninstall [OPTIONS] <NAME>

Arguments:
  <NAME>  Module name

Options:
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm link

```text
Enroll this host and start the agent app (the pasted line); alone, show the link and exit 0 only if it is established; `link stop` / `link start` / `link status` / `link delete` act on an existing link

Usage: 3pm agent link [OPTIONS] [CLUSTER_KEY] [SECRET_KEY] [SERVER]
       3pm agent link <COMMAND>

Commands:
  stop         Stop the agent app and keep the enrollment (`link start` resumes)
  start        Start the agent app again on an existing enrollment (registers it again if it was deleted)
  status       Same as `3pm agent status`
  delete       Disconnect and forget the enrollment (key, link.toml) — same as `3pm unlink`
  authorize    Let the caller whose key this is open `3pm fleet ssh` sessions here (`3pm fleet key` prints it on the caller's machine). Local: the fleet cannot add a key
  deauthorize  Stop admitting a caller's key, named by its fingerprint or its comment
  help         Print this message or the help of the given subcommand(s)

Arguments:
  [CLUSTER_KEY]  Cluster key `3pmc_<id>` (or THREEPM_LINK_CLUSTER)
  [SECRET_KEY]   Enrollment token `3pmk_<id>.<secret>` (or THREEPM_LINK_TOKEN)
  [SERVER]       Backend address: wss://host[:port] or ws://host[:port]; default wss://fleet.edgecr4ft.net (or THREEPM_LINK_SERVER, which wins)

Options:
      --insecure              Accept a clear-text ws:// server that is not loopback, or any TLS certificate (or THREEPM_LINK_INSECURE=1)
      --name <NAME>           Name of the supervised agent app [default: agent]
      --cap <CAP>             Capability to offer the backend (repeatable; default: all of metrics:read, proc:restart, proc:stop, proc:action, node:shell, node:forward)
      --no-env-keys           Do not ship the environment variable *names* of the processes
      --force                 Enroll again even if this host is already linked
      --authorize <SHA256:…>  Admit this caller's key to `3pm fleet ssh` sessions here (repeatable; also THREEPM_LINK_AUTHORIZE, comma-separated)
      --json                  Machine output on stdout (also THREEPM_JSON=1)
  -h, --help                  Print help
```

## 3pm link stop

```text
Stop the agent app and keep the enrollment (`link start` resumes)

Usage: 3pm agent link stop [OPTIONS]

Options:
      --json  Machine output on stdout (also THREEPM_JSON=1)
  -h, --help  Print help
```

## 3pm link start

```text
Start the agent app again on an existing enrollment (registers it again if it was deleted)

Usage: 3pm agent link start [OPTIONS]

Options:
      --json  Machine output on stdout (also THREEPM_JSON=1)
  -h, --help  Print help
```

## 3pm link status

```text
Same as `3pm agent status`

Usage: 3pm agent link status [OPTIONS]

Options:
      --json  Machine output on stdout (also THREEPM_JSON=1)
  -h, --help  Print help
```

## 3pm link delete

```text
Disconnect and forget the enrollment (key, link.toml) — same as `3pm unlink`

Usage: 3pm agent link delete [OPTIONS]

Options:
      --json  Machine output on stdout (also THREEPM_JSON=1)
  -h, --help  Print help
```

## 3pm link authorize

```text
Let the caller whose key this is open `3pm fleet ssh` sessions here (`3pm fleet key` prints it on the caller's machine). Local: the fleet cannot add a key

Usage: 3pm agent link authorize [OPTIONS] <SHA256:…>

Arguments:
  <SHA256:…>  The caller's key, `SHA256:…`

Options:
      --comment <COMMENT>  A name for it, shown by `3pm link status`
      --json               Machine output on stdout (also THREEPM_JSON=1)
  -h, --help               Print help
```

## 3pm link deauthorize

```text
Stop admitting a caller's key, named by its fingerprint or its comment

Usage: 3pm agent link deauthorize [OPTIONS] <SHA256:…|COMMENT>

Arguments:
  <SHA256:…|COMMENT>  `SHA256:…`, or the key's comment

Options:
      --json  Machine output on stdout (also THREEPM_JSON=1)
  -h, --help  Print help
```

## 3pm unlink

```text
Stop and delete the agent app, forget the enrollment

Usage: 3pm agent unlink [OPTIONS]

Options:
      --json  Machine output on stdout (also THREEPM_JSON=1)
  -h, --help  Print help
```

## 3pm fleet

```text
`3pm fleet --help` needs a linked host, and answered here:
[3PM][ERROR] `3pm fleet` needs a link to a fleet backend: run `3pm link <cluster-key> <token>` (nothing at ~/.3pm/agent/link.toml)

The same subcommands, with their help, are under `3pm agent fleet`.
```

## 3pm module

```text
Modules: optional features bundled with 3pm (`3pm module list`); alone, lists them and prints this help

Usage: 3pm module [OPTIONS] [COMMAND]

Commands:
  list  List installed and bundled modules
  help  Print this message or the help of the given subcommand(s)

Options:
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm module list

```text
List installed and bundled modules

Usage: 3pm module list [OPTIONS]

Options:
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm completions

```text
Generate shell completions

Usage: 3pm completions [OPTIONS] <SHELL>

Arguments:
  <SHELL>  [possible values: bash, elvish, fish, powershell, zsh]

Options:
      --json     Machine-readable JSON on stdout (NDJSON for `logs`); chrome and errors go to stderr. Ignored by CLI-local commands (startup, completions)
  -h, --help     Print help
  -V, --version  Print version
```

## 3pm agent

```text
Link this 3pm to a fleet backend and stream its processes there

Usage: 3pm agent <COMMAND>

Commands:
  link    Enroll this host and start the agent app (the pasted line); alone, show the link and exit 0 only if it is established; `link stop` / `link start` / `link status` / `link delete` act on an existing link
  unlink  Stop and delete the agent app, forget the enrollment
  status  What this host is linked to, and how the agent app is doing
  fleet   Read this node's own bucket with this node's own identity: `fleet nodes`, `fleet apps [node]`, `fleet inspect <node> [app]`, `fleet top`, `fleet call`, `fleet ssh <node>`, `fleet cp`
  help    Print this message or the help of the given subcommand(s)

Options:
  -h, --help     Print help
  -V, --version  Print version

Examples:
  3pm agent link 3pmc_… 3pmk_…     # to the hosted backend, wss://fleet.edgecr4ft.net
  3pm agent link 3pmc_… 3pmk_… wss://fleet.example.com
  3pm agent link           # the link, exit 0 only when established
  3pm agent link stop      # pause the link, keep the enrollment
  3pm agent link start     # resume it
  3pm agent link delete    # disconnect, forget the enrollment (= unlink)
  3pm agent status
  3pm agent unlink
  3pm agent fleet nodes    # every node of the bucket (needs fleet:list)
  3pm agent fleet apps edge-7
  3pm agent fleet inspect edge-7 vision
  3pm agent fleet top      # the bucket live, until `q`
  3pm agent fleet call edge-7 vision infer '{"n":1}' --file in.jpg --out .
  3pm agent fleet cp -r ./site edge-7:/srv
```

## 3pm agent link

```text
Enroll this host and start the agent app (the pasted line); alone, show the link and exit 0 only if it is established; `link stop` / `link start` / `link status` / `link delete` act on an existing link

Usage: 3pm agent link [OPTIONS] [CLUSTER_KEY] [SECRET_KEY] [SERVER]
       3pm agent link <COMMAND>

Commands:
  stop         Stop the agent app and keep the enrollment (`link start` resumes)
  start        Start the agent app again on an existing enrollment (registers it again if it was deleted)
  status       Same as `3pm agent status`
  delete       Disconnect and forget the enrollment (key, link.toml) — same as `3pm unlink`
  authorize    Let the caller whose key this is open `3pm fleet ssh` sessions here (`3pm fleet key` prints it on the caller's machine). Local: the fleet cannot add a key
  deauthorize  Stop admitting a caller's key, named by its fingerprint or its comment
  help         Print this message or the help of the given subcommand(s)

Arguments:
  [CLUSTER_KEY]  Cluster key `3pmc_<id>` (or THREEPM_LINK_CLUSTER)
  [SECRET_KEY]   Enrollment token `3pmk_<id>.<secret>` (or THREEPM_LINK_TOKEN)
  [SERVER]       Backend address: wss://host[:port] or ws://host[:port]; default wss://fleet.edgecr4ft.net (or THREEPM_LINK_SERVER, which wins)

Options:
      --insecure              Accept a clear-text ws:// server that is not loopback, or any TLS certificate (or THREEPM_LINK_INSECURE=1)
      --name <NAME>           Name of the supervised agent app [default: agent]
      --cap <CAP>             Capability to offer the backend (repeatable; default: all of metrics:read, proc:restart, proc:stop, proc:action, node:shell, node:forward)
      --no-env-keys           Do not ship the environment variable *names* of the processes
      --force                 Enroll again even if this host is already linked
      --authorize <SHA256:…>  Admit this caller's key to `3pm fleet ssh` sessions here (repeatable; also THREEPM_LINK_AUTHORIZE, comma-separated)
      --json                  Machine output on stdout (also THREEPM_JSON=1)
  -h, --help                  Print help
```

## 3pm agent link stop

```text
Stop the agent app and keep the enrollment (`link start` resumes)

Usage: 3pm agent link stop [OPTIONS]

Options:
      --json  Machine output on stdout (also THREEPM_JSON=1)
  -h, --help  Print help
```

## 3pm agent link start

```text
Start the agent app again on an existing enrollment (registers it again if it was deleted)

Usage: 3pm agent link start [OPTIONS]

Options:
      --json  Machine output on stdout (also THREEPM_JSON=1)
  -h, --help  Print help
```

## 3pm agent link status

```text
Same as `3pm agent status`

Usage: 3pm agent link status [OPTIONS]

Options:
      --json  Machine output on stdout (also THREEPM_JSON=1)
  -h, --help  Print help
```

## 3pm agent link delete

```text
Disconnect and forget the enrollment (key, link.toml) — same as `3pm unlink`

Usage: 3pm agent link delete [OPTIONS]

Options:
      --json  Machine output on stdout (also THREEPM_JSON=1)
  -h, --help  Print help
```

## 3pm agent link authorize

```text
Let the caller whose key this is open `3pm fleet ssh` sessions here (`3pm fleet key` prints it on the caller's machine). Local: the fleet cannot add a key

Usage: 3pm agent link authorize [OPTIONS] <SHA256:…>

Arguments:
  <SHA256:…>  The caller's key, `SHA256:…`

Options:
      --comment <COMMENT>  A name for it, shown by `3pm link status`
      --json               Machine output on stdout (also THREEPM_JSON=1)
  -h, --help               Print help
```

## 3pm agent link deauthorize

```text
Stop admitting a caller's key, named by its fingerprint or its comment

Usage: 3pm agent link deauthorize [OPTIONS] <SHA256:…|COMMENT>

Arguments:
  <SHA256:…|COMMENT>  `SHA256:…`, or the key's comment

Options:
      --json  Machine output on stdout (also THREEPM_JSON=1)
  -h, --help  Print help
```

## 3pm agent unlink

```text
Stop and delete the agent app, forget the enrollment

Usage: 3pm agent unlink [OPTIONS]

Options:
      --json  Machine output on stdout (also THREEPM_JSON=1)
  -h, --help  Print help
```

## 3pm agent status

```text
What this host is linked to, and how the agent app is doing

Usage: 3pm agent status [OPTIONS]

Options:
      --json  Machine output on stdout (also THREEPM_JSON=1)
  -h, --help  Print help
```

## 3pm agent fleet

```text
Read this node's own bucket with this node's own identity: `fleet nodes`, `fleet apps [node]`, `fleet inspect <node> [app]`, `fleet top`, `fleet call`, `fleet ssh <node>`, `fleet cp`

Usage: 3pm agent fleet <COMMAND>

Commands:
  nodes        Every node of the bucket: host, os, state, how long since it was seen, how many apps it runs, whose account runs it and where it is reached
  apps         Every process of the bucket, or of one node (a `3pmn_…` id, a hostname, or an unambiguous start of one)
  inspect      One node in full — and one of its apps, when named
  top          The bucket live, in two panels, until `q`: nodes on top, processes below, redrawn once a second from the event stream
  call         Place a call on another node of the bucket, with this node's own identity, and wait for it: `--file` sends bytes, `--out` keeps what came back
  ssh          An interactive login shell on another node of the bucket, like ssh (`~.` after Enter to leave) — or one command, after `--`
  attach       The terminal of a `tty = true` app on another node of the bucket, like `3pm attach` (`~.` after Enter to detach: the app runs on)
  cp           Copy files to or from another node of the bucket, like scp: one side is `<node>:<path>`
  known-nodes  The shell keys of the nodes this host has met, pinned at the first session (`~/.3pm/known_nodes`, like ssh's `known_hosts`)
  key          This host's caller key (`cli.key`, or THREEPM_CLI_KEY): its fingerprint and the line a node needs to admit it; `key new`, `key push`, `key revoke`
  help         Print this message or the help of the given subcommand(s)

Options:
  -h, --help  Print help
```

## 3pm agent fleet nodes

```text
Every node of the bucket: host, os, state, how long since it was seen, how many apps it runs, whose account runs it and where it is reached

Usage: 3pm agent fleet nodes [OPTIONS]

Options:
      --json            Machine output on stdout (also THREEPM_JSON=1)
      --ca-file <PATH>  Trust the certificates of this PEM file **as well as** the built-in roots — for a private CA, or a public root this build is too old to know
  -h, --help            Print help
```

## 3pm agent fleet apps

```text
Every process of the bucket, or of one node (a `3pmn_…` id, a hostname, or an unambiguous start of one)

Usage: 3pm agent fleet apps [OPTIONS] [NODE]

Arguments:
  [NODE]  

Options:
      --json            Machine output on stdout (also THREEPM_JSON=1)
      --ca-file <PATH>  Trust the certificates of this PEM file **as well as** the built-in roots — for a private CA, or a public root this build is too old to know
  -h, --help            Print help
```

## 3pm agent fleet inspect

```text
One node in full — and one of its apps, when named

Usage: 3pm agent fleet inspect [OPTIONS] <NODE> [APP]

Arguments:
  <NODE>  
  [APP]   

Options:
      --json            Machine output on stdout (also THREEPM_JSON=1)
      --ca-file <PATH>  Trust the certificates of this PEM file **as well as** the built-in roots — for a private CA, or a public root this build is too old to know
  -h, --help            Print help
```

## 3pm agent fleet top

```text
The bucket live, in two panels, until `q`: nodes on top, processes below, redrawn once a second from the event stream

Usage: 3pm agent fleet top [OPTIONS]

Options:
      --json            Machine output on stdout (also THREEPM_JSON=1)
      --ca-file <PATH>  Trust the certificates of this PEM file **as well as** the built-in roots — for a private CA, or a public root this build is too old to know
  -h, --help            Print help
```

## 3pm agent fleet call

```text
Place a call on another node of the bucket, with this node's own identity, and wait for it: `--file` sends bytes, `--out` keeps what came back

Usage: 3pm agent fleet call [OPTIONS] <NODE> <APP> <ACTION> [PARAMS]

Arguments:
  <NODE>    A node of the bucket: a `3pmn_…` id, a hostname, or an unambiguous start of one
  <APP>     App name on that node (never `all`: a call names one app)
  <ACTION>  Action name, as the app declared it
  [PARAMS]  Parameters, as JSON (default null)

Options:
      --instance <N>        One instance of the app (default 0; required with `--file`)
      --src <SOURCE>        The source (process of the app) to call, when several declare it
      --timeout <DURATION>  How long to wait (`30s`, `2m`, `1500ms`); the declaration's own timeout bounds it (default 30s, ceiling 10m)
      --file <PATH>         A file of this host to send with the call (the action must declare an `input`)
      --out <DIR>           Directory to write the file the action produced into
      --stream              Not from here: a stream is opened by an app with `io.open`
      --json                Machine output on stdout (also THREEPM_JSON=1)
      --ca-file <PATH>      Trust the certificates of this PEM file **as well as** the built-in roots — for a private CA, or a public root this build is too old to know
  -h, --help                Print help
```

## 3pm agent fleet ssh

```text
An interactive login shell on another node of the bucket, like ssh (`~.` after Enter to leave) — or one command, after `--`

Usage: 3pm agent fleet ssh [OPTIONS] <NODE> [-- <CMD>...]

Arguments:
  <NODE>    A node of the bucket: a `3pmn_…` id, a hostname, or an unambiguous start of one; `<user>@<node>` is `-l <user> <node>`
  [CMD]...  The command to run instead of a shell, through the node's login shell (`sh -c`); the process exits with its code. One word is the whole line, as ssh has it (`-- 'ls | wc -l'`); several are each quoted, so they reach the program as given (`-- cat 'my file'`)

Options:
  -l, --user <USER>               Run as this unix account of the node rather than as its agent's own; only an agent that runs as root becomes someone else
  -t                              Run the command on a pty, even when stdin is not a terminal
  -T                              Run the command without a pty, even from a terminal: stdin, stdout and stderr as pipes, binary-safe
  -L <[BIND:]PORT:HOST:HOSTPORT>  Forward a local port to `host:hostport` as the node reaches it, every connection its own; repeatable. The bind address is 127.0.0.1 unless given (`*` for every interface)
  -R <[BIND:]PORT:HOST:HOSTPORT>  Forward a port of the node to `host:hostport` as this host reaches it, every connection its own; repeatable. The node binds its loopback unless given, and anything else only when its link.toml says `[shell] gateway_ports = true`; port 0: one the node picks, printed
  -N                              No shell: only the `-L` and `-R` forwards, until Ctrl-C
  -W <HOST:PORT>                  Connect stdin and stdout to `host:port` as the node reaches it, like ssh's `-W`: for ssh's `ProxyCommand`. Exits 0 when both ways are closed, 255 when the node or the backend refused or cut it
  -v                              Say on stderr which way the session's legs go: through this host's agent (LAN direct when they can) or the backend's relay, and why
  -C, --compress                  Compress the command's bytes, both ways, when it runs without a pty (`-T`, or stdin not a terminal) and the node's agent can (zstd, inside the encrypted channel; `-v` says whether it is). Never a shell, a pty or a forward
      --accept-new                Pin the key of a node met for the first time without asking (for scripts); a key that changed is still refused
      --no-e2e                    In clear, for a node whose agent is too old to encrypt: the backend can read and change the session. An up-to-date node refuses it
      --record                    Record the session (its screen and its command lines) when the bucket records shells (`record_shells`); without it, never — the handshake binds it, so the backend cannot record it anyway
      --json                      Machine output on stdout (also THREEPM_JSON=1)
      --ca-file <PATH>            Trust the certificates of this PEM file **as well as** the built-in roots — for a private CA, or a public root this build is too old to know
  -h, --help                      Print help
```

## 3pm agent fleet attach

```text
The terminal of a `tty = true` app on another node of the bucket, like `3pm attach` (`~.` after Enter to detach: the app runs on)

Usage: 3pm agent fleet attach [OPTIONS] <NODE> <APP>

Arguments:
  <NODE>  A node of the bucket: a `3pmn_…` id, a hostname, or an unambiguous start of one
  <APP>   A `tty = true` app of that node, by name

Options:
      --instance <N>    One instance of the app (default 0)
      --raw             The app's bytes as it wrote them, not the node's render of its screen
      --peek            Print the screen as text and leave
      --read-only       Watch only: no keystroke and no resize reaches the app
      --accept-new      Pin the key of a node met for the first time without asking (for scripts); a key that changed is still refused
      --no-e2e          In clear, for a node whose agent is too old to encrypt: the backend can read and change the session. An up-to-date node refuses it
      --record          Record the session (its screen and its command lines) when the bucket records shells (`record_shells`); without it, never — the handshake binds it, so the backend cannot record it anyway
      --json            Machine output on stdout (also THREEPM_JSON=1)
      --ca-file <PATH>  Trust the certificates of this PEM file **as well as** the built-in roots — for a private CA, or a public root this build is too old to know
  -h, --help            Print help
```

## 3pm agent fleet cp

```text
Copy files to or from another node of the bucket, like scp: one side is `<node>:<path>`

Usage: 3pm agent fleet cp [OPTIONS] <SRC… DST> <SRC… DST>...

Arguments:
  <SRC… DST> <SRC… DST>...  The sources, then the destination. Exactly one side is `<node>:<path>` (a node as `fleet ssh` takes it; `<node>:` alone is its home); a local path with a `:` before any `/` is written `./a:b`. Several sources need a destination directory

Options:
  -r                    Copy directories, whole (a file is copied as a file)
  -p                    Keep each file's mode and modification time
  -l, --user <USER>     Run as this unix account of the node rather than as its agent's own; only an agent that runs as root becomes someone else
      --no-compress     Send the bytes as they are. By default they are compressed inside the encrypted channel when the node's agent can (zstd; a file that does not compress goes as it is)
      --accept-new      Pin the key of a node met for the first time without asking (for scripts); a key that changed is still refused
      --no-e2e          In clear, for a node whose agent is too old to encrypt: the backend can read and change the session. An up-to-date node refuses it
      --record          Record the session (its screen and its command lines) when the bucket records shells (`record_shells`); without it, never — the handshake binds it, so the backend cannot record it anyway
      --json            Machine output on stdout (also THREEPM_JSON=1)
      --ca-file <PATH>  Trust the certificates of this PEM file **as well as** the built-in roots — for a private CA, or a public root this build is too old to know
  -h, --help            Print help
```

## 3pm agent fleet known-nodes

```text
The shell keys of the nodes this host has met, pinned at the first session (`~/.3pm/known_nodes`, like ssh's `known_hosts`)

Usage: 3pm agent fleet known-nodes <COMMAND>

Commands:
  ls    The pinned nodes: id, fingerprint, name, date
  rm    Forget a node's key (its `3pmn_…` id, or its name as pinned), like `ssh-keygen -R`: the next session asks again
  help  Print this message or the help of the given subcommand(s)

Options:
  -h, --help  Print help
```

## 3pm agent fleet known-nodes ls

```text
The pinned nodes: id, fingerprint, name, date

Usage: 3pm agent fleet known-nodes ls [OPTIONS]

Options:
      --json            Machine output on stdout (also THREEPM_JSON=1)
      --ca-file <PATH>  Trust the certificates of this PEM file **as well as** the built-in roots — for a private CA, or a public root this build is too old to know
  -h, --help            Print help
```

## 3pm agent fleet known-nodes rm

```text
Forget a node's key (its `3pmn_…` id, or its name as pinned), like `ssh-keygen -R`: the next session asks again

Usage: 3pm agent fleet known-nodes rm [OPTIONS] <NODE>

Arguments:
  <NODE>  

Options:
      --json            Machine output on stdout (also THREEPM_JSON=1)
      --ca-file <PATH>  Trust the certificates of this PEM file **as well as** the built-in roots — for a private CA, or a public root this build is too old to know
  -h, --help            Print help
```

## 3pm agent fleet key

```text
This host's caller key (`cli.key`, or THREEPM_CLI_KEY): its fingerprint and the line a node needs to admit it; `key new`, `key push`, `key revoke`

Usage: 3pm agent fleet key [OPTIONS] [COMMAND]

Commands:
  new     A new caller key in a file of its own, offline (a rescue key: keep it off this host; use it with THREEPM_CLI_KEY=<file>)
  push    Admit a key on nodes, through a `fleet ssh` session on each (this host's key must already be admitted there)
  revoke  Stop admitting a key on nodes, the same way
  help    Print this message or the help of the given subcommand(s)

Options:
      --json            Machine output on stdout (also THREEPM_JSON=1)
      --ca-file <PATH>  Trust the certificates of this PEM file **as well as** the built-in roots — for a private CA, or a public root this build is too old to know
  -h, --help            Print help
```

## 3pm agent fleet key new

```text
A new caller key in a file of its own, offline (a rescue key: keep it off this host; use it with THREEPM_CLI_KEY=<file>)

Usage: 3pm agent fleet key new [OPTIONS] --out <PATH>

Options:
      --out <PATH>      Where to write it (0600); an existing file is never replaced
      --json            Machine output on stdout (also THREEPM_JSON=1)
      --ca-file <PATH>  Trust the certificates of this PEM file **as well as** the built-in roots — for a private CA, or a public root this build is too old to know
  -h, --help            Print help
```

## 3pm agent fleet key push

```text
Admit a key on nodes, through a `fleet ssh` session on each (this host's key must already be admitted there)

Usage: 3pm agent fleet key push [OPTIONS] <NODE… SHA256:…>...

Arguments:
  <NODE… SHA256:…>...  The nodes (as `fleet ssh` takes them), then the key's `SHA256:…`

Options:
      --all                Every node of the bucket
      --comment <COMMENT>  A name for the key, shown by `3pm link status` (push only)
      --accept-new         Pin the key of a node met for the first time without asking
      --json               Machine output on stdout (also THREEPM_JSON=1)
      --ca-file <PATH>     Trust the certificates of this PEM file **as well as** the built-in roots — for a private CA, or a public root this build is too old to know
  -h, --help               Print help
```

## 3pm agent fleet key revoke

```text
Stop admitting a key on nodes, the same way

Usage: 3pm agent fleet key revoke [OPTIONS] <NODE… SHA256:…>...

Arguments:
  <NODE… SHA256:…>...  The nodes (as `fleet ssh` takes them), then the key's `SHA256:…`

Options:
      --all                Every node of the bucket
      --comment <COMMENT>  A name for the key, shown by `3pm link status` (push only)
      --accept-new         Pin the key of a node met for the first time without asking
      --json               Machine output on stdout (also THREEPM_JSON=1)
      --ca-file <PATH>     Trust the certificates of this PEM file **as well as** the built-in roots — for a private CA, or a public root this build is too old to know
  -h, --help               Print help
```

## 3pm serve

```text
Serve a directory over HTTP as a 3pm-managed app (pm2 serve)

Usage: 3pm serve [OPTIONS] [PATH] [PORT]

Arguments:
  [PATH]  Directory to serve (default: current directory), or a single .html file: its directory is served with that page as homepage, opened in your browser
  [PORT]  Port to listen on (default: 8080, or the next free port above it; 0 lets the kernel pick)

Options:
      --port <PORT>                     Port to listen on (same as the positional)
      --host <HOST>                     Address to bind (default: 0.0.0.0)
      --spa                             Single-page app: serve the homepage for unknown paths
      --ftp                             Directory listing, like `python -m http.server`
      --homepage <FILE>                 File served at `/` (default: /index.html)
      --basic-auth-username <USER>      Basic auth user (with --basic-auth-password)
      --basic-auth-password <PASSWORD>  Basic auth password (with --basic-auth-username)
      --name <NAME>                     App name (default: static-page-server-<port>)
      --namespace <NAMESPACE>           App namespace
      --no-qr                           Do not print the QR code of the network link
      --attach                          Stay in the foreground and stop the server on Ctrl-C (the default when serving a single .html file)
      --detach                          Leave the server running in the background and return (the default for a directory)
      --json                            Machine output: {"affected":[…]} on stdout (also THREEPM_JSON=1)
  -h, --help                            Print help
  -V, --version                         Print version

Examples:
  3pm serve ./dist 8080 --spa
  3pm serve --port 9000 --ftp
  3pm serve . --basic-auth-username admin --basic-auth-password s3cret
```
