Living design doc. Tracks the plan to reduce server-side collections.
Current state
The control plane stores 17 Mongo collections. Several are pure config
metadata that could live in the client's .dreamrc or lakeshore.yaml
and be upserted (or sent inline) on each launch. Storing them
server-side adds CRUD surface, migration burden, and forces users to
run lakeshore <resource> add before they can do anything.
Classification
| Collection | Verdict | Reason |
|---|---|---|
| Namespace | Keep | Multi-tenancy root, auto-created |
| Invocation | Keep | Job state machine, server-authoritative |
| Worker | Keep | Daemon registration, heartbeat, scheduling |
| Queue | Keep | Scheduling state, worker membership, elasticity |
| Function | Keep | Content-addressed dedup, dashboard inspection |
| Event | Keep | Audit log, change feed for dashboard |
| ExecJob | Keep | Ad-hoc command lifecycle |
| ElasticityEvent | Keep | Autoscaler audit trail |
| QueueServer | Keep | Dispatch tier hierarchy |
| Token | Keep | Auth, per-user bearer tokens |
| NymphRelease | Keep | OTA binary store |
| Secret | Keep | Server-side encryption, daemon access without shipping plaintext |
| Provider | Keep | Launch path needs it; holds $secret refs |
| Storage | Keep | Server-side presign + STS credential vending |
| Mode | Remove | Pure config — inline in .dreamrc or lakeshore.yaml, sent with each invocation as runConfig |
| Mount | Remove | Pure config metadata — declare inline in compose spec, upsert on lakeshore up |
| Tunnel | Inline into Provider | Only used as a Provider attachment — store as a nested tunnel: block inside the Provider's config |
Changes
1. Tunnel → inline in Provider config
Today:
After:
The tunnel config moves into Provider.config.tunnel. The Tunnel
collection and tunnelId FK are removed. The launch path reads
config.tunnel instead of joining.
CLI change: --tunnel <name> on providers add/update becomes
--kwarg tunnel.kind=wireguard --kwarg tunnel.interface.address=...
or --kwarg tunnel.$secret=wg-key. Same $secret resolution, no
separate CRUD.
Migration: one-time script that reads each Provider's linked
Tunnel row, copies { kind, config } into Provider.config.tunnel,
and drops the FK.
2. Mode → client-side only
Modes are execution environment templates (backend, runner, image,
resources, env). Today they're stored server-side and referenced by
name in the Python SDK's @udf("mode_name") decorator.
The actual consumer is the invocation's runConfig — which is
already a full inline copy of the mode's fields, merged with
caller overrides. The Mode row is only used at submit time to
resolve mode_name → runConfig fields.
Change: move mode resolution to the client. The Python SDK
reads .dreamrc or lakeshore.yaml, resolves the mode locally,
and sends the full runConfig inline with the invocation. The
server never needs to know about modes.
CLI: lakeshore modes add/list/show/update/remove can be
dropped. .dreamrc modes: block is the source of truth (already
exists and works).
Dashboard: modes don't appear in the current dashboard. No change needed.
3. Mount → declare in compose, upsert on launch
Mounts are filesystem attach metadata (NFS, S3, bind, configmap). The daemon needs to know about them at launch time, but the server doesn't use them between launches.
Change: declare mounts inline in lakeshore.yaml:
The compose reconciler sends the mount specs as part of the launch
body. The daemon receives them via the /hello response or setup
commands. No server-side Mount collection needed.
Existing mount CRUD can stay as an optional server-side registry for operators who prefer centralized config, but the compose path doesn't require it.
What stays
After simplification, the server-side collections are:
- Namespace — identity
- Token — auth
- Secret — encrypted credentials
- Provider — compute backends (with tunnel inline)
- Storage — S3 bucket/prefix metadata + presign/credentials
- Queue — scheduling
- Worker — daemon fleet
- Function — content-addressed UDF registry
- Invocation — job state machine
- ExecJob — ad-hoc commands
- Event — audit log
- ElasticityEvent — scaler audit
- QueueServer — dispatch hierarchy
- NymphRelease — OTA binaries
Down from 17 to 14. Mode, Mount, and Tunnel (as a separate collection) are removed.
Migration order
- Tunnel → inline (smallest blast radius, Tunnel has fewer than 10 rows in prod)
- Mode → client-side (Python SDK change, no server migration)
- Mount → compose inline (optional — keep the CRUD as fallback)