CSealTest 1.0.2
dotnet add package CSealTest --version 1.0.2
NuGet\Install-Package CSealTest -Version 1.0.2
<PackageReference Include="CSealTest" Version="1.0.2" />
<PackageVersion Include="CSealTest" Version="1.0.2" />
<PackageReference Include="CSealTest" />
paket add CSealTest --version 1.0.2
#r "nuget: CSealTest, 1.0.2"
#:package CSealTest@1.0.2
#addin nuget:?package=CSealTest&version=1.0.2
#tool nuget:?package=CSealTest&version=1.0.2
ConfigSeal
Strict startup configuration validation for ASP.NET Core. The FluentValidation for configuration.
The Problem
Configuration errors are silent killers. Your app starts fine, then crashes in production because App:PaymentApiKey was never set, Database:MaxPoolSize is 0, or a developer accidentally committed localhost as the API base URL.
.NET's built-in IOptions<T> binding gives you type safety, but no enforcement. Missing values stay null. Invalid values go unnoticed. Secrets get left as placeholders.
ConfigSeal catches all of this at startup, before your app serves a single request.
What It Looks Like
// Program.cs
var guard = builder.Services.AddConfigSeal(builder.Configuration, builder.Environment);
guard.Bind<AppOptions>("App")
.Require(x => x.ApiKey)
.Validate(x => x.Timeout > 0)
.ValidateEnvironment();
guard.Bind<DatabaseOptions>("Database")
.Require(x => x.ConnectionString)
.Validate(x => x.MaxPoolSize is > 0 and <= 500, "MaxPoolSize must be 1–500.");
guard.ValidateAll(); // throws if anything is wrong — app will not start
On failure, instead of a cryptic NullReferenceException at runtime, you get:
╔══════════════════════════════════════════════════════════════╗
║ ConfigSeal: Configuration Sealed — Startup Blocked ║
╠══════════════════════════════════════════════════════════════╣
║ ✗ [Critical] App.ApiKey
║ → Required configuration value is missing or empty.
║ 💡 Generate via: openssl rand -hex 32
║ ✗ [Error] Database.MaxPoolSize
║ → Value 0 is outside the allowed range [1, 500].
╠══════════════════════════════════════════════════════════════╣
║ 2 validation error(s) must be resolved before startup.
╚══════════════════════════════════════════════════════════════╝
Features
Attribute-Based Validation
Decorate your options classes — no extra registration needed:
public class AppOptions
{
[RequiredConfig(Hint = "Generate via: openssl rand -hex 32")]
[SecretConfig(MinLength = 32)]
public string? ApiKey { get; set; }
[RangeConfig(1, 300)]
public int TimeoutSeconds { get; set; }
[AllowedValues("Strict", "Relaxed", "Off")]
public string? CorsPolicy { get; set; }
[RegexConfig(@"^https://", ErrorMessage = "BaseUrl must use HTTPS.")]
public string? BaseUrl { get; set; }
}
| Attribute | What it enforces |
|---|---|
[RequiredConfig] |
Non-null, non-empty value |
[SecretConfig(MinLength)] |
Exists, meets length, not a placeholder (CHANGEME, TODO, etc.) |
[RangeConfig(min, max)] |
Numeric value within bounds |
[AllowedValues("a","b")] |
Value must match one of the allowed strings |
[RegexConfig(pattern)] |
Value matches the regex |
[EnvironmentOnly("Production")] |
Only validated in specified environments |
Fluent Validation
For logic that spans multiple properties or needs runtime context:
guard.Bind<CacheOptions>("Cache")
.Validate(x => !(x.UseMemoryCache && x.UseRedis),
"Cannot enable both MemoryCache and Redis simultaneously.")
.Validate(x => x.ExpiryMinutes,
v => v is > 0 and <= 1440,
"Cache expiry must be between 1 minute and 24 hours.");
Environment Safety Checks
.ValidateEnvironment() fails startup if production config contains development defaults:
localhost, 127.0.0.1, development, debug, ::1, etc.
Secret Validation
[SecretConfig] catches the most common secrets-in-production mistakes:
- Missing entirely → Critical
- Looks like a placeholder (
CHANGEME,your_secret_here,<replace_me>) → Critical - Too short for the context → Warning
All Failures at Once
ConfigSeal collects every problem before throwing. You see the full picture on the first failed deploy, not one error at a time.
Custom Validators
Plug in reusable cross-cutting validation:
public class ConnectionStringValidator : IConfigValidator
{
public void Validate(object options, ValidationContext context)
{
if (options is DatabaseOptions db)
if (!db.ConnectionString?.Contains("Encrypt=True") ?? true)
context.AddWarning("ConnectionString",
"Connection string does not enforce encryption.",
"Add 'Encrypt=True' for production.");
}
}
guard.Bind<DatabaseOptions>("Database")
.WithValidator(new ConnectionStringValidator());
Installation
dotnet add package ConfigSeal
Targets net8.0. Depends only on Microsoft.Extensions.* abstractions — no heavy third-party dependencies.
Publishing a Release
Releases are fully automated via GitHub Actions. The publish job runs only on version tags and requires the build-and-test job to pass first.
One-time setup
Add your NuGet API key as a repository secret:
- Go to nuget.org → Account → API Keys → Create
- In your GitHub repo → Settings → Secrets and variables → Actions → New repository secret
- Name:
NUGET_API_KEY, value: your key
Releasing
# Bump the version in src/ConfigSeal/ConfigSeal.csproj first, then:
git tag v1.2.3
git push origin v1.2.3
The CI pipeline will:
- Build and run all tests (blocks publish on failure)
- Pack
ConfigSeal.1.2.3.nupkgusing the tag as the version - Push to nuget.org
- Create a GitHub Release with auto-generated notes and the
.nupkgattached
The tag version and the
<Version>in the.csprojcan differ — the tag always wins at publish time. Keep them in sync to avoid confusion.
Versioning
This project follows Semantic Versioning:
| Change | Version bump |
|---|---|
| Breaking API change | MAJOR — e.g. v2.0.0 |
| New backward-compatible feature | MINOR — e.g. v1.1.0 |
| Bug fix / internal change | PATCH — e.g. v1.0.1 |
See CHANGELOG.md for the full release history.
Why ConfigSeal?
Every ASP.NET Core app uses configuration. Very few apps validate it rigorously. The ones that do write the same if (string.IsNullOrEmpty(...)) throw boilerplate in every project.
ConfigSeal makes thorough configuration validation the default, not the exception.
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
-
net8.0
- Microsoft.Extensions.Configuration.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 8.0.0)
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.0.2 | 113 | 3/29/2026 |