Documentation

Everything you need to install, configure, and use Dev Secret Stash.

Install the CLI

DevSecretStash is distributed as a .NET global tool. You need the .NET SDK 9.0+ installed.

dotnet tool install --global DevSecretStash

If dss is not found after install, add the dotnet tools directory to your PATH:

# Linux / macOS — add to ~/.bashrc or ~/.zshrc for persistence
export PATH="$PATH:$HOME/.dotnet/tools"

# Windows PowerShell — usually added automatically, but if not:
$env:PATH += ";$env:USERPROFILE\.dotnet\tools"

Verify the installation:

dss --help

To update to the latest version:

dotnet tool update --global DevSecretStash

Quick Start

1. Create an account

Register via the CLI (encryption keys are generated locally on your machine):

dss register

Or create an account on the website and then log in via the CLI:

dss login

2. Set up secrets for your project

If your project doesn't have user secrets initialized yet:

# Initialize secrets (adds UserSecretsId to .csproj)
cd /path/to/your/project
dotnet user-secrets init

# Add secrets
dotnet user-secrets set "ConnectionStrings:Default" "Server=mydb;Password=secret123"
dotnet user-secrets set "ApiKey" "sk_live_abc123"
dotnet user-secrets set "Smtp:Password" "email-pw"

# View your secrets
dotnet user-secrets list

3. Push secrets

From a .NET project directory that has a UserSecretsId in its .csproj:

dss push

Or specify the ID explicitly:

dss push my-project-secrets-id

4. Pull on another machine

dss login
dss pull

That's it. Your secrets are now synced. Run dotnet user-secrets list to confirm.

Working with .NET User Secrets

DevSecretStash syncs the secrets managed by dotnet user-secrets. Here's how they work together.

Setting individual secrets

dotnet user-secrets set "ConnectionStrings:Default" "Server=prod;Password=s3cret"
dotnet user-secrets set "Jwt:SigningKey" "my-long-secret-key"
dotnet user-secrets set "SendGrid:ApiKey" "SG.xxxxxxxx"

# Then sync
dss push

Sharing secrets across projects

dotnet user-secrets is project-specific — each collection is tied to a UserSecretsId in the .csproj. But you can reuse the same ID across multiple projects to share secrets:

<!-- In each .csproj that should share secrets -->
<PropertyGroup>
  <UserSecretsId>my-shared-secrets</UserSecretsId>
</PropertyGroup>

Managing secrets directly with dss

The dss secrets commands let you create and manage secret collections without dotnet user-secrets or a .csproj file:

# Create a new collection
dss secrets init my-app-secrets

# Add secrets
dss secrets set my-app-secrets "ConnectionStrings:Default" "Server=mydb;Password=s3cret"
dss secrets set my-app-secrets "ApiKey" "sk_live_abc123"

# View secrets
dss secrets list my-app-secrets

# Remove a secret
dss secrets remove my-app-secrets "ApiKey"

# Clear all secrets
dss secrets clear my-app-secrets

# Link a .csproj to use this collection
dss secrets link my-app-secrets
dss secrets link my-app-secrets --project ./src/MyApp/MyApp.csproj

# Sync to all your machines
dss push my-app-secrets

Secrets management commands

CommandDescription
dss secrets init <id>Create a new empty secrets collection
dss secrets set <id> <key> <value>Set a secret (creates collection if needed)
dss secrets remove <id> <key>Remove a secret
dss secrets list [id]List all secrets (auto-detects from .csproj if omitted)
dss secrets clear <id>Remove all secrets from a collection
dss secrets link <id> [-p project]Set a .csproj's UserSecretsId to this collection

Where secrets are stored locally

PlatformPath
Windows%APPDATA%\Microsoft\UserSecrets\<UserSecretsId>\secrets.json
Linux / macOS~/.microsoft/usersecrets/<UserSecretsId>/secrets.json

How It Works

[Your Machine]                     [devsecretstash.com]                  [Another Machine]
secrets.json  -->  encrypt  -->  encrypted blob (SQLite)  -->  decrypt  -->  secrets.json
                   (AES-256-GCM)                                (AES-256-GCM)

CLI Commands

