Skip to content

Commit 6df4b7b

Browse files
domenkozarclaude
andcommitted
feat: add per-secret provider configuration with fallback chains
Add support for configuring different providers for individual secrets, enabling fallback chains where secrets can be retrieved from multiple providers in order of preference. New features: - Per-secret `providers` field in configuration (list of provider aliases) - Provider alias management in global config (~/.config/secretspec/config.toml) - CLI commands to manage aliases: config provider add/remove/list - Fallback chain resolution for secure multi-provider setups This allows complex scenarios like: - Try OnePassword vault first, fall back to keyring - Different providers for different environments - Shared vs environment-specific provider locations Implementation includes: - New resolve_provider_aliases() method for alias → URI conversion - New get_secret_from_providers() for fallback chain resolution - Updated all secret operations (get, set, validate, import, run) - CLI provider alias management commands - Comprehensive unit and integration tests: * test_per_secret_provider_configuration * test_provider_alias_resolution * test_provider_alias_not_found * test_per_secret_provider_with_fallback_chain * test_get_secret_with_fallback_chain (new integration test) * test_validate_with_per_secret_providers (new integration test) * test_secret_config_merges_providers_from_default (new integration test) * test_cli_provider_alias_operations (new integration test) All tests pass (63 tests in secretspec, 19 in secretspec-derive). Fixes #10 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 8bce286 commit 6df4b7b

File tree

11 files changed

+1222
-47
lines changed

11 files changed

+1222
-47
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
- Per-secret provider configuration: secrets can now specify their own provider(s) with fallback chains
12+
- New `providers` field in secret configuration (list of provider aliases tried in order)
13+
- Provider alias management via `secretspec config provider add/remove/list` commands
14+
- New `providers` map in global config for defining named provider aliases
15+
16+
### Changed
17+
- Secret configuration now supports `providers: [...]` field instead of single provider assignment
18+
- Provider resolution includes per-secret provider overrides before falling back to global defaults
19+
1020
## [0.3.3] - 2025-09-10
1121

1222
### Fixed

