Changelog - 2026-07-16
Secrets & Vault Integration
New Feature EnhancementIGNIS applications previously read configuration and credentials straight from process.env. There was no secrets provider and no way to consume a vault. This release adds a pluggable Secrets provider family, a boot-time hydration phase, and restart-free rotation of dynamic database credentials.
The whole subsystem is additive and dormant by default: an application that does not override registerSecrets() uses the system-envs provider and behaves exactly as before.
Overview
- Secrets provider family (
@venizia/ignis-helpers):AbstractSecretsHelperplusSystemEnvsHelper,HashiCorpVaultHelper, andDotenvVaultHelper, selected by aSecretProvidersconst-class. - Two consumption paths: boot-time hydration into
Envs/process.env(existing code unchanged), and an injectable provider bound at@app/configfor on-demand reads. - Dynamic leases + rotation: HashiCorp Vault dynamic credentials are renewed automatically, and on rotation the PostgreSQL pool is rebuilt gracefully - no restart.
- Fail-closed: a vault failure crashes the boot in production; development falls back to
process.env.
New Features
Registering a provider
File: packages/core/src/base/applications/base.ts
Problem: Applications had no way to source secrets from a vault; every credential had to be present in process.env.
Solution: Override registerSecrets() on the application. The default returns { provider: 'system-envs' }, so nothing changes until you opt in.
import { SecretProviders, VaultAuthMethods } from '@venizia/ignis-helpers';
export class Application extends BaseApplication {
override registerSecrets() {
return {
provider: SecretProviders.HASHICORP_VAULT,
config: { endpoint, auth: { method: VaultAuthMethods.APP_ROLE, roleId, secretId } },
hydrate: [{ path: 'secret/data/myapp/config' }],
lease: [{ key: 'datasources.PostgresDataSource', path: 'database/creds/app-role' }],
};
}
}Benefits:
- Static secrets hydrate into
APP_ENV_*keys; existingprocess.envreaders are unchanged. - Dynamic database credentials rotate without a restart.
- The provider is injectable at
@app/configfor on-demand reads.
The hydration lifecycle phase
File: packages/core/src/base/applications/base.ts
A new async hydrateSecrets() phase runs between preConfigure() and registerDataSources(): it resolves the provider, merges static secrets into Envs, opens dynamic leases, and binds the provider at the previously-dormant @app/config key. wireSecretRotatables() then connects each lease to its datasource after registration.
Restart-free credential rotation
File: packages/core/src/connectors/postgres/datasources/abstract.ts
The PostgreSQL datasource gains an onSecretRotated() hook that performs a graceful soft-evict: it swaps the new credentials into this.settings, rebuilds the pool via configure(), and drains the old pool with end() so in-flight transactions finish. Rotation is opt-in - it is wired only for datasources named in a lease entry.
Vault token self-renewal
File: packages/helpers/src/modules/secrets/hashicorp/hashicorp.helper.ts
HashiCorpVaultHelper renews its own Vault auth token on the same cadence and re-authenticates if the token can no longer be renewed, so AppRole and Kubernetes deployments keep working past the token TTL.
Optional Peers
The vault backends are optional peer dependencies, reached only through sub-paths and dynamic imports - importing @venizia/ignis-helpers never requires them.
| Provider | Peer | Sub-path |
|---|---|---|
| HashiCorp Vault | node-vault | @venizia/ignis-helpers/hashicorp-vault |
| Dotenv Vault | @dotenvx/dotenvx | @venizia/ignis-helpers/dotenv-vault |
Install a peer only in the application that uses that provider.
Migration
None required. This release is fully additive:
- Applications that do not override
registerSecrets()usesystem-envsand are byte-for-byte unchanged at boot. - The reserved binding keys
@app/configand@app/environmentswere previously unused. - No new dependency is forced; the vault peers are optional and installed only when their provider is used.