Skip to main content

CLI

The Quonfig CLI provides powerful tools for creating, modifying, and getting information about your configuration and flags. It also includes TypeScript code generation capabilities to provide type-safe access to your flags and configs.

Installation​

On a system with Node version 18+, run

npm install -g @quonfig/cli

Authentication​

Authentication is only required for server-backed commands — qfg pull, qfg push, qfg create, qfg list, qfg get, qfg set-default, etc. The local-only commands — qfg init, qfg verify, qfg generate, qfg config-schema, and qfg migrate — work without an account. If you just want to use Quonfig as a local git-backed config store, you can skip this section entirely. See the Open Source / Fully Local how-to for that path.

To use the hosted features, authenticate with Quonfig:

qfg login

This will open your browser and authenticate you with Quonfig. You can also use profiles to manage multiple workspaces:

qfg login --profile my-company

For additional details about authentication, profiles, and troubleshooting, see the login command documentation below.

Usage​

See qfg --help for all the available commands. qfg [COMMAND] --help will give you detailed information about a given command.

qfg is designed for humans first, but all commands support a --json flag to output JSON instead.

Need env vars in a build or migration step?

For build steps, migrations, and one-shot scripts that read configuration from process.env before user code runs (e.g. drizzle-kit migrate, next build), use qfg run to resolve Quonfig configs into env vars and exec a child process.

Global Flags​

These flags are available for all commands:

  • --verbose - Enable verbose output for debugging and detailed logging
  • --no-color - Disable colored output (useful for CI/CD or when piping output)
  • --interactive / --no-interactive - Force interactive mode on/off (defaults to auto-detect)
  • --json - Format output as JSON instead of human-readable text
  • --profile <name> - Use a specific authentication profile (available on API commands)

Examples:

# Get detailed logging information
qfg list --verbose

# Generate clean output for scripts
qfg get my.config --no-color --json

# Force non-interactive mode for automation
qfg create my.flag --type boolean-flag --value=true --no-interactive

# Use a specific profile
qfg list --profile production

TypeScript Code Generation​

⭐ Recommended: The generate command creates TypeScript definitions and type-safe clients for your Quonfig configuration, providing autocomplete and type safety in your IDE.

generate can read from either a local copy of your workspace (paired with qfg pull) or directly from the API in one shot — see the generate reference for the in-memory mode.

Quick Start​

# Step 1: clone or update your workspace config files locally
qfg pull --dir ./my-config

# Step 2: generate TypeScript definitions from the local files
qfg generate --dir ./my-config

To avoid repeating --dir in every command, set the QUONFIG_DIR environment variable:

export QUONFIG_DIR=./my-config
qfg pull
qfg generate

This generates TypeScript files in the generated/ directory:

  • quonfig-client-types.d.ts - Type definitions for React/JavaScript
  • quonfig-client.ts - Type-safe React/JavaScript client

Filter: The react-ts target only includes configs with "Send to Client SDKs" enabled or configs of type FEATURE_FLAG. If a config is missing from generated types, check that setting in the Quonfig dashboard.

Generate for Node.js​

qfg pull --dir ./my-config
qfg generate --dir ./my-config --targets node-ts

This generates:

  • quonfig-server-types.d.ts - Type definitions for Node.js
  • quonfig-server.ts - Type-safe Node.js client

The node-ts target includes all configs regardless of client SDK settings.

Generate for Both Platforms​

qfg pull --dir ./my-config
qfg generate --dir ./my-config --targets react-ts,node-ts

CI/CD Integration​

Run both steps in your pipeline to keep generated types in sync with your workspace:

qfg pull --dir ./my-config
qfg generate --dir ./my-config --output-directory ./src/generated

In CI there is no browser to qfg login with, so authenticate with an API key instead: set QUONFIG_API_KEY and name the workspace with QUONFIG_WORKSPACE=<org>/<workspace> or --workspace. If the key is a service-account key (qf_sa_...), name the workspace by its UUID rather than org/workspace — a service account has no user-level workspace list to resolve a slug against. See Environment Variables.

Configuration File​

Create a quonfig.config.json file in your project root to customize output:

{
"outputDirectory": "src/generated",
"targets": {
"react-ts": {
"outputDirectory": "src/client/generated",
"declarationFileName": "quonfig-types.d.ts",
"clientFileName": "quonfig-client.ts"
},
"node-ts": {
"outputDirectory": "src/server/generated",
"declarationFileName": "quonfig-server-types.d.ts",
"clientFileName": "quonfig-server.ts"
}
}
}

