Files
2026-09-18 14:14:56 +02:00

3.9 KiB

status, lastUpdated, applyTo, relatedDecisions
status lastUpdated applyTo relatedDecisions
Active 2026-09-18
Fuchs/Program.cs
Fuchs/appsettings*.json
OCORE/OCORE/Secrets/**
OCORE_web/OCORE_web/Secrets/**
0031-shared-keyvault-dpapi-secret-management.md

Secret management (Key Vault + DPAPI)

Summary

Fuchs loads its secrets (connection strings, API keys, mailer tokens, ...) from the shared Azure Key Vault pcwkeys at startup, with a DPAPI-encrypted local cache as an offline fallback. The mechanism is shared, host-type-agnostic infrastructure owned by the OCORE/OCORE_web submodules (0031); this doc covers how Fuchs specifically wires into it and where to make changes. The full mechanism reference (naming/encoding rules, DI wiring, cache format) is maintained independently in OCORE_web/.github_instructions/secretsmanagement.instructions.md.

How it works

  1. Fuchs/Program.cs calls builder.AddSecretManagement() (OCORE_web.Secrets.SecretManagementWebExtensions) as the first step in ConfigureServices, before FuchsOcmsIntranet.Initialize(configuration) or anything else reads IConfiguration.
  2. That call, backed by OCORE.Secrets.Extensions.SecretManagementExtensions, registers two configuration sources in order: the local DPAPI cache (secrets.cache, offline fallback) first, then the live Azure Key Vault (overrides the cache when reachable) using PrefixKeyVaultSecretManager to filter to secrets named fuchs--... and map them onto IConfiguration keys.
  3. KeyVaultSyncService (a registered BackgroundService) periodically re-pulls every key listed in Fuchs:SecretManagement:ManagedSecretKeys from Key Vault, re-writes the DPAPI cache, and calls IConfigurationRoot.Reload() so a running process picks up rotated secrets without a restart.
  4. A secret whose name is only known at runtime (not a fixed IConfiguration key) — e.g. resolved from a DB job record — is instead fetched directly via OCORE.Secrets.Extensions.SecretsOnDemandLoader, bypassing ManagedSecretKeys and the periodic sync entirely.
  5. In Development, appsettings.Development.json (git-ignored) can override any of these values locally, bypassing Key Vault for that key; AssembleConnectionStrings (Fuchs/Program.cs) additionally prefers _Dev-suffixed credential keys so a reachable Key Vault can never hand a dev machine production DB credentials.

Key files

  • Fuchs/Program.cs — builder.AddSecretManagement() call site and AssembleConnectionStrings.
  • Fuchs/appsettings.json — SecretManagement block (VaultUri, AppName: "fuchs", CacheFilePath, SyncIntervalHours, ManagedSecretKeys) plus "MANAGED_BY_KEYVAULT" placeholders for every managed key.
  • OCORE/Secrets/Extensions/SecretManagementExtensions.cs — shared Key Vault + DPAPI wiring and DI registration, used by every host type.
  • OCORE/Secrets/Extensions/SecretsOnDemandLoader.cs — direct, cached, runtime-name secret lookup outside the ManagedSecretKeys sync path.
  • OCORE/Secrets/Sync/KeyVaultSyncService.cs — periodic re-sync + IConfigurationRoot.Reload().
  • OCORE/Secrets/Cache/DpapiSecretsCache.cs — DPAPI encrypt/decrypt of the local cache file.
  • OCORE_web/Secrets/SecretManagementWebExtensions.cs — the WebApplicationBuilder.AddSecretManagement() entry point Fuchs calls.
  • OCORE_web/.github_instructions/secretsmanagement.instructions.md — canonical mechanism reference (naming/encoding rules, cache format, "adding a new secret" steps), kept current independently of this doc.
  • .github/copilot-instructions.md ("Azure Key Vault — Secret Naming") — the naming rules summarized for this repo, with the fuchs-- prefix.