mrhamburg.SecretsManager
0.0.1
dotnet add package mrhamburg.SecretsManager --version 0.0.1
NuGet\Install-Package mrhamburg.SecretsManager -Version 0.0.1
<PackageReference Include="mrhamburg.SecretsManager" Version="0.0.1" />
<PackageVersion Include="mrhamburg.SecretsManager" Version="0.0.1" />
<PackageReference Include="mrhamburg.SecretsManager" />
paket add mrhamburg.SecretsManager --version 0.0.1
#r "nuget: mrhamburg.SecretsManager, 0.0.1"
#:package mrhamburg.SecretsManager@0.0.1
#addin nuget:?package=mrhamburg.SecretsManager&version=0.0.1
#tool nuget:?package=mrhamburg.SecretsManager&version=0.0.1
<p align="center"> <img src="hero.svg" alt="SecretsManager - Unified .NET Secret Management" width="800"> </p>
A unified .NET abstraction layer over secret management backends, inspired by the External Secrets Operator for Kubernetes. Program against a single ISecretProvider interface regardless of where your secrets live.
Features
- Unified API — Get, put, delete, version, and check secrets through one interface
- Multiple backends — Filesystem, Azure Key Vault, Scaleway, AWS, Google Cloud, Oracle Vault, IBM Cloud, PostgreSQL, OVH, Passbolt, Aliyun KMS, Tencent Cloud, Infisical, HashiCorp Vault, CyberArk Conjur
- Flexible configuration — Fluent API, environment variables, or YAML (ESO-style)
- Secret versioning — First-class support for secret version history across all providers
- JSON property extraction — Retrieve nested values from JSON secrets via dot-path queries
- Zero unnecessary dependencies — Each provider is a separate package; only pull in what you use
NuGet package prefix: The
SecretsManagerpackage ID is already taken on nuget.org by another publisher, so every package in this project is published asmrhamburg.SecretsManager.*. The C# namespaces remainSecretsManager.*.
Providers
| Provider | Package | Description |
|---|---|---|
| Core | mrhamburg.SecretsManager |
Shared abstractions, builder, and runtime model types |
| FileSystem | mrhamburg.SecretsManager.FileSystem |
Local encrypted file storage (AES-256-GCM) |
| Azure Key Vault | mrhamburg.SecretsManager.AzureKeyVault |
Azure Key Vault via official SDK |
| Scaleway | mrhamburg.SecretsManager.Scaleway |
Scaleway Secret Manager via REST API |
| IBM Cloud Secrets Manager | mrhamburg.SecretsManager.IBMCloudSecretsManager |
IBM Cloud Secrets Manager via REST API |
| PostgreSQL | mrhamburg.SecretsManager.PostgreSql |
PostgreSQL-backed encrypted storage with versioning |
| OVH | mrhamburg.SecretsManager.OVH |
OVH Secret Manager / OKMS via REST API |
| AWS Secrets Manager | mrhamburg.SecretsManager.AwsSecretsManager |
AWS Secrets Manager via AWS SDK |
| Oracle Vault | mrhamburg.SecretsManager.OracleVault |
Oracle Cloud Secret Manager via OCI SDK |
| Google Cloud Secret Manager | mrhamburg.SecretsManager.GoogleSecretManager |
Google Secret Manager via GCP SDK |
| Passbolt | mrhamburg.SecretsManager.Passbolt |
Passbolt secret management via REST API |
| Aliyun KMS | mrhamburg.SecretsManager.AliyunKms |
Aliyun KMS via REST API |
| Tencent Cloud | mrhamburg.SecretsManager.TencentCloud |
Tencent Cloud Secrets Manager via REST API |
| Infisical | mrhamburg.SecretsManager.Infisical |
Infisical via the official .NET SDK |
| HashiCorp Vault | mrhamburg.SecretsManager.Vault |
HashiCorp Vault KV v2 engine via REST API |
| CyberArk Conjur | mrhamburg.SecretsManager.Conjur |
CyberArk Conjur via REST API |
Integration tests
Integration tests run each provider against a live service in CI. The table shows the
latest result on main. A grey no check runs pill means the provider's CI secrets
are not configured, so the job is skipped.
| Provider | Integration test config | Latest status on main |
|---|---|---|
| FileSystem | configs/filesystem.yaml |
|
| Azure Key Vault | configs/azure-keyvault.yaml |
|
| Scaleway | configs/scaleway.yaml |
|
| PostgreSQL | configs/postgresql.yaml |
|
| OVH | configs/ovh.yaml |
|
| AWS Secrets Manager | configs/aws-secrets-manager.yaml |
|
| Oracle Vault | configs/oracle-vault.yaml |
|
| Google Cloud Secret Manager | configs/google-secret-manager.yaml |
|
| Passbolt | configs/passbolt.yaml |
|
| Aliyun KMS | configs/aliyun-kms.yaml |
|
| IBM Cloud Secrets Manager | configs/ibm-cloud-secrets-manager.yaml |
|
| Tencent Cloud | configs/tencent-cloud.yaml |
|
| HashiCorp Vault | configs/vault.yaml |
|
| Infisical | configs/infisical.yaml |
|
| CyberArk Conjur | configs/conjur.yaml |
Passbolt is disabled in CI (if: false), so its badge stays grey until the job is enabled.
Quick Start
using SecretsManager;
using SecretsManager.FileSystem;
await using var provider = new SecretProviderBuilder()
.WithFileSystem(opts =>
{
opts.BasePath = "/secrets";
opts.EncryptionKey = "base64encodedkey==";
})
.Build();
// Store a secret
await provider.PutSecretAsync("api-key", "sk-secret-value");
// Retrieve it
var secret = await provider.GetSecretAsync("api-key");
Console.WriteLine(secret.Value);
// Check existence
if (await provider.SecretExistsAsync("api-key"))
Console.WriteLine("Secret exists!");
// List versions
var versions = await provider.GetSecretVersionsAsync("api-key");
// Delete
await provider.DeleteSecretAsync("api-key");
Layers
Layers are decorators that wrap a provider to add operational capabilities like retries, timeouts, logging, and observability. They are applied in registration order using the builder's fluent API.
Available Layers
| Layer | Extension Method | Description |
|---|---|---|
ConcurrentLimiterLayer |
.WithConcurrentLimiter() |
Limits concurrent operations with an optional acquire timeout |
RetryLayer |
.WithRetry() |
Retries failed operations with exponential backoff and jitter |
TimeoutLayer |
.WithTimeout() |
Enforces a per-operation timeout |
LoggingLayer |
.WithLogging() |
Logs start, success, failure, and retry events. Includes a Microsoft.Extensions.Logging adapter |
AuditLayer |
.WithAudit() / .WithAuditSink() |
Writes durable per-access audit records without secret values |
OtelMetricsLayer |
.WithOtelMetrics() |
Emits OpenTelemetry-compatible metrics (counters, histograms) |
OtelTraceLayer |
.WithOtelTrace() |
Emits OpenTelemetry-compatible distributed traces |
Layer Configuration
await using var provider = new SecretProviderBuilder()
.WithFileSystem(opts =>
{
opts.BasePath = "/secrets";
opts.EncryptionKey = "base64encodedkey=="; // omit only for throwaway local development
})
.WithConcurrentLimiter(opts => {
opts.MaxConcurrentRequests = 10;
opts.AcquireTimeout = TimeSpan.FromSeconds(5);
})
.WithRetry(opts => {
opts.MaxAttempts = 3;
opts.BaseDelay = TimeSpan.FromMilliseconds(100);
opts.MaxDelay = TimeSpan.FromSeconds(2);
opts.JitterFactor = 0.2;
})
.WithTimeout(opts => opts.Timeout = TimeSpan.FromSeconds(30))
.WithLogging(opts => {
opts.Logger = new LoggerSecretProviderLogger(logger); // Microsoft.Extensions.Logging
opts.OperationPrefix = "secrets";
})
.WithOtelMetrics(opts => {
opts.MeterName = "SecretsManager";
opts.MetricPrefix = "secrets.";
})
.WithOtelTrace(opts => {
opts.ActivitySourceName = "SecretsManager";
opts.RecordException = true;
})
.Build();
Layers are processed in registration order (outermost to innermost). The first registered layer wraps the provider, and each subsequent layer wraps the previous one.
Audit Trail
The audit layer writes one durable record per secret operation: operation, provider, key, version, outcome, timestamp, actor, trace id, and failure type. Records never contain secret values, and a failing sink does not change the operation's outcome by default.
var provider = new SecretProviderBuilder()
.WithFileSystem(opts => opts.BasePath = "/secrets")
.WithAudit(opts =>
{
opts.Sinks.Add(new FileSecretAuditSink("/var/log/secrets-audit.jsonl"));
})
.Build();
mrhamburg.SecretsManager.PostgreSql also ships PostgreSqlSecretAuditSink, available through
.WithPostgreSqlAuditSink(...). See Audit Trail for actor
propagation, scopes, failure modes, and the PostgreSQL table schema.
Configuration
SecretsManager supports four ways to configure a provider.
Fluent API
var provider = new SecretProviderBuilder()
.WithFileSystem(opts =>
{
opts.BasePath = "/secrets";
opts.EncryptionKey = "base64encodedkey==";
})
.Build();
Dependency Injection (IOptions<T>)
Bind options from appsettings.json through the DI container:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSecretsManager();
builder.Services.Configure<FileSystemOptions>(
builder.Configuration.GetSection("SecretsManager:FileSystem"));
// Or bind and build in one step
var provider = new SecretProviderBuilder()
.ConfigureFileSystem(builder.Configuration)
.Build();
{
"SecretsManager": {
"FileSystem": {
"BasePath": "/secrets",
"EncryptionKey": "base64encodedkey=="
}
}
}
Environment Variables
export SECRETS_PROVIDER=filesystem
export SECRETS_FILESYSTEM_PATH=/secrets
export SECRETS_FILESYSTEM_ENCRYPTION_KEY=base64encodedkey==
var provider = new SecretProviderBuilder()
.WithFileSystem()
.FromEnvironment()
.Build();
YAML (External Secrets Operator Style)
provider:
filesystem:
path: /secrets
encryption:
key: base64encodedkey==
var provider = new SecretProviderBuilder()
.WithFileSystem()
.FromYamlFile("secretstore.yaml")
.Build();
Supported Provider Docs
See the full provider setup and configuration details in the docs:
API Reference
All providers implement ISecretProvider:
| Method | Description |
|---|---|
GetSecretAsync(key, query?, ct) |
Retrieve a secret, optionally by version or JSON property path |
PutSecretAsync(key, value, metadata?, ct) |
Create or update a secret |
DeleteSecretAsync(key, ct) |
Delete a secret |
GetSecretVersionsAsync(key, ct) |
List all versions of a secret |
SecretExistsAsync(key, ct) |
Check existence without fetching the value |
ISecretProvider extends IAsyncDisposable — use await using for proper cleanup.
Building from Source
dotnet build
dotnet test
Run tests with code coverage:
dotnet test --collect:"XPlat Code Coverage"
Adding a New Provider
- Create
src/SecretsManager.<Name>/with a project reference to the coreSecretsManagerlibrary. Its package ID (mrhamburg.SecretsManager.<Name>) is derived automatically from the project name bysrc/Directory.Build.props - Implement
ISecretProvider - Implement
ISecretProviderFactoryto map flat configuration dictionaries to your options - Add a
SecretProviderBuilderExtensionsclass with.With<Name>()extension method - Add a test project under
tests/SecretsManager.<Name>.Tests/ - Register both projects in
SecretsManager.sln - Add an
integration-testCI job namedIntegration tests (<Name>), include it in theintegration-statusneedslist, and add its badge row toREADME.mdanddocs/docs/providers/overview.md
License
This project is dual-licensed under your choice of:
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.14)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.14)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.13)
- Microsoft.Extensions.Options (>= 10.0.14)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.13)
- YamlDotNet (>= 18.1.0)
NuGet packages (15)
Showing the top 5 NuGet packages that depend on mrhamburg.SecretsManager:
| Package | Downloads |
|---|---|
|
mrhamburg.SecretsManager.PostgreSql
PostgreSQL secret provider with AES-256-GCM encryption at rest for SecretsManager |
|
|
mrhamburg.SecretsManager.OVH
OVHcloud secret provider for SecretsManager |
|
|
mrhamburg.SecretsManager.Conjur
CyberArk Conjur secret provider for SecretsManager |
|
|
mrhamburg.SecretsManager.FileSystem
Filesystem-backed secret provider with AES-256-GCM encryption for SecretsManager |
|
|
mrhamburg.SecretsManager.AliyunKms
Aliyun KMS secret provider for SecretsManager |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.0.1 | 309 | 10/30/2026 |