Release 4.110.0
4 Nov 2026 · pinned package [email protected]
openvibe-shared/config: sealed secrets, write-only secret routes, env pins and the owner settings' schema (D11 S2). Every new option is opt-in: without a KEK the store stores, reads and redacts exactly as before and reportssealed: false.- Sealed secrets. A secret-class value is stored as
v1:<kek_id>:<fingerprint>:<hint>:<bind>:…: a random 256-bit data key seals the value with AES-256-GCM, the KEK seals that data key, and both use the AADservice|namespace|key|bind.values_jsonnever holds the plaintext again;get()opens the envelope for the service, and a summary, snapshot, history or rollback shows only its marker. The KEKs come fromopts.keks({ current, keys }) orOV_CONFIG_KEKS/OV_CONFIG_KEK_CURRENT; a value that is not base64 of exactly 32 bytes refuses startup with an error naming the variable, never the key. Onlynode:cryptois used. The namespace fingerprint key is plain only when no KEK is configured; with one,config_keysholds that same key wrapped under the current KEK (AES-256-GCM, AADopenvibe-config-fingerprint|<service>|<namespace>), so a database dump has no key that reproduces a fingerprint. It is rewrapped on boot and never changes, so markers and checksums are stable across a KEK rotation and when a KEK is first enabled;reseal()recomputes each envelope's fingerprint with it. x-bind. A secret property can name another key (x-bind: "base_url"); its value joins the AAD, so changing it locks the secret until it is entered again. A copied envelope fails on another key or namespace.reseal(). Plaintext secrets and envelopes under an older KEK move to the current KEK, revision by revision, preserving fingerprints and never resurrecting a changed bind. It reports{ resealed, current, plaintext, locked, errors }and is a 409config.no_kekwithout a KEK.- Write-only routes.
PUT <base>/:namespace/secrets/:key { value, reason }andDELETE …answer a marker (fingerprint, four-characterhint,set_at,set_by,bound_to), never the value. A normalPOSTmay no longer write or unset a secret-class key (422config.secret_write_only); a marker round trip is still allowed. - Env pins and the summary.
x-env: "NAME"makes the env value effective and read-only (summary().pins, 409config.pinned_in_env).summary()now carriessealed,kek,pins,secretsandschema; a secret property'sdefault,examples,enumandconstare removed from the schema copy. The annotationstitle,description,x-group,x-env,x-danger,x-bindandx-unitare checked at creation. - Hooks.
onChange({ namespace, keys, actor, revision })runs after every successful activation (a throwing hook is logged, never fatal);requireFresh(req, { namespace, keys })onadminRoutesruns before secret writes andx-dangerkeys and answers 403config.fresh_requiredwhen false. test/config-secrets.test.jscovers the envelopes and their AAD, x-bind and copied envelopes, write-only routes, pins, the summary schema, the hooks, reseal and rotation, that a dump ofconfig_keysand the snapshots has no key that reproduces a fingerprint, that the fingerprint is stable across a k1→k2 rotation and a KEK first enabled, that no-KEK markers are unchanged, and that no value reaches a summary, snapshot, history, error, log orvalues_json; the existing test still proves the no-KEK call sites and redaction are unchanged.npm test: 129/130 files passed, 1 skipped (floating-promises, acorn not installed).
- Sealed secrets. A secret-class value is stored as