ForgeTrust.AppSurface.Config.LocalSecrets
0.2.0-preview.1
dotnet add package ForgeTrust.AppSurface.Config.LocalSecrets --version 0.2.0-preview.1
NuGet\Install-Package ForgeTrust.AppSurface.Config.LocalSecrets -Version 0.2.0-preview.1
<PackageReference Include="ForgeTrust.AppSurface.Config.LocalSecrets" Version="0.2.0-preview.1" />
<PackageVersion Include="ForgeTrust.AppSurface.Config.LocalSecrets" Version="0.2.0-preview.1" />
<PackageReference Include="ForgeTrust.AppSurface.Config.LocalSecrets" />
paket add ForgeTrust.AppSurface.Config.LocalSecrets --version 0.2.0-preview.1
#r "nuget: ForgeTrust.AppSurface.Config.LocalSecrets, 0.2.0-preview.1"
#:package ForgeTrust.AppSurface.Config.LocalSecrets@0.2.0-preview.1
#addin nuget:?package=ForgeTrust.AppSurface.Config.LocalSecrets&version=0.2.0-preview.1&prerelease
#tool nuget:?package=ForgeTrust.AppSurface.Config.LocalSecrets&version=0.2.0-preview.1&prerelease
ForgeTrust.AppSurface.Config.LocalSecrets
OS-backed local secret posture for AppSurface configuration.
Use this package when a solo or hobbyist AppSurface app needs local development secrets before it has a remote vault. LocalSecrets is not a team vault, CI secret system, container secret provider, or production rotation/audit solution.
Install
dotnet package add ForgeTrust.AppSurface.Config.LocalSecrets
Register AppSurfaceLocalSecretsModule beside your Config module. Environment variables still win, LocalSecrets sits
above file configuration, and only a true missing local secret falls through to files.
For Linux, AppSurface treats secret-tool as an external command with an explicit trust boundary. By default it uses
only /usr/bin/secret-tool, then /bin/secret-tool. It does not execute secret-tool from PATH; a PATH match is
reported only as ignored diagnostic context.
First Secret
appsurface secrets init --app MyApp --environment Development
printf '%s' "<secret>" | appsurface secrets set Stripe:ApiKey --app MyApp --environment Development --stdin
appsurface secrets doctor --app MyApp --environment Development
dotnet run
appsurface config diagnostics
The diagnostics path reports where a value came from without printing the raw secret value.
File fallback posture
The OS-backed stores are the normal LocalSecrets path. The --store-file <path> fallback exists for deterministic
examples, unsupported local environments, and tests. It is not equivalent to Keychain, Secret Service, or Windows
Credential Manager.
On Unix platforms, the file fallback creates missing directories with 0700 mode bits and writes or repairs the JSON
file with 0600 mode bits during set, delete, and doctor. Existing parent directories are inspected, not
modified in place; loose parent directories stop resolution with a paste-safe diagnostic. Reads inspect existing files
before returning a secret value: symbolic-link paths, directory paths, and non-canonical mode bits stop resolution
instead of silently serving a risky file. doctor may report:
| Diagnostic code | Meaning |
|---|---|
local-secret-store-ready |
The fallback file can be opened and posture is already ready. |
local-secret-file-posture-repaired |
doctor or a write tightened Unix file mode bits. |
local-secret-file-posture-degraded |
The fallback can be opened and doctor can exit successfully, but this platform path does not prove owner-only posture in v1. |
local-secret-file-posture-unsupported |
The path shape or checked Unix posture is unsafe for fallback storage, such as a symbolic link, directory path, loose mode bits, or writable non-sticky ancestor. |
Example deterministic file fallback check:
appsurface secrets doctor --app MyApp --environment Development --store-file ./.appsurface/local-secrets.json
The command prints Problem, Cause, Fix, Docs, and Retryable without printing secret values.
local-secret-file-posture-degraded is a degraded readiness result for explicit local/test fallback workflows;
local-secret-file-posture-unsupported is a fail-closed result that stops reads and writes until the path is moved or
repaired. Prefer the OS-backed store for normal local development.
Linux Nonstandard secret-tool
Use this only when your trusted Linux secret-tool install lives outside /usr/bin or /bin, such as a Nix,
Linuxbrew, Guix, or custom prefix install.
SECRET_TOOL=/absolute/path/to/secret-tool
test -x "$SECRET_TOOL"
appsurface secrets doctor --app MyApp --environment Development --secret-tool-path "$SECRET_TOOL"
printf '%s' "<secret>" | appsurface secrets set Stripe:ApiKey --app MyApp --environment Development --secret-tool-path "$SECRET_TOOL" --stdin
Use the package option for app runtime configuration:
services.ConfigureAppSurfaceLocalSecrets(options =>
{
options.LinuxSecretToolPath = "/absolute/path/to/secret-tool";
});
The override must be an absolute path to an executable file. Empty, relative, missing, directory, non-executable, and
non-Linux overrides fail before command launch with Problem, Cause, Fix, Docs, and Retryable diagnostics.
--secret-tool-path cannot be combined with --store-file; --store-file is the deterministic example/test store, not
a platform-store verification path.
Listing And Cleanup
appsurface secrets list prints only currently retrievable logical key names, never values. Platform-backed stores keep
a local name index so they can list safely across macOS Keychain, Linux Secret Service, and Windows Credential Manager.
When list can read the index and validate the named values, it silently prunes indexed names whose values are already
missing. If the platform store is locked, unavailable, or the index is corrupt, list fails with a paste-safe diagnostic
instead of hiding names it could not verify.
appsurface secrets delete KEY is narrowly idempotent for stale indexed names: if the value is already missing but the
platform index still contains KEY, delete removes the stale name and reports success. A key that has no value and no
index entry still reports local-secret-missing.
Posture Modes
DevelopmentOnlyis the default. It permitsDevelopment,Local, andDev.SingleMachineSelfHostedis explicit self-hosting. It does not provide team vault guarantees.Disabledstops LocalSecrets from resolving values.
Use environment variables, key-per-file, or a remote vault in CI, containers, team environments, and production.
Release Guidance
Use the package chooser and release hub for the current package-facing prerelease story, risk notes, and migration guidance.
Structured Statuses
AppSurfaceLocalSecretProvider.GetValue<T> adapts LocalSecrets into the normal AppSurface config provider contract.
When callers need the LocalSecrets status directly, use ResolveValue<T>(environment, key). It returns
Found, Missing, Unavailable, Locked, UnsupportedPlatform, DisabledByPosture, InvalidIdentity,
ConversionFailed, or ProviderFailed with a paste-safe diagnostic and source name. Only Missing means the
provider should fall through to lower-priority configuration.
Platform Matrix
| Platform | Adapter | Notes |
|---|---|---|
| macOS | Keychain generic passwords through Security.framework | Requires an interactive user session when Keychain prompts. |
| Linux | Secret Service through trusted secret-tool paths |
Uses /usr/bin/secret-tool, then /bin/secret-tool, or an explicit absolute LinuxSecretToolPath/--secret-tool-path. Requires DBus/session secret service availability. |
| Windows | Credential Manager generic credentials for the current user | Requires an interactive user profile; use environment variables/key-per-file for services, CI, and containers. |
| Explicit file fallback | JSON file at --store-file <path> |
Unix mode-bit hardening only; Windows and unknown filesystem ACL posture is reported as degraded. |
Escape Hatches, Safest First
- Keep the trusted Linux defaults when
secret-toolis in/usr/binor/bin. - Set
AppSurfaceLocalSecretsOptions.LinuxSecretToolPathor pass--secret-tool-pathfor a trusted nonstandard Linux executable that you verified withtest -x. - Use
--store-file <path>only for deterministic examples, tests, and docs snippets. - Replace the store with
UseAppSurfaceLocalSecretStore(...)for controlled integration tests or app-specific local development behavior. - Change
FailClosedOnStoreFailure = falseonly as a last resort. It can make unavailable local stores behave like missing values and hide secrets from lower-priority file providers.
Linux Smoke Checklist
Deterministic tests cover resolver branches with fakes. Before release, run a live Linux desktop session smoke when DBus and a Secret Service implementation are available:
appsurface secrets doctor --app MyApp --environment Development
printf '%s' "smoke-value" | appsurface secrets set Smoke:Value --app MyApp --environment Development --stdin
appsurface secrets get Smoke:Value --app MyApp --environment Development
appsurface secrets list --names-only --app MyApp --environment Development
appsurface secrets delete Smoke:Value --app MyApp --environment Development
For nonstandard installs, repeat the same commands with --secret-tool-path "$SECRET_TOOL" after test -x "$SECRET_TOOL".
Migration Ladder
appsettings defaults < LocalSecrets < environment variables < future remote vault provider
Keep the same AppSurface config key when moving from .env, dotnet user-secrets, or accidental
appsettings.Development.json secrets into LocalSecrets. Later vault providers should preserve the same logical key.
Guides:
| 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
- CliWrap (>= 3.10.1)
- ForgeTrust.AppSurface.Config (>= 0.2.0-preview.1)
- ForgeTrust.AppSurface.Core (>= 0.2.0-preview.1)
- Microsoft.Extensions.Hosting (>= 10.0.8)
- Microsoft.Extensions.Logging.Console (>= 10.0.8)
- Microsoft.Extensions.Options (>= 10.0.8)
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.2.0-preview.1 | 57 | 6/28/2026 |
| 0.1.0-rc.4 | 176 | 6/16/2026 |