Using Generated Types​

Once generated, import and use the type-safe clients:

// For React — the generator emits both a QuonfigTypesafeReact class
// and a ready-to-use hook bound to it.
import { useQuonfig, QuonfigTypesafeReact } from './generated/quonfig-client';

function Nav() {
const q = useQuonfig();
return q.buildDarkMode() ? <DarkNav /> : <LightNav />;
}

// For Node.js — wrap a Quonfig instance to get typed accessors.
import { Quonfig } from '@quonfig/node';
import { QuonfigTypesafeNode } from './generated/quonfig-server';

const quonfig = new Quonfig({ sdkKey: process.env.QUONFIG_BACKEND_SDK_KEY! });
await quonfig.init();
const typed = new QuonfigTypesafeNode(quonfig);

typed.buildDarkMode(); // boolean, no string keys
typed.get('build.dark-mode'); // same value, key autocompleted

Commands​

init​

qfg init scaffolds a new Quonfig workspace in the current directory, or refreshes an existing one. Fully offline — no login required.

It creates:

  • A git repo (or reuses an existing one) with a pre-commit qfg verify hook
  • The workspace directory layout: configs/, feature-flags/, segments/, log-levels/, schemas/
  • A quonfig.json describing the workspace and its environments
  • A managed README.md, CLAUDE.md, and AGENTS.md that explain the local-only workflow to humans and AI agents
  • Optionally, one sample of each config type (with --samples) so you have working files to copy from

Options:

  • --samples — drop in one example feature flag, config, and segment
  • --workspace <org/slug> — pin the workspace identity (only matters if you later run qfg push against a hosted account)
  • --dir <path> — initialize in a path other than the current directory

Examples:

# Scaffold a brand-new workspace with samples
mkdir my-config && cd my-config
qfg init --samples

# Refresh templates in an existing workspace (idempotent — safe to re-run)
qfg init

After qfg init, the next things you'll run are qfg verify (validation) and qfg generate (typed SDK client). You can then point any Quonfig SDK at this directory with datadir and you're done — see Open Source / Fully Local.

verify​

qfg verify validates every JSON file in a Quonfig workspace against the canonical schema and against cross-file rules. Fully offline.

It checks:

  • JSON syntax and schema conformance (field names, value types, operator enums)
  • Filename matches the key field
  • No duplicate keys across the workspace
  • Segment references (IN_SEG / NOT_IN_SEG) resolve to real segments
  • Schema references (schemaKey) resolve to real schemas
  • Variant-only configs only emit declared variants

Examples:

# Verify the current directory
qfg verify

# Verify a specific workspace path
qfg verify ./my-config

A pre-commit git hook is installed by qfg init to run this automatically.

generate-new-hex-key​

qfg generate-new-hex-key generates a cryptographically secure hex key suitable for encrypting config values with the --secret flag.

Example:

qfg generate-new-hex-key

This outputs a 64-character hex key like:

a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456

Use this key when creating or updating configs with encryption:

# Create an encrypted config using the generated key
qfg create my.secret --type string --value "sensitive data" --secret --secret-key-name "my-encryption-key"

Use cases:

  • Encrypting sensitive configuration values
  • Setting up encryption keys for different environments
  • Generating keys for config value protection

pull​

qfg pull clones or updates a local copy of your workspace config files. This is required before running generate, and it is also how you edit flag JSON directly for anything beyond the set-default / set-rollout shortcuts.

Options:

  • --dir <path> - Local directory to clone/update (defaults to QUONFIG_DIR env var)
  • --workspace <id> - Workspace ID (defaults to active profile)

Examples:

# Clone or update into a local directory
qfg pull --dir ./my-config

# Use QUONFIG_DIR env var instead of --dir
export QUONFIG_DIR=./my-config
qfg pull

Editing JSON for targeting rules​

Once you have a local copy, editing the JSON file is how you express rules that go beyond a catch-all default or a simple percentage rollout — for example "user.email == jdwyah@gmail.com → true, everyone else → false", segment membership, or custom properties. See the Targeting rules section below for the full workflow and a sample rule.

After editing files:

qfg verify ./my-config                           # validate JSON before pushing
git -C ./my-config add -A
git -C ./my-config commit -m "feat: target beta cohort"
git -C ./my-config push

