Skip to content

Declarative Configuration

SecretSpec uses secretspec.toml to declare what secrets your application needs, separating requirements from storage mechanisms for portability across environments.

[project]
name = "my-app"
revision = "1.0"
extends = ["../shared/common"] # Optional: inherit from other configs
[profiles.default]
DATABASE_URL = { description = "PostgreSQL connection string", required = true }
API_KEY = { description = "External API key", required = true }
LOG_LEVEL = { description = "Logging verbosity", required = false, default = "info" }

Each secret is declared with configuration options:

SECRET_NAME = {
description = "Human-readable explanation", # Required: shown in prompts
required = true, # Optional: defaults to true
default = "value" # Optional: fallback if not set
}

Options:

  • description: Explains the secret’s purpose (required)
  • required: Whether the secret must be provided (default: true)
  • default: Fallback value for optional secrets

SecretSpec supports sharing common secrets across projects through the extends field.

shared/common/secretspec.toml
[project]
name = "common"
[profiles.default]
DATABASE_URL = { description = "Main database", required = true }
LOG_LEVEL = { description = "Log verbosity", required = false, default = "info" }
myapp/secretspec.toml
[project]
name = "myapp"
extends = ["../shared/common"]
[profiles.default]
DATABASE_URL = { description = "MyApp database", required = true } # Override
API_KEY = { description = "External API key", required = true } # Add new
monorepo/
├── shared/
│ ├── base/secretspec.toml # Common secrets
│ └── database/secretspec.toml # DB-specific (extends base)
└── services/
├── api/secretspec.toml # API service (extends database)
└── frontend/secretspec.toml # Frontend (extends base)
[project]
name = "api-service"
extends = ["../../shared/base", "../../shared/database", "../../shared/auth"]

Rules:

  • Child definitions completely replace parent definitions
  • Later sources in extends override earlier ones
  • Each profile is merged independently
  • Paths are relative to the containing file
  1. Descriptive names: Use STRIPE_API_KEY instead of generic API_KEY
  2. Clear descriptions: Help developers understand each secret’s purpose
  3. Sensible defaults: Provide development defaults, require production values
  4. Modular inheritance: Create reusable base configurations for common patterns
[project]
name = "web-api"
revision = "2.1.0"
extends = ["../shared/base", "../shared/auth"]
[profiles.default]
# Inherits DATABASE_URL, LOG_LEVEL from base
# Inherits JWT_SECRET, SESSION_SECRET from auth
# Service-specific additions:
STRIPE_API_KEY = { description = "Stripe payment API", required = true }
REDIS_URL = { description = "Redis cache connection", required = true }
PORT = { description = "Server port", required = false, default = "3000" }