AeroDB.Sable.Configuration
0.0.9.7-alpha
dotnet add package AeroDB.Sable.Configuration --version 0.0.9.7-alpha
NuGet\Install-Package AeroDB.Sable.Configuration -Version 0.0.9.7-alpha
<PackageReference Include="AeroDB.Sable.Configuration" Version="0.0.9.7-alpha" />
<PackageVersion Include="AeroDB.Sable.Configuration" Version="0.0.9.7-alpha" />
<PackageReference Include="AeroDB.Sable.Configuration" />
paket add AeroDB.Sable.Configuration --version 0.0.9.7-alpha
#r "nuget: AeroDB.Sable.Configuration, 0.0.9.7-alpha"
#:package AeroDB.Sable.Configuration@0.0.9.7-alpha
#addin nuget:?package=AeroDB.Sable.Configuration&version=0.0.9.7-alpha&prerelease
#tool nuget:?package=AeroDB.Sable.Configuration&version=0.0.9.7-alpha&prerelease
AeroDB.Sable.Configuration
Encrypted .NET configuration backed by persistent embedded SurrealDB SurrealKV.
This package is an in-process encrypted configuration store. It protects
copied database files, backups, and database-only access. It is not the future
remote AeroDB.Sable.Vault
security boundary: the application process can
access the plaintext configuration and its wrapping-key provider.
Provision values
Bootstrap paths and key IDs must come from outside the encrypted store.
using AeroDB.Sable.Configuration;
var options = new SableConfigurationOptions
{
DatabasePath = "/var/lib/my-app/sable-configuration"
};
options.UseMountedKeyFile(
"/run/secrets/sable-configuration-kek",
keyId: "config-kek-2026-07");
using var store = new SableConfigurationStore(options);
await store.SetAsync(
"ConnectionStrings:Primary",
"Server=database;Database=application;...");
await store.SetAsync("Payments:ApiKey", paymentApiKey);
The mounted key file must contain either exactly 32 raw bytes or the Base64 encoding of exactly 32 bytes. The package never auto-generates or replaces the KEK.
Add the provider
using AeroDB.Sable.Configuration;
using Microsoft.Extensions.Configuration;
var builder = WebApplication.CreateBuilder(args);
var databasePath =
builder.Configuration["SableConfiguration:DatabasePath"]
?? throw new InvalidOperationException(
"SableConfiguration:DatabasePath is required.");
var keyFile =
builder.Configuration["SableConfiguration:KeyFile"]
?? throw new InvalidOperationException(
"SableConfiguration:KeyFile is required.");
builder.Configuration.AddSableConfiguration(options =>
{
options.DatabasePath = databasePath;
options.UseMountedKeyFile(keyFile, keyId: "config-kek-2026-07");
});
.NET configuration providers use last-added-wins precedence. In the example,
Sable values override matching appsettings.json, environment, User Secrets,
and command-line values already registered by WebApplication.CreateBuilder.
This is normally desirable for values deliberately moved into the encrypted
store.
If deployment environment variables must override Sable values, explicitly add that provider after Sable:
builder.Configuration
.AddSableConfiguration(options)
.AddEnvironmentVariables(prefix: "MYAPP_");
Across platforms, an environment hierarchy such as Payments:ApiKey is
written as MYAPP_Payments__ApiKey.
Consume values
Consumers use the ordinary .NET configuration APIs; they do not depend on the storage provider.
string? connectionString =
builder.Configuration.GetConnectionString("Primary");
string? paymentApiKey =
builder.Configuration["Payments:ApiKey"];
For related settings, prefer the options pattern:
public sealed class PaymentsOptions
{
public const string SectionName = "Payments";
public required Uri Endpoint { get; set; }
public required string ApiKey { get; set; }
}
builder.Services
.AddOptions<PaymentsOptions>()
.Bind(builder.Configuration.GetRequiredSection(PaymentsOptions.SectionName))
.Validate(
static options => !string.IsNullOrWhiteSpace(options.ApiKey),
"Payments:ApiKey is required.")
.ValidateOnStart();
IOptions<T> is a read-once singleton view. Use
IOptionsMonitor<T> for singleton consumers that must observe explicit Sable
refreshes, or IOptionsSnapshot<T> for scoped consumers.
Refresh
V1 refresh is explicit; the package does not start a polling loop.
var root = (IConfigurationRoot)builder.Configuration;
var sableProvider = root.Providers
.OfType<SableConfigurationProvider>()
.Single();
bool changed = await sableProvider.RefreshAsync();
When values changed, RefreshAsync calls the standard configuration reload
token. Bound IOptionsMonitor<T> instances receive the change.
Writes are asynchronous
.NET IConfiguration is a read-only unified view and isn't designed as a
programmatic persistence API. Assigning through its indexer is rejected by
this provider. Persist changes with ISableConfigurationStore.SetAsync or
DeleteAsync, then call RefreshAsync.
Development secrets
ASP.NET Core User Secrets is still useful for development bootstrap paths and
non-production values, but Microsoft documents that User Secrets is an
unencrypted JSON file and isn't a trusted store. Do not use production values
in development or commit secrets to appsettings.json.
References:
| 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
- AeroDB.Sable (>= 0.0.9.7-alpha)
- Dahomey.Cbor (>= 1.26.3)
- Microsoft.Extensions.Configuration (>= 10.0.5)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.9)
- SurrealDb.Embedded.SurrealKv (>= 0.10.2)
- SurrealDb.Net (>= 0.10.2)
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 |
|---|---|---|
| 0.0.9.7-alpha | 34 | 7/31/2026 |
| 0.0.9.6-alpha | 44 | 7/23/2026 |
| 0.0.9.5-alpha | 52 | 7/23/2026 |