login β
pfy login saves the Manager URL and a Personal Access Token (PAT) to ~/.pfy/config.yaml. By default it gets the PAT through a browser sign-in; it also accepts an existing PAT. pfy whoami checks the saved credentials and pfy logout removes the token.
Synopsis β
pfy login --server <url> # browser single sign-on (default)
pfy login --server <url> --token-stdin # headless: PAT from stdin
pfy login --server <url> --token <token> # headless: PAT on the command lineDescription β
The login command saves your ProductifyFW Manager URL and a Personal Access Token (PAT) to ~/.pfy/config.yaml.
By default login uses browser single sign-on: it signs you in through the platform's identity provider and provisions a named PAT for the CLI, with no copying of tokens out of the Manager UI. The flow is a device-authorization grant, so it also works over SSH and on headless machines: the CLI prints a URL and a short user code you can open from any browser.
The browser flow runs whenever no token source is present (no --token, no --token-stdin, no PFY_MANAGER_TOKEN environment variable) and --no-web was not given. Supplying any token source keeps the headless path (the CI path), and --web forces the browser flow even when a token was provided.
--server is required and must be an absolute http:// or https:// URL with a host; a bare host:port or any other malformed value is rejected with invalid --server value before anything is saved. It must point at the proxied entrypoint, not the Manager directly: the Manager only accepts PAT traffic carrying the X-Token-* headers the proxy injects, so a direct URL yields a 401 regardless of the token. Do not include the /query path; it is appended automatically.
Flags β
| Flag | Aliases | Required | Description |
|---|---|---|---|
--server | Yes | ProductifyFW Manager URL (http(s)π/host:port), the proxied entrypoint | |
--token | --pat | No | Personal Access Token, passed on the command line (visible in ps and shell history; prefer --token-stdin) |
--token-stdin | No | Read the PAT from stdin, keeping it out of ps, shell history and CI logs | |
--no-verify | No | Skip the round-trip that checks the token is accepted before saving it | |
--web | No | Force the browser SSO login (device flow) even if a token was provided | |
--no-web | No | Never use the browser flow; require a token via --token/--token-stdin/PFY_MANAGER_TOKEN | |
--no-browser | No | Run the browser flow but do not launch a browser; print the URL and wait (implied by --ci, NO_BROWSER, or an empty BROWSER) |
--token and --token-stdin are mutually exclusive:
$ echo $TOKEN | pfy login --server https://manager.example.com --token-stdin --token pat_x
Error: --token and --token-stdin are mutually exclusive: pass the token on the command line or on stdin, not bothWith --no-web and no token source, login fails with an error naming every token source (--token-stdin, --token, PFY_MANAGER_TOKEN) and the browser alternative.
Examples β
Browser single sign-on (default) β
pfy login --server https://manager.example.comOutput:
To authorize this CLI, open the following URL in your browser:
https://manager.example.com/β¦
Confirm this code matches what the browser shows: ABCD-EFGH
Waiting for approval...
Successfully logged in to ProductifyFW manager at: https://manager.example.com
Minted a user management token named "pfy/alice@laptop/2026-07-21T09:00:00Z".
This is a user management token, distinct from an app-scoped proxy PAT. Revoke it anytime in the manager under your account's tokens.
Configuration saved to ~/.pfy/config.yamlOver SSH or on a machine without a browser, pass --no-browser (or set NO_BROWSER=1): the URL and code are printed and you open them from any device. The whole flow times out after roughly ten minutes if never approved.
Save a PAT from stdin (headless, recommended for CI) β
echo "$PFY_TOKEN" | pfy login --server https://manager.example.com --token-stdinOutput:
Successfully logged in to ProductifyFW manager at: https://manager.example.com
Configuration saved to ~/.pfy/config.yamlSurrounding whitespace is trimmed, so the trailing newline from echo is not saved as part of the secret. If stdin is empty, login fails rather than saving a blank token:
$ printf "" | pfy login --server https://manager.example.com --token-stdin
Error: --token-stdin was given but stdin was emptySave a PAT on the command line β
pfy login --server https://manager.example.com --token pat_AbCd1234_9f8e7d6c5b4a...Avoid --token on shared machines
A token passed as --token <value> is visible in ps output and shell history. Prefer --token-stdin. In CI, you can also set PFY_MANAGER_TOKEN and skip login entirely (see CI/CD Authentication).
Token verification β
A token supplied via flags or PFY_MANAGER_TOKEN is verified against the Manager before it is saved, so a mistyped or expired PAT fails at login rather than at first real use:
Error: not saving credentials: β¦ (pass --no-verify to skip this check)Pass --no-verify for offline/setup scenarios where the Manager is not reachable yet.
How the browser flow works β
ββββββββββββββββ 1. start session ββββββββββββββββ
β pfy login βββββββββββββββββββββββββββββΆβ Manager β
β ββββββββββββββββββββββββββββββ β
β ephemeral β verification URL ββββββββ¬ββββββββ
β keypair β + user code β
β (RAM only) β β 2. sign in via
β β β OIDC and approve
β β βββββββββββββ β on the consent page
β β β Browser βββββββββββββ
β β βββββββββββββ shows: user code
β β 3. poll ββββββββββββββββ
β βββββββββββββββββββββββββββΆβ Manager β
β ββββββββββββββββββββββββββββ β
β β PAT, encrypted to the ββββββββββββββββ
β β CLI's public key (single-read)
β β
β 4. decrypt in RAM, save PAT to ~/.pfy/config.yaml (0600)
ββββββββββββββββ- The CLI generates an ephemeral keypair in memory (never written to disk) and starts a login session at the Manager.
- The browser opens the verification URL (always also printed, with the user code, for SSH sessions). You authenticate through the normal OIDC sign-in and approve, but only if the code matches what the CLI printed; that check is what prevents someone else's login request from being authorized.
- On approval, the Manager mints a PAT named
pfy/<user>@<host>/<timestamp>and returns it sealed to the CLI's public key. - The CLI decrypts it in memory and persists only the PAT to
~/.pfy/config.yaml; the keypair is discarded.
The minted token is a normal user management token: it appears in the PAT list in the Manager (name, last used) and is revocable there at any time. It is distinct from an app-scoped proxy PAT.
Configuration File β
The login command creates or updates ~/.pfy/config.yaml:
manager_url: https://manager.example.com
token: your-personal-access-tokenFile Location β
- Default:
~/.pfy/config.yaml - Permissions: automatically set to
0600(read/write for owner only)
Manual Configuration β
You can also manually create the configuration file:
mkdir -p ~/.pfy
cat > ~/.pfy/config.yaml <<EOF
manager_url: https://manager.example.com
token: your-personal-access-token
EOF
chmod 600 ~/.pfy/config.yamlPersonal Access Tokens β
To mint a token manually (for CI, or with --no-web), use the Manager web interface: open the Personal Access Tokens page under your account settings, create a token with a descriptive name (for example "CI - release pipeline") and an optional expiry, and copy it immediately; it is shown only once.
Token Security β
- Store the PAT via
PFY_MANAGER_TOKENrather than pasting it into shell history - Rotate tokens regularly; revoke tokens for machines you no longer use
- Never commit tokens to version control
- Review your PAT list (name + last used) periodically
Multiple Environments β
Development and Production β
Manage multiple Manager instances:
# Login to development
pfy login --server https://dev.manager.example.com
# Later, override for production commands
pfy --manager https://prod.manager.example.com \
--token prod-token \
project listEnvironment-Specific Aliases β
The CLI always reads ~/.pfy/config.yaml; there is no flag to point it at another config file. To target multiple environments, use shell aliases with the --manager and --token overrides:
# ~/.bashrc or ~/.zshrc
alias pfy-dev='pfy --manager https://dev.manager.example.com --token dev-token'
alias pfy-prod='pfy --manager https://prod.manager.example.com --token prod-token'
pfy-dev project list
pfy-prod project listwhoami β
pfy whoami is the "is my setup working" check: it runs a cheap authenticated query and reports the manager URL and whether the token is accepted.
pfy whoamiManager: https://manager.example.com
Token: validStructured output for scripts:
pfy -o json whoami{
"manager_url": "https://manager.example.com",
"token_valid": true
}logout β
pfy logout removes the stored token from ~/.pfy/config.yaml (the file itself, and the saved manager URL, remain):
pfy logoutLogged out: removed the stored token from ~/.pfy/config.yamlRunning it again is harmless:
Already logged out (no stored token in ~/.pfy/config.yaml)The PAT itself stays valid until revoked in the Manager (or until it expires); revoke it from the PAT list if the machine is being retired.
Switching Accounts β
Run pfy login again with a different --server (and, for the headless path, a different token); it overwrites the previous configuration. Revoke the old PAT from the Manager's PAT list if it is no longer needed.
CI/CD Authentication β
Using Environment Variables β
In CI/CD pipelines, use environment variables instead of login:
# Set environment variables
export PFY_MANAGER_URL="https://manager.example.com"
export PFY_MANAGER_TOKEN="${CI_PFY_TOKEN}"
# Commands now authenticate automatically
pfy project list
pfy application list --project my-projectPFY_MANAGER_TOKEN is also accepted by login itself as a token source: with it set, pfy login --server <url> takes the headless path and saves that token (after verification).
GitHub Actions β
- name: List applications
run: pfy application list --project my-project
env:
PFY_MANAGER_URL: ${{ secrets.PFY_MANAGER_URL }}
PFY_MANAGER_TOKEN: ${{ secrets.PFY_MANAGER_TOKEN }}GitLab CI β
rollout:
script:
- pfy application enable --app $APP_SLUG --tenant $TENANT_SLUG --project $PROJECT_SLUG
variables:
PFY_MANAGER_URL: $PFY_MANAGER_URL
PFY_MANAGER_TOKEN: $PFY_MANAGER_TOKENUnder --ci (or with NO_BROWSER set) the browser flow never launches a browser; it prints the URL and waits. CI jobs should always provide a token instead.
Troubleshooting β
Authentication Failed β
If commands fail with authentication errors:
- Run
pfy whoami; check expiry and revocation in the Manager's PAT list - Check the server URL: it must be reachable, include the protocol (https://), and be the proxied entrypoint (a URL pointing at the Manager directly is rejected regardless of the token)
- Log in again:
pfy login --server <url>
Connection Refused β
If the Manager is unreachable:
# Test connectivity (GraphQL endpoint)
curl https://manager.example.com/query
# Check TLS certificate (if issues)
pfy --insecure-skip-tls-verify login --server https://manager.example.com --token-stdin--insecure-skip-tls-verify applies to both paths: the token verification round-trip and the browser (device) flow's calls to the Manager.
Permission Denied β
If config file can't be written:
# Check directory permissions
ls -la ~/.pfy/
# Create directory if missing
mkdir -p ~/.pfy
chmod 700 ~/.pfy
# Retry login
pfy login --server <url>Security Considerations β
Token Storage β
- Tokens stored in plaintext in
~/.pfy/config.yaml - File permissions restrict access to owner only
- The browser flow's keypair and session live in RAM only; only the decrypted PAT is persisted
Network Security β
- Always use HTTPS for production
- Avoid
--insecure-skip-tls-verifyin production - Verify TLS certificates
Access Control β
- Separate tokens for dev/prod
- Regular token rotation
- Audit token usage via the PAT list's "last used"
See Also β
- Configuration - Configuration details
- Quick Start - Getting started guide
- project - Manage projects