Etymon.Config 0.1.0-preview.6

This is a prerelease version of Etymon.Config.
dotnet add package Etymon.Config --version 0.1.0-preview.6
                    
NuGet\Install-Package Etymon.Config -Version 0.1.0-preview.6
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Etymon.Config" Version="0.1.0-preview.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Etymon.Config" Version="0.1.0-preview.6" />
                    
Directory.Packages.props
<PackageReference Include="Etymon.Config" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Etymon.Config --version 0.1.0-preview.6
                    
#r "nuget: Etymon.Config, 0.1.0-preview.6"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Etymon.Config@0.1.0-preview.6
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Etymon.Config&version=0.1.0-preview.6&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Etymon.Config&version=0.1.0-preview.6&prerelease
                    
Install as a Cake Tool

Etymon.Config

Configuration loaded through a schema, with every problem reported at once.

Part of the Etymon suite. Depends on Etymon.Core, Etymon.Base and Etymon.Schema — no NuGet package beyond FSharp.Core, and deliberately not Microsoft.Extensions.Configuration.

Config.load settingsSchema [
    Source.jsonFile "appsettings.json"
    Source.optionalJsonFile $"appsettings.{environmentName}.json"
    Source.environment "APP"
]

Sources are given in increasing order of precedence: the last one holding a setting wins.

Every problem, once

Starting up, failing on the first missing setting, being restarted, and failing on the second is a slow way to learn something that could have been said in one breath.

3 configuration problems:
  database.port: is required, and must be an integer (searched: appsettings.json, environment)
  database.password: is required, and must be a string (searched: appsettings.json, environment). The value is not shown because this setting is sensitive.
  serviceName: is required, and must be a string (searched: appsettings.json, environment)

Each line says the path, what was expected, and where it was looked for. When a value was found but was wrong, it says which source supplied it instead:

  database.port: expected an integer (from environment)

That last detail is the one that saves an afternoon. The configuration bug that actually costs time is rarely a wrong value — it is the wrong source winning, and that is invisible until something names it.

A source that cannot be read at all is reported the same way, alongside the settings it could not supply, rather than throwing before the others have been looked at.

Everything is a string, and that is fine

Environment variables, query strings and .env files have no types. Etymon.Config decodes in DecodeMode.Coercing, so "8080" is the port and "yes" is the boolean — the forms that actually occur, not only the two JSON recognises.

Coercion never makes nonsense acceptable. "eighty" is still not a port.

Secrets

Mark a field Schema.sensitive and it is redacted wherever Etymon prints it — in errors, and in the startup summary. Decode it into Secret<'T> from Etymon.Core and it redacts itself everywhere else too, including when somebody prints the whole settings record:

Schema.required "password"
    (Schema.string |> Schema.sensitive |> Schema.convert Secret.create Secret.reveal)
    (fun d -> d.Password)
printfn "%A" settings
// { Database = { Host = "db.internal"; Port = 5432; Password = <redacted> } ... }

Say what you loaded

printfn "%s" (Config.explain settingsSchema sources)
database.host      db.internal  appsettings.json
database.port      5432         appsettings.json
database.password  <redacted>   environment
serviceName        billing      appsettings.json
retries            (not set)    (not set)

Worth printing at startup for the same reason the errors name their source. Config.settings gives the same information as data if you would rather log it structurally.

Sources

Source.environment (with a required prefix — without one, PATH starts meaning something), Source.jsonFile, Source.optionalJsonFile, Source.jsonText, Source.inMemory, and Source.custom for user secrets, a key vault, a command line or anything else.

A source is one function returning flat key-value entries, so adding one is small.

Two deliberate behaviours around optional files: an absent optional file is not a problem, but one that exists and does not parse still is. Silently ignoring a malformed file is how a deployment runs for a week on defaults nobody meant.

How lookup works

Rather than merging every source into one document and hoping, the loader walks the schema to learn which settings exist, then looks each one up in each source. That is why it can say "database.port was not found" instead of waiting for a decoder to notice, and why it knows what type each setting wanted.

Keys are matched case-insensitively and __ separates path segments — the same convention Docker, Kubernetes and Microsoft.Extensions.Configuration already use, so an existing deployment needs no changes.

Licence

MIT.

Product 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Etymon.Config:

Package Downloads
Etymon

The Etymon suite: one schema definition as the source of truth for JSON codecs, validation, OpenAPI, TypeScript, configuration and database structure. This package has no code of its own; it references the parts of the suite that cost nothing but FSharp.Core. The adapters (Etymon.Api.Giraffe, Etymon.Api.AspNetCore) and the generator package (Etymon.Invariants.FsCheck) are deliberately left out, because each carries a dependency that would then be everyone's.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0-preview.6 0 9/21/2026
0.1.0-preview.5 0 9/21/2026
0.1.0-preview.4 27 9/21/2026
0.1.0-preview.3 31 9/21/2026
0.1.0-preview.2 35 9/21/2026
0.1.0-preview.1 35 9/20/2026