docs/content/sips/002-app-config.md
Summary: A configuration system for Spin applications.
Owner: [email protected]
Created: March 22, 2022
Updated: July 19, 2022
It is common for applications to require configuration at runtime that isn't known at build time or is too sensitive to be stored with build artifacts:
Configuration within a "parent" (component or application) consists of a number of configuration "slots":
_ ([a-z0-9_])
_ at a time and not at the end (to allow delimiting in env vars with __)[variables]
required_key = { required = true }
optional_key = { default = "default_value" }
secret_key = { required = true, secret = true }
Default values can use template strings to reference other slots.
[variables]
key1 = { required = true }
key2 = { default = "prefix-{{ key1 }}-suffix" }
In dependency configuration, templates strings can reference top-level config keys (those in [variables]), "sibling" keys within the same dependency, and "ancestor" dependant configs.
{{ top_level_key }}. prefix: {{ .sibling_key }}.s: {{ ..parent_dep_key }}, {{ ...grandparent_key }}spin.toml:
[variables]
app_root = { default = "/app" }
log_file = { default = "{{ app_root }}/log.txt" }
...
[[component.config]]
work_root = "{{ app_root }}/work" # -> "/app/work"
work_out = "{{ .work_root }}/output" # -> "/app/work/output"
[[component.dependencies.dep1.config]]
dep_root = "{{ ..work_root }}/dep" # -> "/app/work/dep"
When resolving the value of an application configuration slot, providers will be queried in-order for a value. If no value is returned by any provider, the resolution will either use the default value or fail (if the slot is "required").
Provider configuration is handled by spin at instantiation time (spin up).
Note: Provider configuration is TBD; as TOML it could look like:
[[config-provider]]
type = "json_file"
path = "config.json"
[[config-provider]]
type = "env"
prefix = "MY_APP_"
SPIN_CONFIG_key_one -> SPIN_CONFIG_KEY_ONE{"key_one": "value-one"}
spin-config.wit
// Unknown key is a runtime error
get-config: function(key: string) -> expect<string>
spin-config import, the executor can resolve keys automatically and only expose a component's own config to it.This section contains possible future features which are not fully defined here.
The above assumes only string values, but we could include some typing:
# Type can be inferred from default value:
number_key = { default = 123 }
# equivalent to:
number_key = { type = "number", default = 123 }
required_string = { type = "string", required = true }
# "bytes" would require e.g. base64 encoding in some places
encryption_key = { type = "bytes", required = true, secret = true}
For languages without component support, we could expose config as synthetic mounted files:
key1_value = File.read("/config/key1")
# Typed config; `.json` encodes values to JSON
key1_value = JSON.parse(File.read("/config/key1.json"))
# "bytes" type; `.raw` decodes from base64
encryption_key = File.read("/config/encryption_key.raw")