Skip to content

Getting started with the CLI

This page covers the tool itself: how to install it, where it keeps your credentials, how you sign in, and how to read what it prints. For a first installation through to a deployed application, see Get Flui running.

Install

The CLI is published on npm as @flui-cloud/cli and needs Node.js 18 or later. The command it installs is flui.

Terminal window
npm install -g @flui-cloud/cli # or pnpm add -g, yarn global add
flui --version

flui update replaces the global install with the latest published version. It asks npm for the latest version, tells you what it found, and asks before installing anything.

Two version numbers matter. flui --version is the CLI’s own version. flui version also prints the platform release this CLI pins: the component versions a new installation would be built from. When an install behaves strangely, check the second number first.

flui --help lists the topics. There are more than two hundred commands, grouped into topics such as app, env, iam, scaling and backup, and every one answers --help with its own flags and examples. The topic map is the quickest way in.

The vault comes first

The CLI stores sensitive data: provider tokens, and the API key that acts as your account. They are encrypted, and a key kept in the same directory as the data it protects would offer no real protection. So the key is derived from a passphrase you know and the machine does not keep. That is the vault, and you set it up before you store any credential.

Terminal window
flui vault init # once per machine
flui vault unlock # once per session
flui vault status # open? and for how long?
flui vault lock # close it now

flui vault init asks for a passphrase and suggests a five-word one you can accept with Enter. The passphrase is not stored anywhere and cannot be reset, so it has to be one you can write down and reproduce. Losing it means losing every credential this machine has sealed. For the same reason, init refuses to overwrite a vault that already exists.

There is one vault per machine, with its header at ~/.flui/vault.json, but not one key. Each profile gets its own key, derived from your passphrase and the profile’s name. A command working in one profile cannot open another profile’s stored credentials, even though both were sealed under the same passphrase.

After unlock, a small background helper holds the key for the session and hands it to commands that ask for it. Nothing is written to disk. The helper closes after thirty minutes of inactivity, and after eight hours regardless of activity.

A locked vault is not an error in itself. Most commands only read a preference or an API address, which are plain text, and never ask for the key. Only a command that opens a stored credential stops. It says the vault is closed and names flui vault unlock, instead of reporting a decryption failure that would send you looking for corrupt data.

If you are upgrading from a CLI that predates the vault, nothing is lost. A profile still holding the old key file is read as before, and the first flui vault unlock seals it again under the vault and removes the old file.

For scripted setup, flui vault init and flui config set both take --stdin and read the value from standard input:

Terminal window
printf 'my passphrase' | flui vault init --stdin
printf '%s' "$TOKEN" | flui config set hetzner --stdin

A value passed as a command argument is visible to every process on the machine through the process list, which is not acceptable for a credential on a shared host.

Signing in

Flui authenticates against the identity provider that runs inside your own installation. There is no Flui account anywhere else, so flui auth login works only once there is an installation to sign in to and the active profile knows its address.

Terminal window
flui auth login # opens a browser
flui auth login --headless # prints the URL instead
flui auth login --key-name my-laptop

The CLI opens your browser, runs a standard code-with-PKCE sign-in against that identity provider, and uses the result to mint a long-lived API key, which it saves in the active profile. Every later command uses that key without asking again. The CLI never sees a password.

By default the sign-in shows the account picker, so you confirm which user you are minting a key for. --prompt login forces a fresh authentication; --prompt '' reuses the browser session silently.

The key login mints is unscoped. It carries your full permissions, because that is what signing in means. You mint narrower keys separately, as the next section describes.

When the browser is somewhere else

The CLI waits for the redirect on http://localhost:8899, falling back through 8900, 8901, 8902 and 8910, and gives up after two minutes. It listens on the loopback address only.

On a machine with no browser, --headless prints the URL so you can open it elsewhere. The redirect still has to reach the listener on the machine running the CLI, so forward the port from wherever the browser is:

Terminal window
ssh -L 8899:localhost:8899 your-server

A key has to say what it is for

