Configuration
Four config files, owned by different processes and frequently confused with each other. Nothing merges across them.
| File | Read by | Purpose |
|---|---|---|
.dreamrc | CLI + Python SDK, producer-side | Named compute modes (RunConfigs) and provider connections. |
udf-daemon.toml | nymph, on the host | Daemon bootstrap — server URL, runners, workdir, poll tunables, identity. |
.lakeshore / .lakeshore.local | CLI, lakeshore daemon launch only | Per-project launch defaults. |
lakeshore.yaml | CLI, up / down / ps / status / logs | Declarative queue + daemon-group topology. |
.dreamrc
YAML. Declares named modes (RunConfigs) and providers — the
account/cloud connections the control plane launches workers through.
Resolution order
The CLI resolver returns the first hit:
--dreamrc <path>→ sourceexplicit$DREAMRC→env./.dreamrc, walking up from cwd →workspace$HOME/.dreamrc→home- Server pull (needs saved auth or
LAKESHORE_URL) →server - On-disk cache from a previous pull →
cache - Nothing →
none(an emptyDreamRc)
Inspect with lakeshore config show (the resolved config as YAML),
lakeshore config refresh (force a server pull and update the cache), or
lakeshore config source (one word: env, workspace, home,
server, cache, none).
The server pull composes a DreamRc from
GET /v1/namespaces/<ns>/providers and GET /v1/namespaces/<ns>/modes.
A server mode literally named default is promoted to the root
RunConfig. Providers whose launcher the CLI doesn't recognize are
skipped silently rather than failing the pull. The cache lives at
<cacheRoot>/<server-host-slug>/<namespace>/dreamrc.yml, where
cacheRoot is $XDG_CACHE_HOME/lakeshore or ~/.cache/lakeshore
(directories 0700, files 0600). A failed pull falls back to the cache
and prints ⚠ server unreachable, using cached config from <ts>.
The Python SDK's own loader (dls.load_config()) is deliberately
shorter — explicit path, $DREAMRC, ./.dreamrc walking up,
~/.dreamrc. It does not pull from the server.
Example
Provider entries
A provider entry must be a YAML-tagged mapping. The tag is the type
discriminator — there is no type: field.
Those five are the whole set (KNOWN_PROVIDER_TAGS). An unknown
!providers.X tag is a hard parse error. The body is the launcher
kwargs, deep-merged with the referencing mode's launcher-shaped fields at
dispatch time.
Default dispatch per launcher, overridable with an explicit dispatch:
key inside the kwargs:
| Launcher | Default dispatch |
|---|---|
SSH | direct |
SLURM | direct |
Kube | direct |
EC2 | daemon |
GCE | daemon |
Mode fields
Top-level keys: default_mode (default "local"), modes (alias:
mode), providers. Any other top-level key is parsed as a root
RunConfig. A synthetic local mode is injected when the file declares
none.
| Field | Type | Default | Notes |
|---|---|---|---|
backend | string | "local" | "local" runs in-process; "fabric" dispatches through the fabric. |
server | string | null | none | Control-plane URL override for this mode. |
tags | list[string] | [] | Selector — matched against daemon tags. |
runner | string | "docker" | "process", "docker", "gvisor", "slurm", "kube" ("subprocess" aliases "process"). |
resources | table | {} | Reservation hints (cpu, mem, gpu, …). |
image | string | null | none | OCI image. Required by the docker / gvisor runners. |
env | table | {} | Extra env vars for the invocation. |
timeout_s | int | null | none | Wall-clock cap. |
provider | string | null | none | Name of a provider entry. |
Any key not in that list lands in extras and is forwarded to the
launcher at dispatch time (instance_type, image_id, partition,
time_limit, …).
Server-stored modes
Modes also live as rows on the control plane, managed with
lakeshore modes add | list | show | update | remove. The override flag
is --field, not --kwarg:
Dotted keys nest. lakeshore modes update <name> --field … deep-merges,
so you can patch one field; --field foo=null deletes a key.
--config-file <path> loads (on add) or replaces (on update) the
whole RunConfig from YAML or JSON.
The Mode create handler defaults backend to "fabric" and runner to
"process" when unsupplied, and otherwise accepts any string. A typo
is stored and echoed back rather than rejected. Unknown config keys
round-trip through resources.__extras.
udf-daemon.toml
nymph's bootstrap config. Read once at process start and never
re-read — everything mutable arrives over the long-poll channel.
Lookup order when --config <path> is absent:
/etc/dreamlake/udf-daemon.toml$HOME/.config/dreamlake/udf-daemon.toml- Built-in defaults
$LAKESHORED_CONFIG (note the trailing D) is the env form of
--config.
The top-level Config is #[serde(deny_unknown_fields)], so an unknown
section is a hard parse error. The sub-structs are not, so an unknown
key inside a known section is silently ignored.
[server]
Exactly four keys.
| Field | Type | Default | Notes |
|---|---|---|---|
url | string | http://localhost:8080 | Control-plane base URL. |
token_file | path | null | none | Legacy bearer token, sent as Authorization: Bearer on every request. |
tls_verify | bool | true | false sets reqwest's danger_accept_invalid_certs. Local dev only. |
namespace | string | "default" | Used only for namespace-scoped routes (the payload presign endpoint). |
ServerConfig has no transport, no socket_path, and no Unix-socket
mode. Because [server] is not deny_unknown_fields, a transport =
line would be silently ignored rather than honored.
[daemon]
| Field | Type | Default | Notes |
|---|---|---|---|
machine_id | string | "auto" | "auto" resolves to the hostname on /hello. |
label | string | "" | Display name; falls back to the worker id when empty. |
tags | list[string] | [] | Merged with auto-detected host tags; config tags win on a key= collision. |
lanes | list[string] | [] | Queue memberships, sent as HelloRequest.lanes (the back-compat wire name for queues). Omitted from the wire entirely when empty, which the control plane reads as default-queue membership. |
The control plane's daemon-bootstrap renderer emits a
queues = [...] key in [daemon]. The nymph source in this checkout
has no queues field — only lanes — so that key parses without error
and is then ignored. If you are hand-writing the TOML for this build,
use lanes.
[runtime]
| Field | Type | Default | Notes |
|---|---|---|---|
workdir | path | /var/lib/dreamlake/udf | Root for per-invocation directories. See the workdir contract. |
max_invocations | int | 100 | Concurrency cap, enforced by a semaphore. 0 = unbounded. |
runners | list[string] | ["process"] | Advertised on /hello. Detected runners (slurm, kube) are appended if not already present. |
default_runner | string | "process" | Used when a run config names no runner. |
keep_alive_s | int | 300 | -1 = pool mode, never idle-exit. 0 = exit as soon as the idle clock starts. N = exit after N continuous idle seconds. |
auto_update | bool | false | Converge to the namespace's target_nymph_version. |
auto_update_check_interval_s | int | 300 | Floor on how often the daemon reacts to a version advertisement. |
Runner kinds accepted at dispatch: process, subprocess (alias for
process), docker, gvisor, slurm, kube. An unknown kind fails
the invocation with unknown runner kind: <k> — it does not fall
back to the default runner.
There is no default_image key and no [runtime.runner] section. The
image comes from run_config.image per invocation; the docker runner
fails immediately with ImageRequired when it is missing.
[poll], [shutdown], [mounts]
| Field | Default | Notes |
|---|---|---|
poll.wait_max_s | 20.0 | Requested long-poll window. The control plane hard-caps its own side at 20 s. |
poll.retry_min_s | 1.0 | Backoff floor; a successful poll resets to this. |
poll.retry_max_s | 60.0 | Backoff ceiling. Growth is min(backoff * 2, retry_max_s) with full jitter. |
shutdown.grace_s | 60 | Seconds in-flight work gets after SIGTERM before the kill token fires. |
mounts.cache_dir | /var/lib/dreamlake/cache | Mount cache root. |
mounts.cache_max_bytes | 21474836480 | 20 GiB. |
[log], [introspect], [identity]
[log]: level (default "info"), format ("human" | "json"; an
unrecognised value warns to stderr and falls back to human),
event_buffer (default 1024; 0 disables the ring buffer).
RUST_LOG overrides level; LAKESHORE_LOG_FORMAT overrides format.
[introspect]: enabled (default false), bind (default
127.0.0.1), port (default 9876; 0 = OS-assigned). The endpoint is
unauthenticated, so a non-loopback bind is refused at startup — the
failure is logged as a warning and introspection is skipped, not fatal.
[identity]: enabled (default false), key_file (default
/etc/dreamlake/daemon.key), enroll_token, enroll_token_file. The
inline token wins over the file. If enabled = true and the key cannot
be loaded, the daemon fails closed rather than silently degrading to the
bearer path.
Both are documented in full on Telemetry and introspection and Key rotation.
Daemon env vars
| Var | Equivalent |
|---|---|
LAKESHORED_CONFIG | --config |
LAKESHORE_SERVER | --server |
LAKESHORE_TOKEN | --token |
DREAMLAKE_QUEUE | --queue (default "default") |
RUST_LOG | overrides log.level |
LAKESHORE_LOG_FORMAT | overrides log.format |
A comment in config.rs mentions LAKESHORE_INTROSPECT as a way to
enable the introspection server. No code reads it. The only switch is
[introspect] enabled = true.
.lakeshore project defaults
Two files at the project root, read only by lakeshore daemon launch: .lakeshore (checked in) and .lakeshore.local (gitignored
override). Discovery walks up from cwd and stops at the filesystem root
or at any directory containing .git. The first level where either
file exists wins; .lakeshore.local layers on top with a shallow
spread — arrays replace, they do not concatenate.
Every key is optional and unknown top-level keys are ignored. lanes is
a legacy synonym for queues. bootstrap_script and setup_scripts
paths resolve relative to the config file's directory, not cwd. Full
walkthrough: Daemon lifecycle → Project defaults.
lakeshore.yaml compose
The declarative topology file for lakeshore up | down | ps | status | logs. Looked up as ./lakeshore.yaml from cwd only — there is no
upward walk — or named explicitly with -f, --file.
Requires version: 1, a project: slug matching ^[a-z0-9][a-z0-9-]*$,
and a non-empty daemons: mapping. Optional at top level: namespace,
provider, queues.
lakeshore.yaml accepts only the underscored forms — fixed,
fully_elastic, pool_with_threshold, max_count. The
lakeshore queues add --elasticity flag documents the hyphenated
forms — fixed, fully-elastic, pool-with-threshold, max-count.
Neither spelling is universal.
Daemon-group keys: queues (required, non-empty), count (required
positive integer, no default), plus optional provider, instance_type,
image_id, runner, keep_alive_s, setup[], startup, python,
mounts[]. Any other key is a hard error listing the allowed set. Full
reference: Compose.
See also
- Architecture — how modes, providers, queues, and daemons fit together.
- Auth and secrets —
$secretreferences inside provider kwargs. - Daemons — the host-side runtime.
- Daemon protocol — what the daemon sends and receives.
- Function protocol — the invocation wire format.