serve​

qfg serve reads a local datadir and exposes it over the HTTP wire protocol the browser and React Native SDKs already speak. Use it when you want browser-side feature flags powered by a checked-in datadir — server-side SDKs read a datadir directly, so they don't need this.

qfg serve --datadir ./our-config --environment development
# → http://127.0.0.1:6580

Options:

  • --datadir <path> - Path to the workspace (defaults to ./our-config, then ./.quonfig, then QUONFIG_DIR)
  • --environment <slug> - Environment to evaluate. The flag wins; otherwise QUONFIG_ENVIRONMENT; otherwise development. qfg serve hosts a service, so it reads the variable the same way an SDK does (see Environment Variables).
  • --port <n> - TCP port (default 6580)
  • --host <addr> - Bind address (default 127.0.0.1; non-loopback requires --allow-non-loopback)
  • --frontend-sdk-key <key> - If set, require Authorization: Basic 1:<key> on every request
  • --cors-origin <url> - Allowed CORS origin (repeatable; default *)
  • --watch / --no-watch - Reload the envelope when the datadir changes (default on)

See Serve a Datadir to Browser SDKs for the full how-to, including SDK init snippets and production positioning.

generate​

qfg generate creates TypeScript definitions and type-safe clients for your Quonfig configuration.

It can read from either:

  • A local directory. Explicitly via --dir/QUONFIG_DIR, or auto-detected: if neither flag is set and the current directory (or an ancestor) contains a quonfig.json, generate uses that. No login required — this is the path the Open Source / Fully Local workflow uses.
  • The API. When no local workspace can be found and --workspace is given (or active credentials are set), generate mints a Gitea read token, clones the workspace into a temp dir, generates from it, and cleans up. Use this in CI or one-shot scripts where you don't want to keep a working copy on disk.

Options:

  • --dir <path> - Path to local config directory (defaults to QUONFIG_DIR env var). When unset, generate walks up from cwd looking for quonfig.json; if it finds one, it uses that. Otherwise it falls back to fetching from the API.
  • --workspace <slug-or-id> - Workspace to fetch when running without a local workspace. Required if QUONFIG_API_KEY is set without QUONFIG_WORKSPACE. Passing --workspace skips the cwd auto-detect and goes straight to the API path.
  • --targets <targets> - Specify target platforms: react-ts (default) or node-ts
  • --output-directory <dir> - Custom output directory

Examples:

# Pull-then-generate (keeps a local copy)
qfg pull --dir ./my-config
qfg generate --dir ./my-config

# One-shot generate from the API (CI / no local copy)
qfg generate --workspace acme-prod --targets node-ts

# Generate for Node.js from a local dir
qfg generate --dir ./my-config --targets node-ts

# Generate for both platforms
qfg generate --dir ./my-config --targets react-ts,node-ts

# Custom output directory
qfg generate --dir ./my-config --output-directory src/generated

This generates:

  • Type definitions (.d.ts files) with full TypeScript support
  • Type-safe client classes with camelCase method names
  • IntelliSense autocomplete for all configs and feature flags
  • Context-aware typing for better developer experience
info

For detailed setup instructions, configuration options, and usage examples, see the TypeScript Code Generation section above.

login​

qfg login authenticates you with Quonfig using OAuth. This opens your browser to complete the authentication flow and stores tokens locally for subsequent CLI commands.

Options:

  • --profile <name> - Profile name to create or update (defaults to "default")
  • --json - Format output as JSON

Example:

qfg login
qfg login --profile production

Profiles allow you to manage multiple Quonfig accounts or environments. Use the QUONFIG_PROFILE environment variable or --profile flag to specify which profile to use.

logout​

qfg logout clears all stored authentication tokens from your local machine. After logging out, you'll need to run qfg login again to authenticate.

Example:

qfg logout

This affects all profiles and is useful for security, troubleshooting authentication issues, or switching accounts.

list​

qfg list shows keys for your configurations, feature flags, log levels, schemas, and segments. By default, all types are shown, but you can filter to specific types.

Options:

  • --configs - Include only configs
  • --feature-flags - Include only feature flags
  • --log-levels - Include only log levels
  • --schemas - Include only schemas
  • --segments - Include only segments
  • --json - Format output as JSON
  • --profile <name> - Use specific profile

Examples:

