diff --git a/reference/configuration/overview.md b/reference/configuration/overview.md index 03322169..23d9bc55 100644 --- a/reference/configuration/overview.md +++ b/reference/configuration/overview.md @@ -54,6 +54,8 @@ HTTP_PORT=9926 harper > **Note:** Component configuration cannot be set via environment variables or CLI arguments. +To set multiple values (or a whole config object) from a single environment variable, use `HARPER_CONFIG` — see [Environment Variable-Based Configuration](#environment-variable-based-configuration). + ### 3. CLI Arguments Same naming convention as environment variables, prefixed with `--`: @@ -91,7 +93,7 @@ hdb/backup/2026-05-06T14-22-08.123Z-harper-config.yaml.bak Notes: - Backups are not produced when you edit `harper-config.yaml` directly — Harper only sees that change on its next start. -- The `HARPER_DEFAULT_CONFIG` and `HARPER_SET_CONFIG` mechanisms do not produce timestamped backups; their provenance is tracked separately in `{rootPath}/backup/.harper-config-state.json` (see [State Tracking](#state-tracking)). +- The `HARPER_CONFIG`, `HARPER_DEFAULT_CONFIG`, and `HARPER_SET_CONFIG` mechanisms do not produce timestamped backups; their provenance is tracked separately in `{rootPath}/backup/.harper-config-state.json` (see [State Tracking](#state-tracking)). - Backups are not rotated. Old `.bak` files accumulate in `{rootPath}/backup/` until you remove them. - A failure to write the backup is logged at `error` level and does not block the configuration update. @@ -115,13 +117,46 @@ HDB_CONFIG=/existing/rootpath/harper-config.yaml harper -Harper provides two special environment variables for managing configuration across deployments: `HARPER_DEFAULT_CONFIG` and `HARPER_SET_CONFIG`. Both accept JSON-formatted configuration that mirrors the structure of `harper-config.yaml`. +Harper provides three special environment variables for managing configuration across deployments: `HARPER_CONFIG`, `HARPER_DEFAULT_CONFIG`, and `HARPER_SET_CONFIG`. All accept JSON-formatted configuration that mirrors the structure of `harper-config.yaml`, and all **merge** into the configuration — they set only the keys you name, leaving every other key untouched. ```bash -export HARPER_DEFAULT_CONFIG='{"http":{"port":8080},"logging":{"level":"info"}}' +# Recommended: set configuration from the environment +export HARPER_CONFIG='{"http":{"port":8080},"logging":{"level":"info"}}' + +# Specialized: defaults that yield to other sources, and forced/locked values +export HARPER_DEFAULT_CONFIG='{"logging":{"level":"info"}}' export HARPER_SET_CONFIG='{"authentication":{"enabled":true}}' ``` +`HARPER_CONFIG` is the recommended choice for "how do I set configuration from the environment?" — it applies with least-surprise merge semantics. Reach for `HARPER_DEFAULT_CONFIG` only when you specifically need overridable defaults, and `HARPER_SET_CONFIG` only when you need to force a value and lock it against drift. + +### HARPER_CONFIG + + + +The recommended way to set configuration from the environment. It applies as a **merge on top of** the existing configuration: it sets exactly the keys you name (at any depth) and leaves everything else — including sibling keys — untouched. + +- Sets only the keys it names; unspecified keys at any depth are preserved +- **Reasserts its keys on every restart** — while the variable names a key, that value wins over the config file and over manual edits (a hand-edit to a named key is reapplied on the next restart) +- Yields only to `HARPER_SET_CONFIG` +- Individual environment variables and CLI arguments (e.g. `HTTP_PORT`) still win over `HARPER_CONFIG` for the specific keys they set +- When a key is removed from the variable, its original value is restored (or the key is deleted if `HARPER_CONFIG` introduced it) + +**Example:** + +```bash +export HARPER_CONFIG='{"http":{"port":8080},"logging":{"level":"info"}}' +harper + +# http.port and logging.level are set to these values on every restart. +# http.securePort, other logging keys, and the rest of the config are untouched. + +# If an administrator edits http.port to 9000 in harper-config.yaml, +# it is reset to 8080 on the next restart (the variable still names http.port). + +# If http.port is later removed from HARPER_CONFIG, the original value is restored. +``` + ### HARPER_DEFAULT_CONFIG Provides default configuration values while respecting user modifications. Ideal for supplying sensible defaults without preventing administrators from customizing their instances. @@ -158,7 +193,7 @@ Forces configuration values that cannot be overridden by user edits. Designed fo **At runtime:** - Always overrides all other configuration sources -- Takes precedence over user edits, file values, and `HARPER_DEFAULT_CONFIG` +- Takes precedence over user edits, file values, `HARPER_CONFIG`, and `HARPER_DEFAULT_CONFIG` - When a key is removed from the variable, it is deleted from the config (not restored) **Example:** @@ -171,31 +206,57 @@ harper # overridden on the next restart. ``` -### Combining Both Variables +### Array values and the `$union` directive + + + +By default an array in any of these variables **replaces** the existing array wholesale — the same as a plain value. To instead **compose** an array, wrap it in a `$union` directive: + +```bash +export HARPER_SET_CONFIG='{"tls":{"uses":{"$union":["server","operations-api"]}}}' +``` + +`$union` guarantees the listed items are present in the target array while preserving any entries already there — the order-preserving union of (existing ∪ listed). It is: + +- **Idempotent** — reapplying the same `$union` on every restart is a no-op; it never grows duplicates +- **Non-destructive** — it only adds the items it names and never removes other entries, even under `HARPER_SET_CONFIG`'s drift handling + +This lets a platform layer guarantee required array entries (for example `tls.uses`) on every restart without clobbering entries an application adds. `$union` works in all three variables. A bare array (no `$union`) still replaces, which remains the default. + +> **Note:** Only `$`-prefixed keys that Harper recognizes as directives (currently `$union`) are treated specially. Other `$`-prefixed keys — such as a JSON Schema's `$schema` or `$ref` inside component configuration — pass through as ordinary configuration values. + +### Combining the variables ```bash +# Recommended: set configuration from the environment +export HARPER_CONFIG='{"http":{"port":8080,"cors":true},"logging":{"level":"info"}}' + # Provide sensible defaults (can be overridden by admins) -export HARPER_DEFAULT_CONFIG='{"http":{"port":8080,"cors":true},"logging":{"level":"info"}}' +export HARPER_DEFAULT_CONFIG='{"operationsApi":{"network":{"port":9925}}}' # Enforce critical settings (cannot be changed) export HARPER_SET_CONFIG='{"authentication":{"enabled":true}}' ``` +Because each variable holds a single string, two parties cannot both write the _same_ variable without one overwriting the other. The split lets them coexist: a platform layer can pin values via `HARPER_SET_CONFIG` (with `$union` for required array entries) while an application sets its own keys via `HARPER_CONFIG`, and the two merge rather than clobbering each other. + ### Configuration Precedence From highest to lowest: -1. **`HARPER_SET_CONFIG`** — Always wins -2. **User manual edits** — Detected via drift detection -3. **`HARPER_DEFAULT_CONFIG`** — Applied if no user edits detected -4. **File defaults** — Original template values +1. **`HARPER_SET_CONFIG`** — Always wins; locked against drift +2. **Individual env vars / CLI arguments** (e.g. `HTTP_PORT`) — for the specific keys they set +3. **`HARPER_CONFIG`** — Reasserted on every restart; wins over the config file and user edits +4. **User manual edits** — Detected via drift detection (relative to `HARPER_DEFAULT_CONFIG`) +5. **`HARPER_DEFAULT_CONFIG`** — Applied if no user edits detected +6. **File defaults** — Original template values ### State Tracking Harper maintains a state file at `{rootPath}/backup/.harper-config-state.json` to track the source of each configuration value. This enables: -- **Drift detection**: Identifying when users manually edit values set by `HARPER_DEFAULT_CONFIG` -- **Restoration**: Restoring original values when keys are removed from `HARPER_DEFAULT_CONFIG` +- **Drift detection**: Identifying when users manually edit values set by `HARPER_DEFAULT_CONFIG` (and, for `HARPER_CONFIG`, reasserting the configured value) +- **Restoration**: Restoring original values when keys are removed from `HARPER_CONFIG` or `HARPER_DEFAULT_CONFIG` - **Conflict resolution**: Determining which source should take precedence ### Format Reference @@ -222,7 +283,7 @@ logging: ### Important Notes -- Both variables must contain valid JSON matching the structure of `harper-config.yaml` +- All three variables must contain valid JSON matching the structure of `harper-config.yaml` - Invalid values are caught by Harper's configuration validator at startup - Changes to these variables require a Harper restart to take effect - The state file is per-instance (stored in the root path)