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.
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/JavaScriptquonfig-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.jsquonfig-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 verifyhook - The workspace directory layout:
configs/,feature-flags/,segments/,log-levels/,schemas/ - A
quonfig.jsondescribing the workspace and its environments - A managed
README.md,CLAUDE.md, andAGENTS.mdthat 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 runqfg pushagainst 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
keyfield - 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 toQUONFIG_DIRenv 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, thenQUONFIG_DIR)--environment <slug>- Environment to evaluate. The flag wins; otherwiseQUONFIG_ENVIRONMENT; otherwisedevelopment.qfg servehosts a service, so it reads the variable the same way an SDK does (see Environment Variables).--port <n>- TCP port (default6580)--host <addr>- Bind address (default127.0.0.1; non-loopback requires--allow-non-loopback)--frontend-sdk-key <key>- If set, requireAuthorization: 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 aquonfig.json,generateuses 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
--workspaceis given (or active credentials are set),generatemints 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 toQUONFIG_DIRenv var). When unset,generatewalks up from cwd looking forquonfig.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 ifQUONFIG_API_KEYis set withoutQUONFIG_WORKSPACE. Passing--workspaceskips the cwd auto-detect and goes straight to the API path.--targets <targets>- Specify target platforms:react-ts(default) ornode-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.tsfiles) 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
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 overridenever readsQUONFIG_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 / canary | qfg set-rollout |
| Set one value for EVERYONE, deleting that environment's targeting rules | qfg set-default --replace-targeting |
| Target by user email, plan, segment, or any custom property | Edit 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:
QUONFIG_WORKSPACE, if set (<org>/<workspace>, or the workspace UUID).- The
workspacepin in<dir>/quonfig.json, if the directory has one. - 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)--secretimplies--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 aqfg loginsession — 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, withQUONFIG_WORKSPACEor--workspace.QUONFIG_WORKSPACE- The workspace to act on whenQUONFIG_API_KEYis 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 anorg/workspaceslug fails withNo workspace matching ... Available: (none). Find the UUID withGET /v1/whoamiusing the same key (it returnsworkspaceId), or read it from the app's URL — every workspace page is/workspaces/<uuid>/....QUONFIG_API_URL_OVERRIDE- Override the default API URLQUONFIG_DIR- Default local directory forpullandgenerate(avoids repeating--dir)QUONFIG_ENVIRONMENT- Read only by the two commands that run as a service:qfg serve(flag, then the variable, thendevelopment) andqfg run. Every operator command that reads or writes a workspace (get,set-default,set-rollout,override,log-level, ...) ignores it and takes--environment(or--envforoverride) 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 useNO_COLOR- Disable colored output