Configuration
The Gestalt config file is the platform’s control plane. gestaltd does not have a large imperative setup process; instead, it reads one YAML document and derives the running system from it.
Config Discovery
When you do not pass --config, gestaltd resolves the config path in this order:
GESTALT_CONFIG./config.yaml~/.gestalt/config.yaml/etc/gestalt/config.yaml
The default command gestaltd has one extra behavior: if no config exists anywhere in that search path, it generates ~/.gestalt/config.yaml and starts with a local-only configuration that uses auth.provider: none and SQLite.
What Happens During Load
Loading a config is more than YAML parsing:
${ENV_VAR}placeholders are expanded first.- YAML is decoded with known-field checking.
- Defaults are applied.
- Connection auth is validated against surface references.
- Relative paths are resolved against the directory that contains the config file.
- Validation runs.
- During bootstrap,
secret://...values are resolved through the configured secret manager.
secret://... resolution is intentionally later than basic config parsing. That keeps the config loader simple and lets the secret manager itself be configured normally.
The Top-Level Feature Sets
This is the complete top-level shape of the config:
auth: {}
datastore: {}
secrets: {}
telemetry: {}
providers: {}
bindings: {}
server: {}
egress: {}
ui: {}Each block controls a distinct part of the runtime:
| Block | What it defines |
|---|---|
server | Port, base URL, encryption key, and API token TTL. |
auth | How users authenticate to Gestalt itself. |
datastore | Where Gestalt stores users, tokens, and related runtime state. |
secrets | How secret://... values are resolved. |
telemetry | Observability: traces, metrics, and structured logs. |
providers | The provider graph users call. |
bindings | Additional inbound surfaces. |
egress | Outbound allow or deny policy plus credential grants. |
ui | Optional custom web UI plugin. |
Defaults And Required Fields
The loader applies a small number of defaults:
server.portdefaults to8080secrets.providerdefaults toenvtelemetry.providerdefaults tostdout
Some fields are effectively mandatory:
auth.providerdatastore.providerserver.encryption_key
server.encryption_key is the root secret for the deployment. Gestalt derives other cryptographic material from it, including session-related secrets for some auth providers.
Prepared State: init, validate, And serve --locked
Gestalt supports both mutable startup and deterministic startup.
Mutable Startup
gestaltd or gestaltd serve without --locked may mutate local state at startup:
- remote REST or GraphQL API sources can be fetched and compiled
- packaged plugins can be prepared locally
- missing or stale lock state can be regenerated
This is convenient for development.
Deterministic Startup
gestaltd init --config PATH resolves remote dependencies and writes lock state next to the config:
gestalt.lock.json.gestalt/providers/*.json.gestalt/plugins/...
After that, gestaltd serve --locked --config PATH starts only if the prepared state exactly matches the config.
Validation
gestaltd validate --config PATH is non-mutating and expects existing lock state. Init first, then validate:
gestaltd init --config ./config.yaml
gestaltd validate --config ./config.yamlRelative Paths
Paths in config are resolved relative to the config file directory, not the process working directory. This matters for:
providers.<name>.icon_fileproviders.<name>.from.command- local
providers.<name>.from.packagepaths
That makes configs portable across local runs, Docker mounts, and Kubernetes volumes.
The Smallest Explicit Config
server:
port: 8080
base_url: http://localhost:8080
encryption_key: change-me
auth:
provider: none
datastore:
provider: sqlite
config:
path: ./gestalt.db
providers: {}That is enough to boot a real server. Everything else is additive.