Dosaic.Plugins.Persistence.VaultSharp
1.2.18
dotnet add package Dosaic.Plugins.Persistence.VaultSharp --version 1.2.18
NuGet\Install-Package Dosaic.Plugins.Persistence.VaultSharp -Version 1.2.18
<PackageReference Include="Dosaic.Plugins.Persistence.VaultSharp" Version="1.2.18" />
<PackageVersion Include="Dosaic.Plugins.Persistence.VaultSharp" Version="1.2.18" />
<PackageReference Include="Dosaic.Plugins.Persistence.VaultSharp" />
paket add Dosaic.Plugins.Persistence.VaultSharp --version 1.2.18
#r "nuget: Dosaic.Plugins.Persistence.VaultSharp, 1.2.18"
#:package Dosaic.Plugins.Persistence.VaultSharp@1.2.18
#addin nuget:?package=Dosaic.Plugins.Persistence.VaultSharp&version=1.2.18
#tool nuget:?package=Dosaic.Plugins.Persistence.VaultSharp&version=1.2.18
Dosaic.Plugins.Persistence.VaultSharp
Dosaic.Plugins.Persistence.VaultSharp is a Dosaic plugin that integrates HashiCorp Vault for secure secret storage. It provides a typed ISecretStorage<TBucket> abstraction backed either by a live Vault server (KV v2 + TOTP engines) or a local filesystem (useful for development and testing without a running Vault instance).
Installation
dotnet add package Dosaic.Plugins.Persistence.VaultSharp
or add as a package reference to your .csproj:
<PackageReference Include="Dosaic.Plugins.Persistence.VaultSharp" Version=""/>
Configuration
The plugin is configured via the vault section in appsettings.yml / appsettings.json. The section is bound to VaultConfiguration using the [Configuration("vault")] attribute.
Vault server mode
vault:
url: "http://localhost:8200"
token: "your-vault-token"
totpIssuer: "MyApp" # optional — default: Dosaic.Plugins.Persistence.VaultSharp
totpPeriodInSeconds: 30 # optional — default: 30
Local filesystem mode (development / testing)
When useLocalFileSystem is true, secrets are stored as JSON files under localFileSystemPath. No Vault server is required. TOTP codes are generated locally using RFC 6238 (HMAC-SHA1).
vault:
useLocalFileSystem: true
localFileSystemPath: "./nodep-vault" # optional — default: ./nodep-vault
totpPeriodInSeconds: 30 # optional — default: 30
VaultConfiguration properties
| Property | Type | Default | Description |
|---|---|---|---|
Url |
string |
"" |
Vault server base URL |
Token |
string |
"" |
Token used to authenticate with Vault |
TotpIssuer |
string |
"Dosaic.Plugins.Persistence.VaultSharp" |
Issuer label stored in the TOTP key |
TotpPeriodInSeconds |
int |
30 |
TOTP counter period in seconds |
UseLocalFileSystem |
bool |
false |
Store secrets on the local filesystem instead of Vault |
LocalFileSystemPath |
string |
"./nodep-vault" |
Root folder for local filesystem secrets |
Vault mount points
The plugin uses two fixed Vault secret engine mount points:
| Engine | Mount |
|---|---|
| KV v2 | credentials |
| TOTP | totp |
Both engines must exist in Vault before the application starts.
Registration
With Dosaic WebHost (automatic)
When using PluginWebHostBuilder, the plugin is discovered automatically and VaultConfiguration is resolved from IConfiguration. You only need to register your secret storage buckets — typically in your own plugin or startup code:
services.AddSecretStorage<SecretBucket>();
Without Dosaic WebHost (manual)
services.AddVaultSharpPlugin(new VaultConfiguration
{
Url = "http://localhost:8200",
Token = "your-vault-token"
});
services.AddSecretStorage<SecretBucket>();
For local filesystem storage without a Vault server:
var config = new VaultConfiguration { UseLocalFileSystem = true, LocalFileSystemPath = "./nodep-vault" };
services.AddLocalFileSystemSecretStorage<SecretBucket>(config);
Usage
Defining a secret bucket
Buckets are plain enums that categorise secrets stored in Vault:
public enum SecretBucket
{
ApiKeys = 0,
DatabaseCredentials = 1,
ServiceAccounts = 2
}
ISecretStorage<TBucket> interface
public interface ISecretStorage<TSecretBucket> where TSecretBucket : struct, Enum
{
Task<Secret> GetSecretAsync(SecretId<TSecretBucket> secretId, CancellationToken cancellationToken = default);
Task<SecretId<TSecretBucket>> CreateSecretAsync(TSecretBucket bucket, Secret secret, CancellationToken cancellationToken = default);
Task<SecretId<TSecretBucket>> UpdateSecretAsync(SecretId<TSecretBucket> secretId, Secret secret, CancellationToken cancellationToken = default);
Task DeleteSecretAsync(SecretId<TSecretBucket> secretId, CancellationToken cancellationToken = default);
}
Inject it via the constructor:
public class MyService(ISecretStorage<SecretBucket> secrets)
{
// ...
}
SecretId<TBucket>
SecretId<TBucket> is a read-only struct that identifies a stored secret. It encodes the bucket, type and a unique key into a URL-safe Sqid string via the Id property.
// Create a new random ID for a bucket + type combination
var id = SecretId<SecretBucket>.New(SecretBucket.DatabaseCredentials, SecretType.UsernamePassword);
// Encode / decode the opaque string representation
string opaqueId = id.Id;
var same = SecretId<SecretBucket>.FromSqid(opaqueId);
// Safe parsing (e.g. from user input)
if (SecretId<SecretBucket>.TryParse(input, out var parsed))
Console.WriteLine(parsed.Bucket); // DatabaseCredentials
Secret types
All secret types derive from the abstract record Secret.
UsernamePasswordSecret
var secret = new UsernamePasswordSecret("admin", "s3cr3t");
var id = await secrets.CreateSecretAsync(SecretBucket.DatabaseCredentials, secret);
var retrieved = (UsernamePasswordSecret)await secrets.GetSecretAsync(id);
Console.WriteLine(retrieved.Username); // admin
UsernamePasswordApiKeySecret
Extends UsernamePasswordSecret with an additional ApiKey field.
var secret = new UsernamePasswordApiKeySecret("svc-account", "p@ssw0rd", "api-key-abc123");
var id = await secrets.CreateSecretAsync(SecretBucket.ApiKeys, secret);
var retrieved = (UsernamePasswordApiKeySecret)await secrets.GetSecretAsync(id);
Console.WriteLine(retrieved.ApiKey); // api-key-abc123
UsernamePasswordTotpSecret
Stores a username, password, and a TOTP key. On create / update the Totp must contain a TotpKey with the Base32-encoded seed. On read the Totp is populated with a TotpCode containing the current OTP, its expiry time and remaining seconds.
// Write — provide the Base32 seed key
var secret = new UsernamePasswordTotpSecret(
"admin",
"p@ssw0rd",
new Totp(null, new TotpKey("JBSWY3DPEHPK3PXP"))
);
var id = await secrets.CreateSecretAsync(SecretBucket.ServiceAccounts, secret);
// Read — the live TOTP code is resolved automatically
var retrieved = (UsernamePasswordTotpSecret)await secrets.GetSecretAsync(id);
Console.WriteLine(retrieved.Totp.TotpCode.Code); // e.g. "482910"
Console.WriteLine(retrieved.Totp.TotpCode.RemainingSeconds); // seconds until code expires
Console.WriteLine(retrieved.Totp.TotpCode.ValidTillUtc); // UTC expiry timestamp
CertificateSecret
var secret = new CertificateSecret("-----BEGIN CERTIFICATE-----\n...", "optional-passphrase");
var id = await secrets.CreateSecretAsync(SecretBucket.ServiceAccounts, secret);
var retrieved = (CertificateSecret)await secrets.GetSecretAsync(id);
Console.WriteLine(retrieved.Certificate);
Updating and deleting secrets
// Update in-place — the SecretId stays the same
var updated = new UsernamePasswordSecret("admin", "new-password");
await secrets.UpdateSecretAsync(id, updated);
// Delete
await secrets.DeleteSecretAsync(id);
Local TOTP code generation (TotpCodeGenerator)
TotpCodeGenerator is a static helper that generates TOTP codes locally (RFC 6238, HMAC-SHA1) without calling Vault. It is used internally by LocalFileSystemSecretStorage but is also available for direct use:
// Generate a 6-digit TOTP code from a Base32 key
string code = TotpCodeGenerator.Generate("JBSWY3DPEHPK3PXP", periodInSeconds: 30);
// Get remaining seconds and expiry for the current period
var (remaining, validUntil) = TotpCodeGenerator.GetPeriodInfo(periodInSeconds: 30);
Features
- Typed secret buckets — secrets are namespaced by a user-defined enum; different bucket enums compile to different
ISecretStorage<T>registrations - Four secret types —
UsernamePassword,UsernamePasswordApiKey,UsernamePasswordTotp,Certificate - Opaque secret IDs — bucket, type and key are encoded into a URL-safe Sqid string for safe external exposure
- First-class TOTP support — creates/manages TOTP keys in Vault's TOTP engine; decodes live OTP codes on every read
- Local filesystem backend — drop-in replacement for local development and testing; TOTP codes generated locally via RFC 6238
- OpenTelemetry tracing — all storage operations are wrapped in
ActivitySourcespans withsecret.bucket,secret.key, andsecret.typetags - Readiness health check —
vault(Vault HTTP health endpoint) orvault-local-filesystem(directory write probe) registered automatically as a readiness check - Dosaic WebHost integration — discovered and wired automatically by the source generator; no manual bootstrap required when using
PluginWebHostBuilder
Health Checks
The plugin registers a readiness health check automatically:
| Mode | Check name | Probe |
|---|---|---|
| Vault server | vault |
GET /v1/sys/health via IVaultClient |
| Local filesystem | vault-local-filesystem |
Creates and deletes a .health-probe file in LocalFileSystemPath |
Both checks report Unhealthy on failure, preventing the application from receiving traffic until the backend is reachable.
| 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
- AspNetCore.HealthChecks.Uris (>= 9.0.0)
- Dosaic.Extensions.Sqids (>= 1.2.18)
- Dosaic.Hosting.Abstractions (>= 1.2.18)
- VaultSharp (>= 1.17.5.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.2.18 | 29 | 4/2/2026 |
| 1.2.17 | 59 | 4/1/2026 |
| 1.2.16 | 58 | 4/1/2026 |
| 1.2.15 | 75 | 3/31/2026 |
| 1.2.14 | 81 | 3/30/2026 |
| 1.2.13 | 78 | 3/26/2026 |
| 1.2.12 | 79 | 3/24/2026 |
| 1.2.11 | 92 | 3/17/2026 |
| 1.2.10 | 89 | 3/16/2026 |
| 1.2.9 | 84 | 3/13/2026 |
| 1.2.8 | 90 | 3/9/2026 |
| 1.2.7 | 88 | 3/4/2026 |
| 1.2.6 | 89 | 2/19/2026 |
| 1.2.5 | 84 | 2/17/2026 |
| 1.2.4 | 115 | 2/13/2026 |
| 1.2.3 | 108 | 1/27/2026 |
| 1.2.2 | 304 | 12/16/2025 |
| 1.2.1 | 282 | 12/16/2025 |
| 1.2.0 | 446 | 12/11/2025 |
| 1.1.21 | 452 | 12/10/2025 |