qfg list
qfg list --feature-flags
qfg list --configs --feature-flags
qfg list --json

When you specify one or more type flags, only those types are included in the output.

info​

qfg info NAME shows detailed information about a specific config, feature flag, or other resource, including current values across environments and recent evaluation statistics.

Example:

qfg info my.config.name

This displays:

  • Current values per environment
  • Links to the web console
  • Usage statistics over the last 24 hours
  • Evaluation breakdowns by value

interactive​

qfg interactive (or just qfg) launches an interactive CLI mode where you can browse and manage your resources through a menu-driven interface.

Example:

qfg
qfg interactive

This provides an easier way to explore your configs and flags without remembering specific command syntax.

override​

qfg override writes a top-priority rule on a flag, keyed on the dev-only quonfig-user.email property, that fires only for SDK clients which set your email in their context. This lets you test a value locally without affecting anyone else. You must qfg login first — the user identity comes from your saved tokens.

Surface:

  • qfg override — list flags where you have an override (in the current env).
  • qfg override <key> <value> — set an override. The value's type is inferred: true/false → bool, integer → int, decimal → double, JSON-shaped ({...}/[...]) → json, anything else → string.
  • qfg override <key> --remove — remove your override on <key>.
  • qfg override --clear — remove all of your overrides in the current env.

Flags:

  • --env <env> — environment to operate in. Required. qfg override never reads QUONFIG_ENVIRONMENT (it did before CLI 0.0.74), so a variable left over from local development cannot pick the environment a write lands in.
  • --remove — remove your override on the given key.
  • --clear — remove every override you have in this env.

Examples:

qfg override                                  # list your overrides
qfg override my.flag true # bool
qfg override my.flag 42 # int
qfg override my.flag '{"a":1}' # json
qfg override my.flag --remove # remove just this one
qfg override --clear # remove all of yours
qfg override my.flag true --env=staging # operate in a specific env

Overrides on production are accepted but inert for SDK clients that don't set quonfig-user.email in their context (most production SDKs don't), so the CLI prints a soft warning when you target production.

For the full picture — how the override rule is stored, how SDKs inject your identity from qfg login, the per-SDK setup, and how to use overrides from frontends — see Personal Overrides.

Targeting rules​

Quonfig evaluates a flag by walking its rules in order, and the last rule is normally the fallback: an unconditional rule that decides what users receive when no targeting rule matches. The CLI gives you these ways to change what those rules return:

You want to…Use
Set the fallback (what users get when no targeting rule matches)qfg set-default
Run a percentage rollout / A-B test / canaryqfg set-rollout
Set one value for EVERYONE, deleting that environment's targeting rulesqfg set-default --replace-targeting
Target by user email, plan, segment, or any custom propertyEdit the JSON config directly

set-default and set-rollout change only the fallback: every targeting rule in that environment is kept unless you pass --replace-targeting.

override is deliberately NOT on this list — it only affects your SDK key. For production targeting, reach for one of the options above.

Editing JSON directly​

The local JSON config supports every targeting operator the dashboard UI supports (PROP_IS_ONE_OF, IN_SEG, PROP_MATCHES, etc.). Two commands print the operator reference:

qfg config-schema                # human-readable operator reference + worked example
qfg config-schema --json-schema # machine-readable JSON Schema (e.g. for IDE completion)

For IDE autocomplete while editing, point your editor at our-config/quonfig.schema.json (or the equivalent file in your workspace clone).

Sample rule — target a single user​

To express "user.email == jdwyah@gmail.com → true, everyone else → false" for forcerank.my.flag:

{
"key": "forcerank.my.flag",
"type": "feature_flag",
"valueType": "bool",
"default": {
"rules": [
{
"criteria": [
{
"operator": "PROP_IS_ONE_OF",
"propertyName": "user.email",
"valueToMatch": { "type": "string_list", "value": ["jdwyah@gmail.com"] }
}
],
"value": { "type": "bool", "value": true }
},
{
"criteria": [{ "operator": "ALWAYS_TRUE" }],
"value": { "type": "bool", "value": false }
}
]
},
"environments": [],
"variants": []
}

End-to-end workflow:

qfg pull --dir ./my-config                       # clone or refresh local copy
$EDITOR ./my-config/feature-flags/forcerank.my.flag.json
qfg verify ./my-config # catch typos: unknown operators, missing propertyName, etc.
git -C ./my-config add -A
git -C ./my-config commit -m "feat: target jdwyah for forcerank.my.flag"
git -C ./my-config push

