Profiles and config
The CLI keeps all its local state under ~/.flui/ and has three
related topics for it:
- Profiles (
contexttopic). Each profile is one isolated Flui installation on this machine, with its own API URL, credentials and local inventory of clusters, nodes and firewalls. - The vault (
vaulttopic). One passphrase per machine that seals every stored credential, in every profile. - Configuration (
configtopic). A layered resolver that decides the value of each preference and stores provider credentials, always inside the active profile.
If you only run one installation from this machine, the
auto-created default profile is enough and you can ignore the
context topic. You use config whenever you change an email, add
a provider credential, or want to know why a preference has the
value it has. You use the vault once to create it, then once per
session to open it.
Profiles
A profile is a directory ~/.flui/profiles/<name>/. The CLI creates
default on first run and remembers the active one in
~/.flui/context. Switching profile only changes local state. The
cluster is not involved; the CLI points every following command at
a different installation.
Commands
flui context list # all profiles, active one markedflui context show # name of the active profileflui context create staging # create a new profileflui context create staging --switch # create and switch in one goflui context use prod # switch to an existing profileflui context delete staging # warns, deletes nothingflui context delete staging --force # deletes itProfile names accept alphanumeric characters, hyphens, and
underscores. The default profile and the active one cannot be
deleted.
The vault
Stored credentials are encrypted. A key kept in the profile directory, next to the data it protects, would give anyone who can read the folder both halves, so the key comes from a passphrase you know and this machine does not keep. That is the vault. There is one per workstation, and it covers every profile.
flui vault init # once per machine, before storing anythingflui vault unlock # once per sessionflui vault status # is it open, and for how longflui vault lock # close it nowflui vault init creates the vault and asks for a passphrase
twice. It suggests a five-word one. The passphrase is never stored,
cannot be derived from anything and cannot be reset, so losing it
means losing every credential this machine has sealed. Write it
down when you create it. If a vault already exists, init refuses
to overwrite it, because that would make every sealed credential
unreadable.
flui vault unlock asks for the passphrase once and then leaves a
small background helper holding the key for the session. That
helper is flui vault agent. It is a command only because that is
how the CLI starts a copy of itself; it is hidden from help and you
never run it by hand. It receives the passphrase through a pipe
rather than as an argument, so the passphrase never appears in the
process list.
Each profile has its own key, derived from the passphrase and the
profile’s name. A command working in staging cannot open
prod’s stored credentials.
The session closes on its own after thirty minutes of inactivity, and after eight hours in any case. Both limits are in minutes and can be changed:
| Variable | Default |
|---|---|
FLUI_VAULT_IDLE_MINUTES | 30 |
FLUI_VAULT_MAX_MINUTES | 480 |
Any value that is not a positive whole number falls back to the
default, so a typo cannot make a session permanent. flui vault unlock --idle-minutes <n> --max-minutes <n> sets them for one
session.
flui vault status tells you whether a vault exists, whether it is
open, and how long each of those two clocks has left. --format json prints the same answer as { initialised, unlocked, location, idleRemainingMs, lifeRemainingMs }. It also warns you if other
users on this machine can reach the helper’s socket. Keeping that
socket private is the only thing that keeps them away from the key.
flui vault lock overwrites the key in memory and stops the
helper. On a machine where nothing is open, it says so and does
nothing.
A locked vault does not make every command fail. Most commands
read a preference or an API URL, both plain text, and never ask for
the passphrase. Only a command that opens a stored credential
stops. It then says the vault is locked and names
flui vault unlock, instead of reporting a decryption failure that
would send you looking for corrupt data.
An upgrade locks nobody out
A profile that still holds the older .key file keeps working
as before, so updating the CLI does not strand credentials you
already have. The first flui vault unlock re-seals those
profiles under the vault and deletes the old key file. Each entry
is rewritten under the new key before the old one is removed, so an
interrupted move loses nothing, and it reports how many secrets it
moved. A profile it cannot move is left as it is, with a warning
naming it.
Without a terminal
Both flui vault init and flui vault unlock take --stdin and
read the passphrase from standard input instead of prompting. Use
it in scripts. Without a terminal, both commands refuse to prompt
and tell you to use --stdin.
printf 'my passphrase' | flui vault init --stdinprintf 'my passphrase' | flui vault unlock --stdinSome callers have no terminal and no home directory that outlives
the run, such as a provisioning worker. For them, setting
FLUI_PROFILE_KEY to 32 bytes of base64 gives the CLI a profile
key directly, and no vault is used:
export FLUI_PROFILE_KEY="$(openssl rand -base64 32)"The key then lives only in whatever started the CLI. Anything the run sealed can no longer be opened once that caller forgets the key. A value that is present but malformed is an error; the CLI does not fall back to anything else.
Layered configuration
flui config reads and writes preferences and credentials. Every
value goes through a cascade, and the first layer that has a value
wins:
explicit CLI input > env var > ./flui.config.json > user-global config.json (active profile) > schema default./flui.config.json is an optional file in the directory you run
the command from, for preferences that belong to one project.
flui config show prints every preference with the layer its
current value came from. Use it to debug an unexpected value:
flui config showPreferences
Four preferences are declared in the schema:
| Key | Env var | Default | Purpose |
|---|---|---|---|
email | FLUI_EMAIL | (required) | Contact email for Let’s Encrypt and operational notifications. |
certificateMode | FLUI_CERTIFICATE_MODE | production | Certificate issuance mode written into the dashboard config. One of production, staging, preflight. See TLS and certificates. |
apiPath | FLUI_API_PATH | . | Path to the flui-core repo. Only relevant to Flui contributors using env export-config / dev creds. |
dashboardPath | FLUI_DASHBOARD_PATH | ../flui-dashboard | Path to the flui-dashboard repo. Same scope as apiPath. |
flui config set email you@example.comflui config set certificateMode preflightflui config get email # resolved value + sourcePreferences are not secrets and are stored in plain text, so reading or writing one never asks for the vault.
Provider credentials
flui config set <provider> stores the credentials needed to talk
to a cloud provider’s API. The command works the same way for every
provider. What differs is whether the credential is a single token
or a key and secret pair. A single token can be passed inline; a
pair goes through the interactive prompt, or a JSON object on
--stdin:
| Provider | Credential | Input |
|---|---|---|
hetzner | Single API token (apiKey) | Inline accepted: flui config set hetzner <TOKEN> |
scaleway | Access Key ID + Secret Key | Not inline: flui config set scaleway prompts for both fields, or reads them as JSON with --stdin, and live-validates them against the Scaleway IAM API before saving |
ovh | OpenStack username + password | Not inline: flui config set ovh prompts for both fields, or reads them as JSON with --stdin, and live-validates them against OVHcloud’s Keystone endpoint before saving |
OVHcloud is addressed through OpenStack, so the credential is the username and password of a Public Cloud user (OVH Manager → Public Cloud → Users & Roles), not an OVH API application key. The password is shown once, at the moment the user is created. The user must have access to exactly one OpenStack project: Flui resolves the project from the credential itself, and stops if the credential reaches none or several.
These are the keys flui config accepts as providers. What each
provider can do is in the providers
table.
Storing a credential writes to the vault, so the vault must be
unlocked. The exception is a profile that still holds the old key
file, where that file’s key is used.
A value passed as an argument is visible to every process on the
machine through the process list, so --stdin is the safer way to
pass one. For a provider with a credential pair, it is also the
only way to configure it without a terminal, as a JSON object of
its fields:
printf '%s' "$TOKEN" | flui config set hetzner --stdinprintf '{"accessKey":"…","secretKey":"…"}' | flui config set scaleway --stdinprintf '{"accessKey":"…","secretKey":"…"}' | flui config set ovh --stdinFor ovh, accessKey is the OpenStack username and secretKey the
OpenStack password. The field names are the generic ones for a
credential pair, shared with Scaleway.
Credentials are never printed back. flui config list shows which
providers have credentials stored but not their values; flui config get works on preferences only, and says so if you ask it for a
provider.
flui config set hetzner <YOUR_API_TOKEN>flui config set scaleway # interactive promptflui config list # everything in the active profileflui config list --tokens # provider credentials onlyflui config list --preferences # preferences onlyflui config list --verbose # with created/updated timesflui config test hetzner # read-only API call with the stored credentialsflui config remove hetzner # asks to confirm by retyping the keyflui config remove scaleway --force # skip the confirmationflui config test <provider> checks the stored credentials with a
real, read-only call to the provider and lists the servers,
Flui-managed volumes and networks it finds on the account. It
creates and changes nothing.
System keys
api-url is the only system-level key: the URL of the Flui API the
active profile talks to. flui env create sets it, and
flui env adopt, env sync and env status refresh it. You rarely
need to edit it by hand; when you do, use flui config set api-url <url>. flui config remove api-url clears it.
What lives in a profile, and how it is stored
One file sits above the profiles:
~/.flui/vault.json: the vault header, with the salt and a value that proves a passphrase is right. It holds no credential, and nothing in it helps anyone who does not know the passphrase.
A profile directory contains:
config.json: preferences (plain) and provider credentials (sealed under the vault)..key: the pre-vault key file. Present only on a profile that has not been moved to the vault yet; the firstflui vault unlockremoves it.ca/: the SSH CA used to authenticate to cluster nodes (sealed under the vault).clusters.json,nodes.json,firewalls.json,vnets.json,operations.json: local inventory cached from the API. You can delete them; the next command that needs them fetches them again.
Examples
# Set the machine up onceflui vault initflui vault unlock
# Create a staging workspace on Hetznerflui context create staging --switchflui config set email staging@example.comflui config set hetzner <staging-token>flui config set certificateMode stagingflui env create
# Same on Scaleway: interactive prompt for the key pairflui context create staging-scw --switchflui config set email staging@example.comflui config set scalewayflui env create --provider scaleway
# Same on OVHcloud: OpenStack username and passwordflui context create staging-ovh --switchflui config set email staging@example.comflui config set ovhflui env create --provider ovh --region GRA
# Debug where a preference is coming fromflui config showflui config get certificateMode
# Remove a provider's credentialsflui config remove scaleway --force
# Finish for the dayflui vault lockMulti-context CLI explains why the vault works this way: one passphrase over many installations, and what it does and does not defend against.