flui auth generate-api-key mints a key without a browser, for CI, a script or an agent. You must say which kind of key you want, so the widest credential the installation can issue is never what you get by leaving a flag out:

Terminal window
flui auth generate-api-key --name ci --unscoped
flui auth generate-api-key --name agent \
--scope mcp:app:read --scope mcp:obs:read

If you pass neither --scope nor --unscoped, the command stops before anything else happens (before it needs a token, a profile or a network) with a message naming the two choices. --unscoped gives the key your full permissions. --scope sets a ceiling; repeat the flag for more than one scope.

A scope you do not hold yourself is refused outright, never trimmed down to what you are allowed, because a key that comes back smaller than you asked for is hard to reason about later.

The command needs a session token from the identity provider, passed as --token <jwt> or in FLUI_SESSION_TOKEN. --expires <days> sets an expiry; leave it out for a key that does not expire.

There is no --groups flag. The readable permission groups, such as “deploy and operate applications” and “operate the infrastructure”, exist in the dashboard and the API. From the terminal you name raw scopes. If you are minting a key for an agent, Choose what your agent may do explains what each group covers and where to mint it.

A key is stored as a digest, and nothing on the installation can hand it back. The command does not print it either: it saves it into the active profile (or the one named with --profile), replacing that profile’s key, and the CLI uses it from there. Later you can list the keys that exist and revoke one:

Terminal window
flui auth api-keys # name, state, scopes
flui auth api-keys -o json
flui auth revoke-api-key <id> # stops working immediately

The context banner

One workstation can drive several installations, and the worst mistake that allows is a command that works perfectly on the wrong one.

So the commands that act on an installation as a whole print a line before their output, naming where they are about to act:

┌─ context production · api https://api.example.com/api/v1

context is the active profile. api is the installation that profile points at, or a yellow (not set), which is normal for a profile you have just created and not yet pointed at anything. Some commands add the target cluster’s provider and region on the same line.

Not every command prints it. The banner appears on the families whose reach is the installation itself: env, backup, migrate, dns, catalog and a few more. Elsewhere, flui context show names the active profile.

Profiles on disk

A profile is one installation as this machine sees it: its API address, its credentials, and its local view of what that installation holds. Everything the CLI keeps lives under ~/.flui:

~/.flui/
├── context # the name of the active profile
├── vault.json # the vault header, one per machine
└── profiles/
├── default/
└── production/

Each profile directory holds:

  • config.json: your preferences in plain text, plus the sealed values (the provider credentials you stored and the API key auth login saved).
  • ca/: the trust material this profile uses to reach cluster nodes.
  • clusters.json, nodes.json, firewalls.json, vnets.json, operations.json: a local copy of what the installation last told the CLI. You can delete these; the next command that needs them fetches them again.

The directories and files are created readable by their owner only.

Switching profiles is local and instant. Nothing in any cluster notices; the CLI reads a different directory from then on.

Terminal window
flui context list # every profile, active marked *
flui context show # just the active name
flui context create staging --switch # create it and switch
flui context use default # switch back
flui context delete staging # warns only; -f deletes

default is created on first run and cannot be deleted, and neither can the active profile. Names accept letters, digits, hyphens and underscores. FLUI_PROFILE overrides the active profile for one command without switching.

Nothing operational crosses between profiles. A provider token saved in one is not visible from another, and the keys that seal them are different. A command typed in the wrong profile cannot reach another installation, because it does not hold the credentials.

If you only run one installation from this machine, the auto-created default profile is enough and you can skip this section.

Where to go next

  • Get Flui running: the first installation, step by step, including the case where you are taking over one somebody else provisioned.
  • Deploy an application: including the one-off GitHub and registry connections the first source-built deploy needs.
  • Multi-context: why one workstation driving several installations is a different problem from one installation driving several clusters.
  • Profiles and config: the layered configuration, provider credentials, and the full profile reference.
  • Authentication: the login flags and the environment variables that override the active key.
  • Connect an agent: what to do with a scoped key once you have minted one.