Configuration reference¶
A trusted .lclcfg file is the formal configuration source. Framework defaults
are shipped as a .lclcfg resource
and combined using the upstream LCL configuration model. The downstream file
overrides these defaults, and command-line overrides win over both. This per-start parsing is not a persistent snapshot or
a file watcher. Never treat LCL expressions as a hostile-code sandbox.
Reusable configuration with using¶
The bundled universal configuration is available to native LCL using through
lcl_fastapi_defaults, an absolute path supplied by the framework:
__LCL_VERSION__: 1
# Shared configuration
using f"{lcl_fastapi_defaults}"
using "./team.lclcfg"
# Application identity and import target
app.name: "catalog"
app.version: "1.0.0"
app.target: "catalog.app:service"
# HTTP serving and shutdown
server.workers: 2
Each using path is resolved relative to its importing file. LCL expands imports
in source order; later definitions win. Dynamic using targets can use preceding
values and CLI overrides. Cycles and invalid targets retain native LCL errors.
Application filesystem settings remain anchored to the root service file, even
when their definitions came from another file. The defaults are also loaded
implicitly for compatibility; placing an explicit using later can reset earlier
definitions to universal defaults. Put it before application customizations.
The framework-owned lcl_fastapi_defaults, worker_pid and __verbose__ cannot
be redefined, including by an import namespace. __verbose__ is the Boolean
selected by lclang.cli's native --verbose option, defaulting to False. It is
available in downstream expressions, get_config() and local derived Frames.
It survives native worker spawning, crash replacement and hot reload.
business.trace_enabled: __verbose__
business.poll_seconds: 1 if __verbose__ else 30
Native import keeps reusable definitions in an isolated alias namespace:
# Independent shared definitions
import f"{__dir__}/pricing.lclcfg" as pricing
import? f"{__dir__}/optional.lclcfg" as optional
using? f"{__dir__}/local.lclcfg"
business.price: pricing.amount
An imported file resolves its own local references and does not inherit preceding definitions from the importing file. Empty imported files still reserve their alias namespace. Optional introductions skip only a directly missing file; syntax, permissions, nested required files, cycles and invalid targets retain their native errors. Existing relative targets remain supported.
The bundled app.name, app.version and app.target declarations use
NEED_OVERRIDE. These fields fail lazily when requested without a later file or
CLI definition. Business configuration may use the same marker. RUNTIME_OVERRIDE
also accepts a host value, including None, supplied through a local
use_lcl_frame() scope. Ordinary definitions
keep their native precedence over same-frame host values.
Command-line values use native lclang syntax, for example -o server.port
"LCL[9000]", -o docs.enabled "LCL[False]", or -o business.region west.
They apply to settings, renderers, business get_config, and logger resolution.
Raw expressions are transported privately to native workers and re-evaluated
against each worker's freshly loaded file, including after crash or hot reload.
Overrides are not written into configuration or runtime-state files. Run local
operations with the same state-directory overrides used at startup.
Three application fields are required: app.name, app.version, and
app.target, where the target is an importable module:attribute exposing the
LclFastAPI object. Relative runtime, log, and disk paths are anchored to the
configuration file's directory. No user-facing environment override layer exists.
The internal LCL_FASTAPI_CONFIG, LCL_FASTAPI_STATE, and
LCL_FASTAPI_OVERRIDES variables transport selected paths and raw overrides to
child workers; users should use the CLI configuration
option instead of setting those variables themselves.
| Setting | Default and contract |
|---|---|
server.host |
127.0.0.1; only this and 0.0.0.0 are accepted. |
server.port |
8080, an integer from 1 through 65535. |
server.workers |
1, bounded by the available Snowflake ID range. |
background_worker.default.enabled |
True; strict Boolean default for code-registered background workers. |
background_worker.default.auto_restart |
True; normal and exceptional returns restart after one second unless stopping. |
server.reload_dirs |
["."]; nonempty list of Python watch directories, relative to this configuration file or absolute. An explicit list replaces the default. Used by serve -o hot_reload. |
server.root_path |
Empty, or an HTTP(S) origin with hostname and optional port; no credentials, path, query, or fragment. |
server.backlog |
2048 pending connections. |
server.keep_alive_seconds |
5; zero disables idle keep-alive waiting. |
server.graceful_timeout_seconds |
30, a positive shutdown deadline in seconds. |
runtime.state_dir |
./run. |
runtime.pid_file |
LCL expression yielding <state_dir>/<app.name>.pid. |
runtime.worker_state_dir |
LCL expression yielding <state_dir>/workers. |
health.enabled |
True; disabling the endpoint does not stop runtime-state refresh. |
health.path |
/health. |
health.sample_interval_seconds |
1, a positive finite number of seconds. |
health.disk_paths |
The configuration directory and configured default log directory. |
docs.enabled |
True; controls the built-in Swagger page, schema, and assets together. |
docs.path |
/docs. |
docs.openapi_path |
/openapi.json. |
request.id_header |
X-Request-ID, a valid ASCII header name. |
snowflake.worker_id_base |
0; first allowed ID in the range 0–1023. |
snowflake.worker_id_count |
64; at least the worker count, with base + count at most 1024. |
logger.timezone |
"local"; native log timestamps with microseconds and a numeric offset. "utc" selects a Z suffix. Filenames and rotation remain UTC-based. |
There is no api.prefix. Configure prefixes on business Routers.
background_worker.<name>.enabled and .auto_restart override those individual
defaults. Remaining nested worker fields are ordinary LCL parameters. Executables
are registered only in application code. A nonempty background registry forces one
effective API worker with a warning, including when every registration is disabled.
See background workers for resources, logs and shutdown.
server.root_path is external origin metadata for code that needs a complete
public URL, not a FastAPI/ASGI mount path. It never changes routing. Read it with
get_config if the business needs it. The framework does not force that origin
into the local Swagger request target or local stop command.
nginx.server_name, nginx.listen_port, and certificate settings determine the
rendered Nginx listener independently. For example, an origin on public port 443
may map to a differently numbered Nginx listener. Deployment operators own that
mapping; the framework must not silently replace those fields with origin data.
Per-machine Snowflake ranges¶
LCL's built-in env utility is the environment integration:
snowflake.worker_id_base: int(env.get("LCL_WORKER_ID_BASE", "0"))
snowflake.worker_id_count: 64
Set LCL_WORKER_ID_BASE=0 on one machine and 64 on another, for example. The
environment value is interpreted through the formal configuration expression.
Operators must choose non-overlapping ranges; there is no cross-machine allocator.
The supported scope is one service per machine. Concurrent worker leases do not
extend the upstream generator's historical guarantees across crashes and rapid ID
reuse.
Configuration changes¶
Application code can temporarily override configuration using
use_lcl_frame. The scope changes local
get_config() lookup without editing files or changing other request scopes.
Inherited definitions retain native parent evaluation and caching.
For development, select one or more recursive Python watch roots:
# HTTP serving and shutdown
server.reload_dirs: ["./src", "../shared"]
Then run lcl-fastapi serve -o config service.lclcfg -o hot_reload.
Include "." in the list to keep watching the configuration directory along with
additional directories. Resolved duplicates are watched once. Selected directories
must exist and be accessible when hot reload starts; invalid entries fail startup.
Only .py additions, modifications, and deletions inside the selected roots trigger
reload, including nested files. Configuration files, other extensions, and resolved
paths outside those roots do not. Watch roots are fixed for the complete start;
changing this list requires stopping and starting the service.
Hot reload forces one effective worker and warns once on stderr when the configured count exceeds one. Configuration validation still applies before that override. Status and health report the effective count. The file itself is never rewritten. See runtime lifecycle for ownership and errors.
The master reads service settings at complete startup. Every worker, including a replacement after a crash, independently loads the current file. Existing workers do not reload; a replacement does not reconfigure the running master's listener or process-management settings. Editing a running service's file can therefore produce mixed worker settings. Prefer an externally managed complete restart.
Local operations first locate runtime state from the supplied configuration and then use recorded identity and launch-time listener information. If you change the runtime directory, retain a configuration that can locate the previous instance until it has been stopped.