# Secret Generation

> Automatically generating passwords, tokens, and keys for missing secrets

**New in version 0.7**

Secrets can be declared with `type` and `generate` to be auto-generated when missing. This is useful for passwords, tokens, and keys that do not need to be shared across developers.

## Basic Usage

```toml
[profiles.default]
DB_PASSWORD = { description = "Database password", type = "password", generate = true }
API_TOKEN = { description = "API token", type = "hex", generate = { bytes = 32 } }
SESSION_KEY = { description = "Session key", type = "base64", generate = { bytes = 64 } }
REQUEST_ID = { description = "Request ID prefix", type = "uuid", generate = true }
```

## Generation Types

| Type                          | Default Output                                | Options                                                                                                                            |
| ----------------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `password`                    | 32 alphanumeric chars                         | `length` (int), `charset` (`"alphanumeric"` or `"ascii"`)                                                                          |
| `hex`                         | 64 hex chars (32 bytes)                       | `bytes` (int)                                                                                                                      |
| `base64`                      | 44 chars (32 bytes)                           | `bytes` (int)                                                                                                                      |
| `uuid`                        | UUID v4 (36 chars)                            | none                                                                                                                               |
| `command`                     | stdout of command; exact bytes in 0.21+       | `command` (string, required)                                                                                                       |
| `rsa_private_key`             | 2048-bit RSA private key (PKCS1 PEM)          | `bits` (int)                                                                                                                       |
| `openpgp_private_key` (0.21+) | ASCII-armored OpenPGP transferable secret key | `user_id` (required), `algorithm` (`"ed25519"` or `"rsa"`), `bits` (RSA only), `capabilities` (`["sign"]`, `["encrypt"]`, or both) |
| `ssh_private_key` (0.21+)     | Unencrypted OpenSSH Ed25519 private key       | `algorithm` (`"ed25519"` or `"rsa"`), `bits` (RSA only), `comment` (string)                                                        |

### Command type

**Changed in version 0.21**

Command output is preserved byte-for-byte, including non-UTF-8 data, NULs, whitespace, and final newlines. Output that is empty or contains only whitespace is rejected. Use `printf` or trim inside your command if you need a value without a final newline. Binary output needs a byte-capable provider or manifest `encoding`.

The `command` type runs a shell command and uses its stdout as the generated value:

```toml
MONGO_KEY = { description = "MongoDB keyfile", type = "command", generate = { command = "openssl rand -base64 765" } }
```

`command` requires `generate = { command = "..." }` rather than just `generate = true`.

### OpenPGP private keys

**New in version 0.21**

`openpgp_private_key` generates a GnuPG-compatible OpenPGP v4 key entirely in process; neither `gpg` nor another executable is required. Its modern default uses an Ed25519 certification-only primary key and puts routine operations on separate Ed25519 signing and Curve25519 encryption subkeys.

The User ID is required. With no `capabilities`, SecretSpec creates both signing and encryption subkeys:

```toml
[profiles.default]
GENERAL_KEY = { description = "Service OpenPGP key", type = "openpgp_private_key", generate = { user_id = "Service Bot <service@example.com>" } }


# A signing-only key has no encryption subkey.
RELEASE_KEY = { description = "Release signing key", type = "openpgp_private_key", generate = { user_id = "Release Bot <releases@example.com>", capabilities = ["sign"] } }


# RSA is available for consumers that require it; 3072 bits is the default.
LEGACY_KEY = { description = "Legacy-compatible OpenPGP key", type = "openpgp_private_key", generate = { user_id = "Legacy Bot <legacy@example.com>", algorithm = "rsa", bits = 4096 } }
```

`capabilities` must be a non-empty list containing `"sign"`, `"encrypt"`, or both without duplicates. `algorithm` defaults to `"ed25519"`. Selecting `"rsa"` uses RSA for the primary key and every requested subkey; `bits` defaults to 3072 and accepts values from 2048 through 8192. `bits` is invalid with `"ed25519"`.

The result is one `-----BEGIN PGP PRIVATE KEY BLOCK-----` value that can be imported by GnuPG and other OpenPGP tools. It has no OpenPGP passphrase and no expiration; protect it with an encrypted provider and rotate it according to the consuming system’s policy. Set `as_path = true` when a command needs a temporary key file rather than the armored value in an environment variable.

### SSH private keys

**New in version 0.21**

`ssh_private_key` generates an unencrypted OpenSSH private key entirely in process. `generate = true` uses Ed25519, the sensible default for new SSH keys:

```toml
[profiles.default]
DEPLOY_KEY = { description = "Deployment SSH key", type = "ssh_private_key", generate = true }


# RSA is available for compatibility; 3072 bits is the default.
LEGACY_DEPLOY_KEY = { description = "Legacy deployment key", type = "ssh_private_key", generate = { algorithm = "rsa", bits = 4096, comment = "deploy@example.com" } }
```

RSA sizes from 2048 through 8192 bits are accepted. `bits` is invalid with Ed25519. `comment` is optional and cannot contain control characters. Generated keys are not passphrase-encrypted, so store them in an encrypted provider. Set `as_path = true` when a command needs the key in a temporary file rather than in an environment variable.

## How it works

* Generation only triggers when a secret is **missing**. Existing secrets are never overwritten.
* Generated values are stored via the secret’s configured provider (or the default provider).
* Subsequent runs find the stored value and skip generation (idempotent).
* The `null` provider (0.19+) instead returns a fresh generated value for only the current resolution.
* `generate` and `default` cannot both be set on the same secret.
* Setting `type` without `generate` is informational only and does not trigger auto-generation.

## Ephemeral generation with null

**New in version 0.19**

Use `providers = ["null"]` when the value should be generated on demand and never written to provider storage:

```toml
[profiles.default]
SESSION_SECRET = { description = "Per-run session secret", type = "base64", generate = { bytes = 32 }, providers = ["null"] }
```

One materializing resolution receives one value. The next `run`, `get`, `check`, or SDK value-carrying resolution receives a new one. Value-free reports describe the value as generated without minting it. Use a writable provider instead when another process or later invocation must retrieve the same value.

## Example

```toml
[profiles.default]
# Auto-generated on first run, reused after that
DB_PASSWORD = { description = "Database password", type = "password", generate = true }


# Custom length and character set
ADMIN_PASSWORD = { description = "Admin password", type = "password", generate = { length = 64, charset = "ascii" } }


# 64-byte key encoded as base64
ENCRYPTION_KEY = { description = "Encryption key", type = "base64", generate = { bytes = 64 } }


# RSA private key (default 2048-bit)
JWT_SIGNING_KEY = { description = "JWT signing key", type = "rsa_private_key", generate = true }


# RSA private key with custom key size
TLS_KEY = { description = "TLS private key", type = "rsa_private_key", generate = { bits = 4096 } }


# OpenPGP signing key (requires SecretSpec 0.21+)
RELEASE_KEY = { description = "Release signing key", type = "openpgp_private_key", generate = { user_id = "Release Bot <releases@example.com>", capabilities = ["sign"] } }


# OpenSSH Ed25519 private key (requires SecretSpec 0.21+)
DEPLOY_KEY = { description = "Deployment SSH key", type = "ssh_private_key", generate = true }


# Informational type only, no generation
EXTERNAL_API_KEY = { description = "Provided by vendor", type = "password" }
```

See the [configuration reference](/reference/configuration/#secret-generation) for the full specification.