qfg verify validates against the same schema the SDK uses, so a passing verify is a strong signal the rule will evaluate correctly.

profile​

qfg profile manages authentication profiles and allows you to set the default profile for CLI operations.

Example:

qfg profile

This command helps you switch between different Quonfig accounts or workspace configurations.

schema​

qfg schema NAME manages Zod schema definitions for your configs, providing type safety and validation.

Options:

  • --get - Get the current schema definition
  • --set-zod <schema> - Set a new Zod schema definition
  • --profile <name> - Use specific profile

Examples:

qfg schema my-schema --set-zod="z.object({url: z.string()})"
qfg schema my-schema --get

Schemas enable runtime validation and better TypeScript integration when using generated types.

workspace​

qfg workspace allows you to switch between different Quonfig workspaces or display your current active workspace.

Example:

qfg workspace

This helps when you have access to multiple Quonfig workspaces and need to switch contexts.

workspace create​

qfg workspace create <slug> provisions a new workspace under one of your organizations. Useful for agent-driven setups and scripted environments where you don't want to click through the UI.

Options:

  • --name <name> - Display name (defaults to the slug)
  • --org <slug-or-id> - Organization slug or UUID. Required if your account has more than one org.

Examples:

qfg workspace create my-new-workspace
qfg workspace create acme-prod --name "Acme Production" --org acme-corp

On success, prints the workspace ID, slug, Gitea repo URL, and the default environments (development, production, staging). Returns a non-zero exit code with a clear message on slug collisions, missing org membership, or auth failure.

workspace bootstrap​

qfg workspace bootstrap --dir <path> lands a local git repo, with its commit history, on a freshly created workspace. This is the move from the fully local workflow to a hosted account for anyone who wants to keep the history they built up locally; qfg push lands the same files as a single commit.

Which workspace it pushes to is resolved the same way push, pull and sync resolve theirs, in this order:

  1. QUONFIG_WORKSPACE, if set (<org>/<workspace>, or the workspace UUID).
  2. The workspace pin in <dir>/quonfig.json, if the directory has one.
  3. Your active profile (qfg workspace).

The confirmation prompt names the workspace that won, so check it before answering. If the directory is pinned to one workspace and something else selects another, bootstrap refuses and pushes nothing.

Bootstrap is for a fresh workspace only: one that holds no configs, flags, segments, log levels or schemas yet. A workspace that is already in use is refused with a pointer to qfg push --dir <path>. It never force-pushes: your history is replayed onto the workspace's own first commits, and your local branch is not moved. On success it prints the commands that point your clone at the workspace.

Options:

  • --dir <path> - The local git repo to push (defaults to the current directory)
  • --skip-validate - Skip config validation before pushing

Examples:

qfg workspace bootstrap --dir ./my-config
QUONFIG_WORKSPACE=acme/prod qfg workspace bootstrap --dir ./my-config

Bootstrap needs no git identity on the machine: on a CI runner or a fresh laptop with no user.name / user.email, the replayed commits keep your authors and are committed as quonfig migrator.

set-default​

qfg set-default NAME sets the fallback value for a flag or config in one environment — the unconditional rule at the end of that environment's rule list, i.e. what users receive when no targeting rule matches.

Targeting rules and percentage rollouts above the fallback are kept, and the command reports how many (an environment with no targeting rules gets no such sentence):

qfg set-default my.flag.name --value=true --environment=staging
# ✔ Set staging fallback to `true`. Kept 2 targeting rule(s); matched users still
# receive their targeted value. To set for everyone, add --replace-targeting.

If the environment has no rules of its own yet it inherits the flag's default rules. Those rules are copied into the environment first, then the fallback is set — so the targeting it was inheriting keeps working, the same shape the UI writes when an environment stops inheriting.

To turn a flag on or off for everyone, including users matched by targeting rules, add --replace-targeting. That collapses the environment to a single unconditional rule and deletes its targeting rules:

qfg set-default my.flag.name --value=false --environment=production --replace-targeting
# ✔ Set production to `false` for everyone. Replaced 2 targeting rule(s); previous version 9f2c1ab.

The deleted rules stay in git history — previous version names the commit to restore from. Under --json the same numbers come back as previousCommitSha + replacedTargetingRuleCount; a normal (surgical) write returns keptTargetingRuleCount. Both counts are omitted when they are 0, so a script should treat a missing field as "no targeting rules were involved", not as an error.

