Skip to content

PhyStack CLI (v7 preview)

Note: v7 is a preview release, published under its own npm tag. Installing it is opt-in and leaves your current CLI untouched until you choose to upgrade. Reference documentation for individual commands is still in progress. Until it is published, the built-in help is the authoritative reference: phy --help, phy <command> --help, and phy man all include worked examples.

v7 is a new major version of the PhyStack CLI (phy), with a number of improvements over v6.

This page covers what changed, how to install the preview, how to migrate an existing v6 setup, and how to run the CLI in a pipeline.

  • Uniform command structure: Every command follows phy <resource> <verb>, with the same verbs across resources: list, get, create, update, delete.
  • Consistent global flags: -o/--output, --tenant, --dry-run, -y/--yes, --no-input, and --quiet behave identically on every command.
  • Scriptable output: Tabular output in a terminal, JSON when redirected, and documented exit codes for branching.
  • Confirmation and preview on writes: Destructive commands require confirmation, --dry-run resolves a request without sending it, and settings are validated against the app’s schema before submission.
  • Keychain-backed credentials: Tokens are held in the operating system keychain where one is available, with a permission-restricted file as the fallback.
  • Compiled binary: A single native executable, with no dependency tree to install or maintain.
  • Wider command coverage: Media library, analytics, peripherals, device containers and logs, installation settings, and access tokens for automation.
  • macOS on Apple Silicon and Intel
  • Linux x64 and arm64, including Alpine and other musl distributions
  • Windows x64
Terminal window
npm i -g @phystack/cli@dev

The @dev tag is required. npm i -g @phystack/cli without a tag installs the current stable v6 release, and continues to do so until v7 becomes generally available.

Terminal window
phy --version

You should see output similar to:

phy 7.0.0-dev.14 (f597f18e 2026-08-17)
target: aarch64-apple-darwin

Automatic updates are disabled during the preview. Run the install command again to upgrade:

Terminal window
npm i -g @phystack/cli@dev
Terminal window
npm i -g @phystack/[email protected]

v7 does not modify v6 configuration. Your existing v6 login and selected tenant remain intact, and nothing needs to be undone before reverting.

Two checks prevent the most common problems when moving from v6.

v7 reads a new set of PHY_* environment variables and ignores the older PHYSTACK_* and PHYGRID_* variables without warning. A legacy variable left in your shell profile will appear to have no effect on the CLI. List anything still set:

Terminal window
env | grep -E 'PHY_|PHYSTACK_|PHYGRID_'

The variables you are most likely to be carrying over:

Legacy variablev7 replacement
PHYSTACK_DEVELOPER_TOKENPHY_TOKEN
PHYSTACK_TENANT_IDPHY_TENANT
PHYSTACK_REGISTRY_PUSH_USERNAME / PHYSTACK_REGISTRY_PUSH_PASSWORDPHY_REGISTRY_PUSH_USERNAME / PHY_REGISTRY_PUSH_PASSWORD
PHYSTACK_REGISTRY_PULL_USERNAME / PHYSTACK_REGISTRY_PULL_PASSWORDPHY_REGISTRY_PULL_USERNAME / PHY_REGISTRY_PULL_PASSWORD

Any other variable beginning with PHYSTACK_ or PHYGRID_ is no longer read.

The retired @phygrid/cli package also installs a command named phy. When both packages are present, the active command depends on the order they were installed. Remove the old package:

Terminal window
npm uninstall -g @phygrid/cli
hash -r
which -a phy

which -a phy should return a single path.

Terminal window
phy login

The command prints a one-time code, opens the approval page in your browser, and completes once you approve. Confirm that the code shown in your terminal matches the code in the browser before approving. Your credential is never printed.

Note: You need to log in again even if you were already authenticated with v6, because v7 stores credentials in a different location. Tokens already in use in scripts are unaffected.

On a remote or headless machine, print the verification URL and code to complete authentication on another device:

Terminal window
phy login --no-browser
Terminal window
phy whoami
phy tenant list
phy tenant set <your-tenant-slug>

The tenant you set becomes the default for subsequent commands.

Terminal window
phy device list

If your devices are listed without errors, the CLI is configured correctly.

