Server CLI (flapi)
flapi is the server binary. It loads a configuration file, opens an embedded DuckDB instance, and serves the REST API + MCP endpoints described by your endpoint YAMLs.
The same binary is also the packager: flapi pack folds an entire config tree (YAML + SQL templates + small data files) into a new self-contained executable, suitable for scp-style deployment.
For the upstream reference document, see docs/CLI_REFERENCE.md on the flAPI repository. For the client CLI (flapii, which talks to a running server's configuration API), see flapii CLI.
Flags
| Flag | Description | Default |
|---|---|---|
-c, --config <path> | Path to the flapi.yaml configuration file | flapi.yaml |
-p, --port <int> | Port for the web server (overrides server.http-port) | from config |
--log-level <level> | One of debug, info, warning, error | info |
--validate-config | Validate the configuration file and exit | off |
--config-service | Enable the Configuration Service API (/api/v1/_config/*) | off |
--config-service-token <token> | Bearer token required by the Configuration Service | auto-generated when missing |
--no-telemetry | Disable startup/shutdown telemetry events | off |
Environment variables
The server reads several environment variables. The general precedence rule is CLI flag > environment variable > built-in default, so any of these may also be passed via the matching CLI flag.
| Variable | Purpose |
|---|---|
FLAPI_CONFIG | Fallback for -c / --config. Lets you point at flapi.yaml purely via the environment. |
FLAPI_LOG_LEVEL | Fallback for --log-level. Invalid values exit non-zero with a single-line error — typos like FLAPI_LOG_LEVEL=DEBUG (uppercase) surface immediately rather than silently defaulting to info. |
FLAPI_CONFIG_SERVICE_TOKEN | Fallback for --config-service-token. |
FLAPI_NO_TELEMETRY | Set to 1, true, or yes to disable telemetry (same as --no-telemetry). |
SOURCE_DATE_EPOCH | Mtime stamped on every entry by flapi pack. Set to a fixed value to make pack output bit-identical across runs (see reproducible builds). |
CODESIGN_IDENTITY | macOS only. Identity passed to codesign --sign after flapi pack. Defaults to - (ad-hoc). |
Beyond these, any environment variable whose name matches a regex in template.environment-whitelist may be substituted inside flapi.yaml and SQL templates via {{env.VAR_NAME}}. That is how secrets like DB_PASSWORD reach the running server — they live in the deployment environment, never in the config tree itself.
Self-packaging subcommands
The flapi binary can fold an entire config tree into itself, producing one self-contained executable deployable via scp. Three subcommands implement the round-trip:
| Subcommand | What it does |
|---|---|
flapi pack --in <dir> --out <bin> [--allow-secrets] [--macos-append] | Bundle <dir> into a new self-contained binary at <bin> |
flapi info | Print EOCD offset, bundle size, and entry list of the running binary (exit 1 + "Bundle: none" when unbundled) |
flapi unpack --to <dir> | Dump the running binary's bundle back to a directory |
flapi pack
flapi pack --in <config-dir> --out <new-binary> [--allow-secrets] [--macos-append]
| Option | Required | Description |
|---|---|---|
--in <dir> | yes | Directory containing flapi.yaml and friends. Walked recursively. |
--out <path> | yes | Path for the bundled output binary. Overwritten if it exists. |
--allow-secrets | no | Bypass the default secret deny list. Testing only — production users must never set this. |
--macos-append | no | macOS only. Use the legacy trailing-bytes layout instead of the reserved __FLAPI/__bundle segment. The result is not notarisable — for local debugging only. |
Re-pack idempotence. If the host binary already has a trailing bundle, pack strips it from the copy (not the running binary) before writing the new one, so repeated invocations don't grow the output. Same goes for the macOS reserved-segment path: the segment is overwritten in place.
flapi info
flapi info
Prints the EOCD offset, bundle size, and entry list of the running binary. Exits non-zero with "Bundle: none (filesystem mode)" when no bundle is present (Linux/Windows binaries built without pack having been run; macOS binaries with an empty placeholder segment).
$ ./flapi-prod info
Binary: /opt/flapi/flapi-prod
Bundle offset: 70123456
Bundle size: 12534 bytes
Entries (17):
flapi.yaml (1024 bytes)
sqls/customers.yaml (412 bytes)
...
flapi unpack
flapi unpack --to <dir>
Writes every bundle entry to <dir> (creating intermediate directories as needed), preserving paths. Useful for diffing a deployed bundle against a development tree or for audit.
Worked example
# 1. Pack a config tree into a new bundled binary
flapi pack --in ./examples --out flapi-prod
# 2. Inspect what's bundled
./flapi-prod info
# 3. (Optional) extract for debugging or audit
./flapi-prod unpack --to /tmp/extracted
# 4. One-file deploy
scp flapi-prod user@host:/opt/flapi/
ssh user@host '/opt/flapi/flapi-prod' # serves bundled config from any cwd
Secret deny list
flapi pack enforces a default deny list and refuses files whose relative path matches any of:
*.envat any depth (e.g..env,config/.env)secrets/segment at any depth (e.g.secrets/db.token,nested/secrets/api.key)*.pemat any depth*.keyat any depth
Hitting any of these aborts the pack with a non-zero exit and a clear error naming the offending file. The --allow-secrets flag exists for testing (it lets the integration test suite verify the deny list itself); production users must never use it.
The rationale: a bundled binary that contains a database password is itself a secret. The whole single-artifact value disappears the moment that artifact becomes sensitive. Credentials come from environment variables at runtime — AWS_*, GOOGLE_*, AZURE_*, FLAPI_CONFIG_SERVICE_TOKEN, and {{env.VAR}} substitution inside whitelisted YAML.
Reproducible builds
Set SOURCE_DATE_EPOCH before flapi pack and the output is byte-identical across runs:
SOURCE_DATE_EPOCH=1700000000 flapi pack --in config --out a
SOURCE_DATE_EPOCH=1700000000 flapi pack --in config --out b
sha256sum a b # identical
The archive itself is deterministic by construction (entries sorted, fixed mtime, no tar-block padding), so once you fix the source binary, the output is a pure function of the input tree.
macOS notarisation
By default flapi pack on macOS writes the archive into a reserved __FLAPI/__bundle Mach-O segment that was allocated at link time (16 MiB default; knob FLAPI_RESERVED_BUNDLE_MIB at CMake configure time). The segment is overwritten in place, then flapi pack re-invokes:
codesign --force --sign "${CODESIGN_IDENTITY:--}" <out>
The result is suitable for xcrun notarytool submit. If the archive doesn't fit (e.g. you tried to bundle a 32 MiB data file into a 16 MiB segment), flapi pack exits non-zero with an error message mentioning FLAPI_RESERVED_BUNDLE_MIB so you know which knob to rebuild flAPI with.
The legacy --macos-append flag falls back to the Linux/Windows trailing-bytes layout. codesign --verify will fail on that output because the trailing data is outside what codesign considers signable; flapi pack warns to stderr and continues. Use --macos-append only for local debugging; the binary is intentionally not notarisable.
See also
- Deployment § 5️⃣ Self-Packaged Binary — when to choose this strategy vs. mounted ConfigMaps, custom Docker images, etc.
docs/CLI_REFERENCE.md §3— full upstream referencedocs/spec/DESIGN_DECISIONS.md §9— rationale: ZIP append vs. tar/7z, IFileProvider reuse,embed://scheme, reserved-segment on macOS
Examples
# Development: debug logging, examples config
./flapi -c examples/flapi.yaml --log-level debug
# Production-style run
./flapi -c /etc/flapi/flapi.yaml --log-level info
# Validate config in CI and exit non-zero on failure
./flapi --validate-config -c flapi.yaml
# Enable the runtime Configuration Service
./flapi -c flapi.yaml --config-service --config-service-token "$FLAPI_CONFIG_SERVICE_TOKEN"
# 12-factor: configure everything from the environment
export FLAPI_CONFIG=/etc/flapi/flapi.yaml
export FLAPI_LOG_LEVEL=info
export FLAPI_CONFIG_SERVICE_TOKEN=...
./flapi
Signal handling
SIGINT(Ctrl+C) triggers a graceful shutdown: stop accepting new connections, drain in-flight requests, close database connections, exit cleanly.- Other signals are not handled explicitly.
Exit codes
| Code | Meaning |
|---|---|
0 | Success (normal exit, or --validate-config passed, or flapi pack / info / unpack succeeded) |
1 | General error: invalid arguments, configuration error, startup failure, validation failure, pack refused a secret-looking file, codesign failed on reserved-segment output, or flapi info ran on a binary without a bundle |
Next Steps
- flapii CLI — the client CLI that talks to a running server's configuration service
- Configuration Service — manage configuration at runtime over HTTP
- Deployment — strategies for shipping
flapito production, including the self-packaged single-file path - Configuration Reference — every option in
flapi.yaml