set-rollout​

qfg set-rollout NAME makes a percentage rollout (gradual rollout / A-B test / canary) the environment's fallback:

qfg set-rollout my.flag --environment production --true-percent 20
qfg set-rollout my.flag --environment production --weights "red:33,green:33,blue:34"

It follows the same rules as set-default: targeting rules above the fallback are kept and reported, an environment with no rules of its own is copied from default first, and --replace-targeting rolls everyone into the split by deleting that environment's targeting rules.

create​

qfg create NAME creates a new flag or config in Quonfig. You can use this to create basic values, encrypted secrets, or values provided by ENV vars.

Supported types: boolean-flag, boolean, string, double, int, string-list, json, duration, int-range, bytes, log_level

Examples:

# Basic types
qfg create my.new.string --type string --value="hello world"
qfg create my.feature --type boolean-flag --value=true
qfg create my.timeout --type int --value=30
qfg create my.price --type double --value=19.99

# Complex types
qfg create my.tags --type string-list --value="tag1,tag2,tag3"
qfg create my.config --type json --value='{"key": "value"}'
qfg create my.duration --type duration --value="30s"
qfg create my.range --type int-range --value="1-100"
qfg create my.size --type bytes --value="1GB"

# Encrypted values (requires string type)
qfg create my.secret --type string --value="sensitive data" --secret

# Environment variable sourced
qfg create my.db.url --type string --env-var=DATABASE_URL

# Confidential (non-encrypted) values
qfg create my.api.key --type string --value="key123" --confidential

# Log level (key must start with `log-level.`; value is one of TRACE/DEBUG/INFO/WARN/ERROR/FATAL)
qfg create log-level.my-app --type log_level --value=INFO

Encryption vs Confidential:

  • --secret: Encrypts the value, requires decryption key
  • --confidential: Marks value as sensitive but doesn't encrypt (useful for display purposes)
  • --secret implies --confidential, so you don't need both

Log levels: --type log_level requires the key to start with log-level. and the value to be a recognized level name (case-insensitive). --secret, --env-var, and --confidential are rejected on this type. To target individual loggers without creating one config per logger, write rules on the quonfig-sdk-logging.key context property — see the SDK docs for details.

log-level​

qfg log-level NAME is a thin alias for qfg create --type log_level NAME. It exists for discoverability — running qfg log-level --help surfaces the log-level. prefix rule and the quonfig-sdk-logging.key per-logger targeting pattern in one place.

Example:

qfg log-level log-level.my-app --value=WARN

get​

qfg get NAME evaluates a config and prints its value. Like the rest of the qfg commands that talk to your workspace, it authenticates with your qfg login session — not with an SDK key — so it is not tied to any one environment. You choose the environment per call with --environment.

In an interactive terminal, omitting --environment prompts you to pick one. Anywhere that is not an interactive terminal — a script, a CI job, a pipeline, or a $(...) command substitution — --environment is required, and leaving it off fails with 'environment' is required when interactive mode isn't available.

qfg get does not read QUONFIG_ENVIRONMENT, and neither does any other command that targets a workspace on your behalf. That variable tells a service which environment it is; the flag tells the CLI which environment to aim at. See Environment Variables.

Example:

qfg get aws.bucket --environment=production

Interpolating a value from Quonfig​

Since the CLI is a well-behaved citizen of the command line, you can use it to compose other commands.

Here's an example command to download a file from s3 using the aws cli. Quonfig values are interpolated for the aws key and bucket name.

Note the explicit --environment on each call: inside $(...) the CLI's stdout is a pipe rather than a terminal, so it cannot prompt and will not guess an environment for you.

aws s3api get-object \
--bucket $(qfg get aws.bucket --environment=production) \
--key $(qfg get aws.db.backup.filename --environment=production) \
db.tgz

As you'd expect, you can similarly use qfg in a pipeline with xargs and similar.

run​