Terminal window
phy logout

This removes the local credential and revokes the session.

v6v7
ConfigurationMultiple files in ~/.config/phystack-cli/A single file, ~/.config/phy/config.toml
Credential at restPlain text fileThe operating system keychain (macOS Keychain, Windows Credential Manager, or the Linux Secret Service), falling back to a 0600 file where no keychain is available

The two locations are independent. v7 never reads, writes, or deletes v6 configuration, and v6 has no knowledge of v7 configuration. Either version can be installed over the other without affecting the other’s state, and switching back leaves you logged in where you were.

Terminal window
phy config view

Secrets are redacted in this output, which makes it safe to attach to a support request.

Terminal window
phy logout
rm -rf ~/.config/phy

Tip: Use phy logout rather than deleting keychain entries manually. It clears both the keychain entry and the configuration file on every platform.

A context pairs a login with a default tenant. One context is created for you the first time you log in, which is all most users need. Additional contexts are useful when you work as more than one user, or when you want to switch between default tenants.

Terminal window
phy config get-contexts # List contexts, the active one is marked
phy config current-context # Print the name of the active context
phy config use-context <name> # Switch the active context

Two behaviors to be aware of:

  • set-context saves a context without switching to it. When the active context is unchanged, the command reports this and prints the use-context command to run.
  • set-context modifies only the fields you pass. All other fields on an existing context are preserved. Pass --replace to reset a context completely.

To override the tenant for a single command, pass --tenant <id-or-slug>.

The complete command list is available through phy --help. The following commands were renamed:

v6v7
phy dev listphy device list
phy dev shell <device>phy device shell <device>
phy dev vnc / forward / screen-devtools / cpphy device vnc / forward / screen-devtools / cp
phy tenant select <slug>phy tenant set <slug-or-id>
phy app build (building a bundle locally)phy app package
phy app publishphy app build create, with --publish to do both in one step
phy inst listphy installation list, and phy inst remains as an alias
phy login --use-device-codephy login --no-browser
phy login --use-developer-token <token>Set PHY_TOKEN, or use phy auth set-token
phy updateRun npm i -g @phystack/cli@dev again

Devices can be identified by id or name in every command, and the verb always precedes the target:

Terminal window
phy device shell my-device # Correct
phy device my-device shell # Incorrect

Output adapts to its destination. Commands print a table in a terminal and JSON when the output is piped or redirected, which means the following already produces JSON:

Terminal window
phy device list | jq '.[].name'

To request a specific format, pass -o:

FormatUse
tableDefault in a terminal
jsonDefault when piped or redirected
yamlConfiguration-style output
wideAdditional columns
nameIds or names only, one per line

Exit codes are documented and stable. Scripts should branch on the exit code rather than parse command output:

CodeMeaning
0Success
2Validation error or bad request
3Authentication or permission error
4Not found
5Conflict
6Server error
7Partially applied, for bulk operations

phy device exec returns the exit code of the remote command, in the same way as ssh. A command that fails on the device is therefore visible to your script.

Lists are paginated with a default page size of 50. Use --page, --page-size, --sort, and --all to control the result set. In a terminal, long tables page as you read them.

The CLI is intended to run unattended. When it is not attached to a terminal, it defaults to non-interactive behavior: JSON output, no spinners, and no prompts. --no-input forces this behavior in any context, and it is applied automatically when CI is set.

A pipeline requires no phy login and no on-disk configuration. Credentials and scope are supplied entirely through environment variables.

Do not run phy login in a pipeline. Create a token once from your own machine:

Terminal window
phy token create ci-pipeline # The secret is displayed once
phy token list # Names and expiry only, never the secret
phy token delete <id> # Revoke

Store the secret in your CI provider’s secret storage. PHY_TOKEN takes precedence over every other credential source, and works on a machine that has never logged in. Tokens already in use with v6 remain valid.

env:
PHY_TOKEN: ${{ secrets.PHY_TOKEN }}
PHY_TENANT: <tenant-id>

Important: PHY_TENANT, or an explicit --tenant on each command, is required in CI. Commands that modify data refuse to run unattended against a tenant inherited from saved configuration. This prevents an automated job from writing to whichever tenant happened to be selected.

