Skip to main content
Package: WestyxNexus.Extensions.DependencyInjection (v0.11.0+) Bind Nexus secrets to a strongly-typed options class, with live reload on rotation - and without putting them into IConfiguration.
IOptions<T>, IOptionsSnapshot<T> and IOptionsMonitor<T> are all registered.

Why not just put secrets in IConfiguration?

If you know the Azure Key Vault configuration provider you will expect exactly that, so this deserves an explanation rather than a shrug. .NET has no way to mark a configuration value as secret. Once a value is in the configuration tree:
  • the parameterless IConfigurationRoot.GetDebugView() prints it,
  • configuration.AsEnumerable() walks it,
  • the developer exception page displays it.
The GetDebugView(Func<ConfigurationDebugViewContext, string>) overload can mask values by originating provider - but only if the caller opts in, and an SDK cannot force that. On the conventional design, whether your Stripe key ends up in a log line or an error page depends on the discipline of every piece of code that touches the configuration root, including code you did not write. AddWestyxNexusSecrets<T>() removes the exposure structurally instead. IOptions<T> does not require IConfiguration - it can be satisfied by a factory - so the value never enters the configuration tree at all. You keep constructor injection, strong typing and live reload; you lose only the ability to dump it by accident.

Key mapping

Secrets are matched by prefix. The remainder of the key becomes the property path. Nexus keys are snake_case by convention and .NET properties are PascalCase. The binder handles that mapping, so api_key finds ApiKey. Matching is case-insensitive.

Live re-bind

Every registration subscribes to the client’s post-sync notification, so a rotated secret reaches IOptionsMonitor<T>.CurrentValue on the next sync - within milliseconds when the SSE stream is connected.

The escape hatch

If you genuinely need secrets reachable through IConfiguration:
Values are placed under the Secrets: prefix.
By enabling includeSecrets you accept everything described above: the values become visible to GetDebugView(), AsEnumerable() and the developer exception page.
A masking helper ships alongside it:
It redacts anything originating from a Nexus provider - but note it only helps where you call it.

What was rejected

A NexusSecretValue wrapper type whose ToString() returns "[REDACTED]" was considered and turned down. It is a breaking change to GetSecretAsync, and in the case that actually matters - a secret bound into a POCO string property - it provides no protection at all. A half-measure that creates false confidence is worse than none.

Next