qfg run resolves Quonfig configs into environment variables and execs a child command. Use this for build steps, migrations, and any tool that reads its config from process.env before user code runs (and therefore can't call the SDK).

Example:

qfg run --env DATABASE_URL=db.url -- drizzle-kit migrate
qfg run --env-file=.qfg.env -- next build

The -- separator between qfg run flags and the child command is required. Auth/environment is binary — set either QUONFIG_BACKEND_SDK_KEY (Mode A) or use qfg login plus --environment / QUONFIG_ENVIRONMENT (Mode B), never both. See Running commands with injected env for the full reference, package.json patterns, and the instrumentation.ts comparison.

Your workspace's main is append-only​

Every workspace is a git repo, and you can push to it with plain git as well as with qfg push. Whatever tool you use, every push to main has to fast-forward from the current tip. A force-push, deleting main, or creating main by push is rejected by the server with a message that says what to do instead. Branches other than main are yours to rewrite.

The reason: config delivery follows main forward. A rewritten main cannot be fast-forwarded by the delivery servers, and connected SDKs ignore a snapshot whose generation is not newer than the one they hold, so a force-push does not roll anything back for your users; it silently freezes what they receive. A forward commit does what a force-push was meant to do, and the next section shows how.

If a push is rejected because someone else pushed first, pull and rebase, then push again:

qfg pull && qfg push          # or: git pull --rebase origin main && git push origin main

If the rejection came from qfg workspace bootstrap on an older CLI, upgrade the CLI (npm install -g @quonfig/cli@latest); bootstrap has not force-pushed since 0.1.0.

Rolling the whole workspace back to an earlier commit​

Something landed that shouldn't have — a bad bulk edit, a script that rewrote more than you meant, a merge that went sideways. Your workspace is a git repo, so every earlier state is still there, and you go back to one by committing it forward: a new commit whose content is the old content. Your workspace's main is append-only, so nothing here rewrites history.

Find the commit you want (qfg activity feed, or git log in your local checkout), then from a local checkout of the workspace:

qfg pull                                              # start from what the workspace has right now
git restore --source=<sha> --staged --worktree -- . # put every file back to how it was at <sha>
git commit -m "restore <sha>"
qfg push

That publishes one new commit on top of the current history whose files are exactly the files from <sha> — your SDKs pick it up like any other push, and the commits in between stay in the history as a record of what happened.

Do not use git checkout <sha> -- . for this. It overlays the files that existed at <sha> and never deletes anything, so every flag or config added after <sha> survives and keeps being served — you get a mix of the two states, not the state you asked for. git restore --staged --worktree -- . removes those files too, which is what "put the workspace back how it was" actually means.

To bring back a single flag or config that was deleted, run qfg activity restore <key> instead — this recipe is for putting the whole workspace back.

Troubleshooting​

Common Issues​

Authentication Problems:

# Clear authentication and re-login
qfg logout
qfg login

# Use specific profile
qfg login --profile production

Configuration Generation Issues:

# Generate with verbose output to see detailed logs
qfg generate --verbose

# Check if config file exists and is valid JSON
cat quonfig.config.json | jq .

Network/API Issues:

# Test connectivity with verbose output
qfg list --verbose

# Check API endpoint override
echo $QUONFIG_API_URL_OVERRIDE

Environment Variables​

  • QUONFIG_API_KEY - Authenticate with an API key instead of a qfg login session — for CI and scripts. Either a personal key (qf_uk_...) or a service-account key (qf_sa_...); mint both in the app, see REST API authentication. When set, the workspace must also be named, with QUONFIG_WORKSPACE or --workspace.
  • QUONFIG_WORKSPACE - The workspace to act on when QUONFIG_API_KEY is set: <org>/<workspace> (e.g. acme/production) or the workspace's UUID. Service-account keys must use the UUID — qf_sa_ principals have no user-level workspace list, so an org/workspace slug fails with No workspace matching ... Available: (none). Find the UUID with GET /v1/whoami using the same key (it returns workspaceId), or read it from the app's URL — every workspace page is /workspaces/<uuid>/....
  • QUONFIG_API_URL_OVERRIDE - Override the default API URL
  • QUONFIG_DIR - Default local directory for pull and generate (avoids repeating --dir)
  • QUONFIG_ENVIRONMENT - Read only by the two commands that run as a service: qfg serve (flag, then the variable, then development) and qfg run. Every operator command that reads or writes a workspace (get, set-default, set-rollout, override, log-level, ...) ignores it and takes --environment (or --env for override) explicitly. The rule: the variable tells a service what it is; the flag tells the CLI what to aim at. An ambient variable must never choose where a write lands.
  • QUONFIG_PROFILE - Set default profile to use
  • NO_COLOR - Disable colored output