Screen and web apps require no credentials beyond PHY_TOKEN and PHY_TENANT. The front-end bundle is packaged into the .gridapp archive that the CLI produces, and there is no container image to push.

Build your project, then submit and publish in one step:

- run: npm ci
- run: npm run build
- run: phy app build create my-screen-app --dir . --publish

--dir packages the source for you and --publish waits for processing to finish before publishing the build. The version is derived automatically as the next minor version after the app’s latest build. --push and --image do not apply to these app types and are rejected.

Edge apps are distributed as container images. A pipeline that builds one needs Docker available and two registry credentials, supplied through the environment rather than through the interactive phy registry login:

VariablePurpose
PHY_REGISTRY_PUSH_USERNAME
PHY_REGISTRY_PUSH_PASSWORD
Pushes the image built on the runner
PHY_REGISTRY_PULL_USERNAME
PHY_REGISTRY_PULL_PASSWORD
Attached to the build and used by devices to retrieve the image

These variables take precedence over any credential stored locally by phy registry login.

Important: The pull credential must be read-only. It is attached to the build and distributed to devices, and a write-capable token is rejected. Create two separate tokens in your registry: one read-only for pulling, and one with write access for pushing.

A complete edge app job:

- uses: docker/setup-buildx-action@v3
- run: phy app build create my-edge-app --dir . --push --publish
env:
PHY_TOKEN: ${{ secrets.PHY_TOKEN }}
PHY_TENANT: ${{ vars.PHY_TENANT }}
PHY_REGISTRY_PUSH_USERNAME: ${{ secrets.REGISTRY_PUSH_USERNAME }}
PHY_REGISTRY_PUSH_PASSWORD: ${{ secrets.REGISTRY_PUSH_PASSWORD }}
PHY_REGISTRY_PULL_USERNAME: ${{ secrets.REGISTRY_PULL_USERNAME }}
PHY_REGISTRY_PULL_PASSWORD: ${{ secrets.REGISTRY_PULL_PASSWORD }}

Additional details for the edge path:

  • --push builds a multi-architecture image for linux/arm64/v8 and linux/amd64 with docker buildx, and pushes it before the build is submitted. The CLI creates the buildx builder it needs if one is not already present.
  • The push credential is seeded into a temporary Docker configuration for the duration of that command. There is no docker login step, and the credential never reaches the platform or a device.
  • --image is optional. When it is omitted alongside --push, the image reference is derived as <registry-host>/<namespace>/<app>:<version>. Pass it explicitly to publish to a different repository.
  • Without --push, the image must already exist in the registry. The CLI verifies the tag before submitting the build.
  • When no pull credential is configured, the build fails with a message naming these variables rather than publishing an image that devices cannot retrieve. For a public image that requires no credential, pass --no-registry-credential instead.
  • Use --build-context <dir> when your Dockerfile is not in the working directory.

Every command emits JSON when its output is redirected, and exit codes are stable, which lets a script branch without parsing prose:

Terminal window
phy device list -o json > devices.json
phy app build list my-edge-app -o json | jq -r '.items[0].version'

See Output Formats and Exit Codes for the full list.

  • Destructive commands such as delete and reboot require confirmation. -y skips the prompt, and is mandatory for those commands when they are not run from a terminal.
  • --dry-run prints the change that would be sent and sends nothing. Read-only lookups, such as resolving a device name, still run.
  • Commands that modify data refuse to run in a script when the tenant was inherited from the active context rather than passed explicitly with --tenant.
  • Settings writes are validated against the app’s schema on your machine. An invalid value fails immediately with a readable message instead of a server error.

Container Registry Credentials for Edge Apps

Section titled “Container Registry Credentials for Edge Apps”

Edge apps require two distinct registry credentials, managed with the same commands you would expect from docker login:

Terminal window
phy registry login docker.io # Prompts for a read-only pull credential and a push credential
phy registry status # Shows what is configured, never the secrets
phy registry logout docker.io
  • Pull credential: Must be read-only. It is attached to the build and used by devices to retrieve the image, and a write-capable token is rejected.
  • Push credential: Used locally to push the image. It is never sent to the platform or to a device.

