Secrets Management¶
This page covers how Deepstaging handles sensitive configuration values — API keys, passwords, connection strings, and other secrets.
Overview¶
The [Secret] attribute marks configuration properties as sensitive. This triggers a separate handling pipeline:
- Secret properties are excluded from
deepstaging.schema.json - Secret properties are included in
deepstaging.secrets.schema.json - A local
deepstaging.secrets.jsonfile is generated (and gitignored) - The generated source method calls
AddUserSecretsto load from the .NET User Secrets store - A sync script (
deepstaging.secrets-update.sh) bridges the JSON file anddotnet user-secrets
public class DatabaseSecrets
{
[Secret]
public string ConnectionString { get; init; } = "";
[Secret]
public string AdminPassword { get; init; } = "";
}
[ConfigRoot]
[Exposes<DatabaseSecrets>]
public sealed partial class DatabaseConfigRoot;
[Secret] Attribute¶
Apply [Secret] to any property on a configuration type that contains sensitive data:
public class SmtpSecrets
{
[Secret]
public string Password { get; init; } = "";
[Secret]
public string ApiKey { get; init; } = "";
// Non-secret — goes in deepstaging.schema.json
public string SenderAddress { get; init; } = "";
}
What [Secret] Does¶
| Aspect | Without [Secret] |
With [Secret] |
|---|---|---|
| Schema | deepstaging.schema.json |
deepstaging.secrets.schema.json |
| Settings file | deepstaging.settings.json |
deepstaging.secrets.json |
| Source control | Committed normally | .gitignore entry added |
| Runtime source | JSON file only | JSON file + AddUserSecrets |
Schema Separation¶
The generator produces two separate JSON Schema files, ensuring secrets never appear in the main schema:
{
"$schema": "https://json-schema.org/draft-07/schema#",
"$id": "deepstaging.secrets.schema.json",
"type": "object",
"properties": {
"Smtp": {
"type": "object",
"properties": {
"SmtpSecrets": {
"type": "object",
"properties": {
"Password": { "type": "string" },
"ApiKey": { "type": "string" }
}
}
}
}
}
}
.NET User Secrets Integration¶
When a [ConfigRoot] exposes any [Secret] properties, the generated Configure*Sources method automatically calls AddUserSecrets:
public static IConfigurationBuilder ConfigureDatabaseConfigRootSources(
this IConfigurationBuilder builder,
string? environmentName = null,
string? settingsPath = null)
{
// ... load settings files ...
builder.AddUserSecrets(typeof(DatabaseConfigRoot).Assembly, optional: true);
return builder;
}
UserSecretsId Requirement (DSCFG07)¶
AddUserSecrets requires a <UserSecretsId> in your project file. If [Secret] properties exist but no UserSecretsId is configured, the analyzer reports DSCFG07 as an error.
DSCFG07 — Missing UserSecretsId
Fix: Use the code fix to add the element, or run manually:
This adds a <UserSecretsId> GUID to your .csproj:
<PropertyGroup>
<UserSecretsId>a1b2c3d4-e5f6-7890-abcd-ef1234567890</UserSecretsId>
</PropertyGroup>
deepstaging.secrets.json¶
The DSCFG06 code fix generates a deepstaging.secrets.json file at the project root. This file:
- Contains a template with all
[Secret]properties - Is added to
.gitignoreautomatically - Serves as the source of truth for local development secrets
- Is synced to
dotnet user-secretsvia the update script
{
"$schema": "deepstaging.secrets.schema.json",
"Database": {
"DatabaseSecrets": {
"ConnectionString": "",
"AdminPassword": ""
}
}
}
Fill in the values for your local environment:
{
"$schema": "deepstaging.secrets.schema.json",
"Database": {
"DatabaseSecrets": {
"ConnectionString": "Host=localhost;Database=myapp;Username=admin;Password=secret",
"AdminPassword": "local-dev-password"
}
}
}
deepstaging.secrets-update.sh¶
The generated sync script loads deepstaging.secrets.json into the .NET User Secrets store:
The script:
- Clears existing user secrets for the project
- Reads
deepstaging.secrets.json - Loads all values via
dotnet user-secrets set - Prints the current secrets list
DSCFG09 — Sync notification
When [Secret] properties exist, the analyzer reports DSCFG09 (Info) as a reminder:
Use the code fix to run the script directly from your IDE.
Automatic Secret Detection (DSCFG05)¶
The PotentialSecretPropertyAnalyzer inspects properties on types exposed via [Exposes<T>] on [ConfigRoot] or [ConfigModule] and flags those whose names match common secret patterns:
| Pattern | Examples |
|---|---|
Password |
Password, AdminPassword, SmtpPassword |
Secret |
ClientSecret, AppSecret |
ApiKey |
ApiKey, StripeApiKey |
Token |
AccessToken, RefreshToken, BearerToken |
ConnectionString |
ConnectionString, DbConnectionString |
Credential |
Credential, UserCredential |
PrivateKey |
PrivateKey, SshPrivateKey |
AccessKey |
AccessKey, AwsAccessKey |
ClientSecret |
ClientSecret, OAuthClientSecret |
Passphrase |
Passphrase, KeyPassphrase |
DSCFG05
warning DSCFG05: Property 'ConnectionString' on 'DatabaseConfig' appears to
contain secrets or PII — consider adding [Secret]
Fix: Apply the code fix to add [Secret] to the property, or suppress the diagnostic if the property is intentionally non-secret.
Best Practices¶
- Always use
[Secret]for passwords, API keys, tokens, and connection strings - Never commit
deepstaging.secrets.json— the code fix adds it to.gitignoreautomatically - Run
dotnet user-secrets initearly — before adding your first[Secret]property, or let the DSCFG07 code fix handle it - Run the sync script after editing secrets —
./deepstaging.secrets-update.shkeepsdotnet user-secretsin sync - Use per-environment overrides for non-secrets — environment-specific settings go in
deepstaging.settings.{Environment}.json - Use CI/CD secret management for production —
deepstaging.secrets.jsonis for local development only; use Azure Key Vault, AWS Secrets Manager, or environment variables in production