C# SDK
Version compatibility: The C# SDK targets SecretSpec 0.16 and is unavailable in the current 0.15 release. With 0.15, use the CLI integration:
secretspec run -- dotnet run. TheCachix.SecretSpecpackage and API below become available with 0.16.
The C# SDK (Cachix.SecretSpec) is a thin client over the same Rust resolver as
the CLI. Every provider, fallback chain, profile, generator, reference, and
as_path secret therefore works without C#-side resolution logic.
Install (0.16+)
Section titled “Install (0.16+)”dotnet add package Cachix.SecretSpecThe package targets .NET 8 and includes the native resolver for Linux x64 and Arm64, macOS Arm64, and Windows x64. No separate SecretSpec CLI or native library installation is needed at runtime.
Quick start
Section titled “Quick start”using Cachix.SecretSpec;
using var resolved = SecretSpec.Builder() .WithProvider("keyring://") .WithProfile("production") .WithReason("boot web app") .Load();
Console.WriteLine($"{resolved.Provider} {resolved.Profile}");Console.WriteLine(resolved.Secrets["DATABASE_URL"].Get());resolved.SetAsEnv();Get() returns the inline value, or the readable file path for an as_path
secret. A missing required secret throws MissingRequiredException; its
Missing property contains the secret names. Other failures throw
SecretSpecException, whose Kind property is a stable error category.
A one-shot form is also available:
using var resolved = SecretSpec.Resolve( provider: "keyring://", profile: "production", reason: "boot web app");ASP.NET Core
Section titled “ASP.NET Core”Resolve and export secrets before creating the application builder, so normal environment-variable configuration sees them:
using Cachix.SecretSpec;
using var secrets = SecretSpec.Builder() .WithProfile(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")) .WithReason("ASP.NET Core boot") .Load();
secrets.SetAsEnv();
var builder = WebApplication.CreateBuilder(args);var app = builder.Build();app.Run();For longer-lived services, you can instead register resolved in dependency
injection and read ResolvedSecret objects directly. Keep the result alive for
as long as consumers need any as_path file.
Value-free preflight
Section titled “Value-free preflight”Report() returns the inventory view exposed by secretspec check --json.
It never carries values. Missing required secrets appear with
Status == "missing_required" rather than throwing, so incomplete deployments
can still be inspected.
var report = SecretSpec.Builder() .WithProfile("production") .WithReason("deployment preflight") .Report();
foreach (var secret in report.Secrets) Console.WriteLine($"{secret.Name}: {secret.Status}");Typed access
Section titled “Typed access”Generate an idiomatic C# model from the manifest schema:
secretspec schema | quicktype -s schema --top-level AppSecrets --lang csharp -o AppSecrets.csThen deserialize the SDK’s flat field map:
var typed = AppSecrets.FromJson(resolved.FieldsJson());Console.WriteLine(typed.DatabaseURL);The schema models successful resolution: required, defaulted, and generated secrets are non-nullable, and profile-specific schemas include inherited default-profile fields.
Files (as_path)
Section titled “Files (as_path)”File-shaped secrets are materialized as mode-0400 temporary files. The returned
path must remain valid after Load(), so the caller owns its lifetime.
Resolved implements IDisposable; use a using declaration or call Close()
to remove these files deterministically:
using var resolved = SecretSpec.Builder().WithReason("TLS boot").Load();var certificatePath = resolved.Secrets["TLS_CERT"].Get();// Use the certificate before resolved is disposed.Native loading
Section titled “Native loading”The NuGet runtime asset is selected automatically. For local SDK development,
SECRETSPEC_FFI_LIB can point to a particular libsecretspec_ffi build. From
a SecretSpec source checkout, the SDK also searches an ancestor Cargo
target/debug or target/release directory.