Both are stored in your keychain, and no data leaves your machine when you run phy registry login.

phy registry login is the interactive path for a workstation. In a pipeline, supply both credentials through environment variables as described in Edge Apps.

Important: If your app’s package.json still contains an embedded docker-credentials block, or pins an old CLI as a dependency, phy app package stops and reports what to remove. The CLI is a global tool in v7 and is never pinned inside a project.

The following commands have no v6 equivalent:

  • phy media: Media library operations, including ls, tree, mkdir, mv, rm, upload, download, and search
  • phy analytics: Reports over your data, including schema, query, rank, and raw-events
  • phy peripheral and phy descriptor: Read peripheral state, change operator-writable settings, invoke actions, and browse or publish descriptors
  • phy token: Access tokens for automation
  • phy registry: Pull and push registry credentials
  • phy config: Context management
  • phy device logs, exec, reboot, and container list|logs|stats|restart|stop|exec
  • phy installation settings get|set|clear: Installation settings with device-level overrides and a --resolved view, alongside activate, deactivate, clone, and device add|remove|list
  • phy space, user, role, invitation, content-tag, short-url, cloud-instance
  • phy openapi: The live API specification
  • phy completions <shell> and phy man: Shell completion and a manual page
  • phy whoami and phy auth status: Confirm your identity and which credential is in use

The CLI behaves consistently across platforms. The following details are specific to individual environments.

Remote Device Access Requires an SSH Client

Section titled “Remote Device Access Requires an SSH Client”

phy device shell, exec, cp, logs, forward, vnc, and screen-devtools operate over an SSH tunnel and use the ssh client installed on your machine. macOS and Windows 10/11 include one. On a minimal Linux installation or inside a container, install your distribution’s openssh-client package first.

Linux: Credential Storage Depends on the Session

Section titled “Linux: Credential Storage Depends on the Session”

On a desktop with a running keyring service such as GNOME Keyring or KWallet, your credential is stored there. On a headless or SSH session with no keyring available, and on the Alpine and musl builds, the CLI stores it in ~/.config/phy/config.toml with 0600 permissions and reports that it has done so. Both are supported. Where the file is used, treat it as a secret.

Linux and Headless: Authentication Without a Browser

Section titled “Linux and Headless: Authentication Without a Browser”

phy login only attempts to open a browser when a graphical session is present. Otherwise it prints the verification URL and code for you to complete elsewhere. Pass phy login --no-browser to force this behavior.

Most Linux desktops have no handler registered for vnc:// addresses. On those systems phy device vnc forwards the port and prints the address for you to open in your viewer. Pass --no-open to skip the launch attempt entirely.

v7 is the platform command line. Capabilities that v6 bundled but that work better as dedicated tools have been separated out, which keeps the CLI focused and allows each tool to release on its own schedule.

  • Device simulator: Shipped as its own package, @phystack/device-simulator, installed and versioned separately. The command is phy-simulator, and it replaces phy simulator from v6. See the Simulator reference.
  • PhyOS image flashing: Becoming a standalone tool in a future release, replacing phy flash from v6.

A number of v6 commands fall outside the scope of v7 and are still available in v6:

  • Local PhyOS virtual machines, phy vm
  • Management of legacy IoT-Edge devices, phy legacydev, because v7 device commands target PhyOS devices
  • Local developer-mode runs against a device, phy dev develop and phy app deploy
  • Screen app emulation device selection, phy dev select
  • Importing an HTML project as a bundle, phy app import
  • Uploading an app description, phy app upload-description, where v7 uses phy app update --set instead

If your work depends on any of these, continue to use v6 for that task. Switching between the two versions is a single install command in either direction, as described in Reverting to v6.

Terminal window
phy # Overview and common commands
phy --help # All command groups
phy <command> --help # Verbs, flags, and worked examples
phy man # Manual page

This is a preview release, and reports of problems during the preview are valuable. When reporting an issue, include the exact command you ran, what you expected, what happened, and the output of phy --version. Adding -v to the failing command provides request-level detail that is usually enough to identify the cause.

For additional help, contact support at [email protected].