CLAUDE.md

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,46 @@ Providers support URI-based configuration (e.g., `keyring://`, `onepassword://va
6666
4. Falls back to "default" profile
6767

6868
### Provider Resolution
69-
1. CLI flag (`--provider`)
70-
2. Environment variable (`SECRETSPEC_PROVIDER`)
71-
3. User config default per profile
72-
4. Falls back to keyring provider
69+
1. **Per-secret providers** (with fallback chain): specified in `secretspec.toml` as `providers: [alias1, alias2, ...]`
70+
- Aliases resolved against `~/.config/secretspec/config.toml` providers map
71+
- Tries each provider in order until secret is found
72+
2. CLI flag (`--provider`)
73+
3. Environment variable (`SECRETSPEC_PROVIDER`)
74+
4. User config default provider
75+
5. Falls back to keyring provider
76+
77+
### Per-Secret Provider Configuration
78+
Secrets can specify their own providers using the `providers` field to override global defaults:
79+
80+
```toml
81+
[profiles.production]
82+
DATABASE_URL = { description = "Production DB", providers = ["prod_vault", "keyring"] }
83+
API_KEY = { description = "API Key", providers = ["shared"] }
84+
GITHUB_TOKEN = { description = "GitHub token from env", providers = ["env"] }
85+
```
86+
87+
Provider aliases are defined in `~/.config/secretspec/config.toml`:
88+
```toml
89+
[defaults]
90+
provider = "keyring"
91+
92+
[providers]
93+
prod_vault = "onepassword://vault/Production"
94+
shared = "onepassword://vault/Shared"
95+
env = "env://"
96+
```
97+
98+
Or managed via CLI:
99+
```bash
100+
# Add provider alias
101+
secretspec config provider add prod_vault "onepassword://vault/Production"
102+
103+
# List all aliases
104+
secretspec config provider list
105+
106+
# Remove alias
107+
secretspec config provider remove prod_vault
108+
```
73109

74110
### Secret Resolution
75111
1. Check active profile for secret

docs/src/content/docs/concepts/providers.md

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ Providers are pluggable storage backends that handle the storage and retrieval o
1919

2020
SecretSpec determines which provider to use in this order:
2121

22-
1. **CLI flag**: `secretspec --provider` flag
23-
2. **Environment**: `SECRETSPEC_PROVIDER` (highest priority)
24-
3. **Global default**: Default provider in user config set via `secretspec config init`
22+
1. **Per-secret providers**: `providers` field in `secretspec.toml` (highest priority, with fallback chain)
23+
2. **CLI flag**: `secretspec --provider` flag
24+
3. **Environment**: `SECRETSPEC_PROVIDER`
25+
4. **Global default**: Default provider in user config set via `secretspec config init`
2526

2627
## Configuration
2728

@@ -61,6 +62,59 @@ $ secretspec run --provider "onepassword://Personal/Development" -- npm start
6162
$ secretspec run --provider "dotenv:/home/user/work/.env" -- npm test
6263
```
6364

65+
## Per-Secret Provider Configuration
66+
67+
For fine-grained control, you can specify different providers for individual secrets using the `providers` field in `secretspec.toml`. This enables fallback chains where secrets are retrieved from multiple providers in order of preference:
68+
69+
```toml
70+
[profiles.production]
71+
DATABASE_URL = { description = "Production DB", providers = ["prod_vault", "keyring"] }
72+
API_KEY = { description = "API key from env", providers = ["env"] }
73+
SENTRY_DSN = { description = "Error tracking", providers = ["shared_vault", "keyring"] }
74+
```
75+
76+
Provider aliases are defined in your user configuration file (`~/.config/secretspec/config.toml`):
77+
78+
```toml
79+
[defaults]
80+
provider = "keyring"
81+
82+
[providers]
83+
prod_vault = "onepassword://vault/Production"
84+
shared_vault = "onepassword://vault/Shared"
85+
env = "env://"
86+
```
87+
88+
### Fallback Chains
89+
90+
When a secret specifies multiple providers, SecretSpec tries each provider in order until it finds the secret:
91+
92+
```toml
93+
# Try OnePassword first, then fall back to keyring if not found
94+
DATABASE_URL = { description = "DB", providers = ["prod_vault", "keyring"] }
95+
```
96+
97+
This enables complex workflows:
98+
- **Shared vs environment-specific**: Try a shared vault first, fall back to local keyring
99+
- **Redundancy**: Maintain secrets in multiple locations for backup
100+
- **Migration**: Gradually move secrets from one provider to another
101+
- **Multi-team setups**: Different teams can manage different providers
102+
103+
### Managing Provider Aliases
104+
105+
Use CLI commands to manage provider aliases:
106+
107+
```bash
108+
# Add a provider alias
109+
$ secretspec config provider add prod_vault "onepassword://vault/Production"
110+
111+
# List all aliases
112+
$ secretspec config provider list
113+
114+
# Remove an alias
115+
$ secretspec config provider remove prod_vault
116+
```
117+
64118
## Next Steps
65119

66120
- Learn about specific providers in the [Providers](/providers/keyring/) section

docs/src/content/docs/reference/cli.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,57 @@ Provider: keyring
5454
Profile: development
5555
```
5656

57+
### config provider add
58+
Add a provider alias to your configuration.
59+
60+
```bash
61+
secretspec config provider add <ALIAS> <URI>
62+
```
63+
64+
**Arguments:**
65+
- `<ALIAS>` - Short name for the provider (e.g., `prod_vault`, `shared`)
66+
- `<URI>` - Provider URI (e.g., `onepassword://vault/Production`, `env://`)
67+
68+
**Example:**
69+
```bash
70+
$ secretspec config provider add prod_vault "onepassword://vault/Production"
71+
✓ Provider alias 'prod_vault' saved
72+
73+
$ secretspec config provider add shared "onepassword://vault/Shared"
74+
✓ Provider alias 'shared' saved
75+
```
76+
77+
### config provider list
78+
List all configured provider aliases.
79+
80+
```bash
81+
secretspec config provider list
82+
```
83+
84+
**Example:**
85+
```bash
86+
$ secretspec config provider list
87+
prod_vault → onepassword://vault/Production
88+
shared → onepassword://vault/Shared
89+
env → env://
90+
```
91+
92+
### config provider remove
93+
Remove a provider alias from your configuration.
94+
95+
```bash
96+
secretspec config provider remove <ALIAS>
97+
```
98+
99+
**Arguments:**
100+
- `<ALIAS>` - Name of the alias to remove
101+
102+
**Example:**
103+
```bash
104+
$ secretspec config provider remove prod_vault
105+
✓ Provider alias 'prod_vault' removed
106+
```
107+
57108
### check
58109
Check if all required secrets are available, with interactive prompting for missing secrets.
59110

docs/src/content/docs/reference/configuration.md

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ Each secret variable is defined as a table with the following fields:
4545
| `description` | string | Yes | Human-readable description of the secret |
4646
| `required` | boolean | No* | Whether the value must be provided (default: true) |
4747
| `default` | string | No** | Default value if not provided |
48+
| `providers` | array[string] | No | List of provider aliases to use in fallback order |
4849

49-
*If `default` is provided, `required` defaults to false
50+
*If `default` is provided, `required` defaults to false
5051
**Only valid when `required = false`
5152

5253
## Complete Example
@@ -62,6 +63,7 @@ extends = ["../shared/secretspec.toml"] # Optional inheritance
6263
[profiles.default]
6364
APP_NAME = { description = "Application name", required = false, default = "MyApp" }
6465
LOG_LEVEL = { description = "Log verbosity", required = false, default = "info" }
66+
GITHUB_TOKEN = { description = "GitHub token", required = true, providers = ["env"] }
6567

6668
# Development profile - extends default
6769
[profiles.development]
@@ -71,12 +73,39 @@ DEBUG = { description = "Debug mode", required = false, default = "true" }
7173

7274
# Production profile - extends default
7375
[profiles.production]
74-
DATABASE_URL = { description = "PostgreSQL cluster connection", required = true }
76+
DATABASE_URL = { description = "PostgreSQL cluster connection", required = true, providers = ["prod_vault", "keyring"] }
7577
API_URL = { description = "Production API endpoint", required = true }
76-
SENTRY_DSN = { description = "Error tracking service", required = true }
78+
SENTRY_DSN = { description = "Error tracking service", required = true, providers = ["shared_vault"] }
7779
REDIS_URL = { description = "Redis cache connection", required = true }
7880
```
7981

82+
### Provider Aliases
83+
84+
When using per-secret provider configuration, provider aliases must be defined in your user configuration file at `~/.config/secretspec/config.toml`:
85+
86+
```toml
87+
[defaults]
88+
provider = "keyring"
89+
90+
[providers]
91+
prod_vault = "onepassword://vault/Production"
92+
shared_vault = "onepassword://vault/Shared"
93+
env = "env://"
94+
```
95+
96+
Manage provider aliases using CLI commands:
97+
98+
```bash
99+
# Add a provider alias
100+
$ secretspec config provider add prod_vault "onepassword://vault/Production"
101+
102+
# List all aliases
103+
$ secretspec config provider list
104+
105+
# Remove an alias
106+
$ secretspec config provider remove prod_vault
107+
```
108+
80109
## Profile Inheritance
81110

82111
- All profiles automatically inherit from `[profiles.default]`

0 commit comments

Comments
 (0)