CommandDescription
dss config --server-url <url>Set or view the server URL
dss registerCreate a new account (generates encryption keys locally)
dss loginAuthenticate and cache credentials locally
dss logoutClear cached credentials from this machine
dss push [id]Encrypt and upload local secrets
dss pull [id]Download and decrypt secrets to local machine
dss status [id]Compare local vs remote sync status
dss listList all secret collections stored on the server

Global options: --verbose / -v enables detailed HTTP request/response logging.

If [id] is omitted, the CLI auto-detects UserSecretsId from a .csproj in the current directory.

The CLI defaults to https://devsecretstash.com. Use dss config to point at a self-hosted instance.

Security Model

DevSecretStash uses a two-layer encryption scheme. Your plaintext secrets never leave your machine.

Encryption flow

Your password
  |  Argon2id (64MB memory, 3 iterations)
  v
Key-Encryption-Key (KEK) — never sent to server
  |  AES-256-GCM
  v
Wraps your Master Encryption Key (MEK) — server stores wrapped version only
  |
  v
MEK encrypts all your secrets via AES-256-GCM
LayerMechanismPurpose
AuthenticationArgon2id (server-side)Verify your identity
Key wrappingArgon2id (client) + AES-256-GCMProtect master key with password
Secret encryptionAES-256-GCMEncrypt secrets before upload
TransportHTTPS + HSTSProtect data in transit
Local storagechmod 600 (Linux/Mac)Protect cached keys on disk
API authJWT (15m) + refresh tokens (30d)Stateless auth with rotation
Rate limiting10 attempts / 15 minPrevent brute force

What a server compromise reveals

An attacker with full database access gets: your email, a password hash (Argon2id), an encrypted master key, and encrypted secret blobs. They would need to brute-force your password through Argon2id to decrypt anything. No plaintext secrets are ever on the server — not in memory, not in logs, not in the database.

API Reference

The full REST API is documented via Swagger at /swagger.

EndpointAuthDescription
POST /api/auth/registerNoCreate account + store wrapped master key
POST /api/auth/loginNoAuthenticate, get JWT + encrypted master key
POST /api/auth/refreshNoRotate refresh token, get new JWT
POST /api/auth/change-passwordJWTUpdate password + re-wrapped master key
GET /api/secretsJWTList all collections
GET /api/secrets/{id}JWTGet encrypted collection
PUT /api/secrets/{id}JWTPush encrypted collection (optimistic concurrency via version)
DELETE /api/secrets/{id}JWTDelete collection

Self-Hosting

You can self-host DevSecretStash instead of using devsecretstash.com.

Docker

docker build -t devsecretstash-api -f src/DevSecretStash.Api/Dockerfile .
docker run -d -p 8080:8080 \
  -v dss-data:/app/data \
  -e Jwt__Key="your-secret-key-minimum-32-characters!" \
  devsecretstash-api

Azure App Service

# Create resource group and B1 Linux plan
az group create --name devsecretstash-rg --location centralus
az appservice plan create --name devsecretstash-plan \
  --resource-group devsecretstash-rg --is-linux --sku B1

# Create web app
az webapp create --name devsecretstash \
  --resource-group devsecretstash-rg \
  --plan devsecretstash-plan --runtime "DOTNETCORE:9.0"

# Configure
az webapp config appsettings set --name devsecretstash \
  --resource-group devsecretstash-rg --settings \
    Jwt__Key="YOUR-SECRET-KEY-MIN-32-CHARS" \
    ConnectionStrings__Default="Data Source=/home/site/data/devsecretstash.db" \
    ASPNETCORE_URLS="http://+:8080"

# Deploy
dotnet publish src/DevSecretStash.Api -c Release -o ./publish
cd publish && zip -r ../deploy.zip . && cd ..
az webapp deploy --name devsecretstash \
  --resource-group devsecretstash-rg --src-path deploy.zip --type zip

Point CLI at your server

dss config --server-url https://your-server.example.com

Server configuration

SettingEnvironment VariableDefault
DatabaseConnectionStrings__DefaultData Source=devsecretstash.db
JWT KeyJwt__KeyChange in production!
JWT IssuerJwt__IssuerDevSecretStash
Token lifetimeJwt__AccessTokenMinutes15
Refresh lifetimeJwt__RefreshTokenDays30