Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
17ba5d9
feat(bitwarden): add Password Manager provider with CLI integration
ashebanow Jul 23, 2025
731a77d
refactor(bitwarden): strip Secrets Manager support
ashebanow Jul 17, 2026
8db9e07
refactor(bitwarden): port to ProviderUrl/Address API
ashebanow Jul 17, 2026
dd9672b
feat(bitwarden): register Password Manager provider
ashebanow Jul 17, 2026
d309999
docs(bitwarden): remove BWS artifacts from provider docs
ashebanow Jul 17, 2026
c59a8f1
chore: ignore .pi/ and apply clippy collapsible_if fix
ashebanow Jul 17, 2026
3840a50
test(bitwarden): auto-create test items in integration script
ashebanow Jul 17, 2026
caece11
test(bitwarden): use valid identifier keys with ref mapping
ashebanow Jul 17, 2026
391cc43
test(bitwarden): fix tets → test typo in expected output
ashebanow Jul 17, 2026
3ef4115
chore: add bitwarden-cli to devenv packages for integration testing
ashebanow Jul 17, 2026
0237708
refactor(bitwarden): remove remaining debug eprintln
ashebanow Jul 17, 2026
e092416
docs: note vaultwarden path for automating bitwarden integration test
ashebanow Jul 17, 2026
6c0b679
docs: move bitwarden changelog entry under Unreleased
ashebanow Jul 17, 2026
56eceff
refactor(bitwarden): rename provider id to bw
ashebanow Jul 18, 2026
92ba652
docs(bw): complete provider listings
ashebanow Jul 18, 2026
9c2890c
refactor(bw): pass --nointeraction to all bw CLI invocations
ashebanow Jul 18, 2026
31961a4
test: fully-automated integration run against disposable Vaultwarden
bsorescu Jul 18, 2026
0cfcfce
Merge upstream/main into bitwarden-provider
ashebanow Jul 22, 2026
5b3f13e
test: regression tests for the vault-reproducible PR review findings
bsorescu Jul 23, 2026
857e32d
Merge pull request #2 from bsorescu/feat/vaultwarden-ci-harness
ashebanow Jul 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ target
/secretspec-dotnet/**/obj/

.idea/

.pi/
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- Bitwarden Password Manager provider. `bw://` uses the `bw` CLI for
vault-wide secret storage across all Bitwarden item types.
- The `required` field accepts `at_least_one` and `exactly_one` group tables,
supporting overlapping alternative and mutually exclusive credentials
across `check`, `run`, and SDK resolution.
Expand Down Expand Up @@ -51,6 +53,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [0.16.0] - 2026-07-17

### Added
>>>>>>> upstream/main

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 — Remove the unresolved changelog merge marker

The literal >>>>>>> upstream/main is a merge artifact and will be published as part of the 0.16 changelog unless removed.

- Composed secrets derive read-only values such as connection strings from
other declared secrets using strict `${UPPERCASE_NAME}` templates; names must
match `[A-Z][A-Z0-9_]*`, `$$` produces a literal dollar sign, and ordinary
Expand Down Expand Up @@ -86,6 +89,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [0.15.0] - 2026-07-16

### Added

- Gopass provider (`gopass://`) for GPG-based password manager with git-synced password store.
- `secretspec export` command that resolves every secret for the active profile
and writes them to stdout without running a command, in a chosen `--format`:
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ linkme = "0.3"
jiff = "0.2"
secrecy = { version = "0.10.3", features = ["serde"] }
bitwarden = { version = "2.0.0", features = ["secrets"] }
base64 = "0.22"
rustls = { version = "0.23", default-features = false, features = ["aws_lc_rs"] }
google-cloud-secretmanager-v1 = "1.2"
aws-config = "1"
Expand Down
2 changes: 2 additions & 0 deletions devenv.nix
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
pkgs.cargo-tarpaulin
# installers
pkgs.cargo-dist
# bitwarden-cli for integration testing
pkgs.bitwarden-cli
# Building the secretspec-php-native extension (ext-php-rs) needs php-config +
# the PHP dev headers, and bindgenHook wires libclang/clang system headers so
# ext-php-rs's bindgen step can parse php.h.
Expand Down
6 changes: 5 additions & 1 deletion docs/astro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ $ secretspec import dotenv://.env.production

## Providers

Secrets can be stored in: keyring (default), dotenv files, environment variables, 1Password, Gopass (0.15+), LastPass, Pass, Proton Pass, Google Cloud Secret Manager, AWS Secrets Manager, HashiCorp Vault, OpenBao (0.17+), Bitwarden Secrets Manager, Azure Key Vault, or Infisical (0.16+).`,
Secrets can be stored in: keyring (default), dotenv files, environment variables, 1Password, Gopass (0.15+), LastPass, Pass, Proton Pass, Google Cloud Secret Manager, AWS Secrets Manager, HashiCorp Vault, OpenBao (0.17+), Bitwarden Password Manager (0.16+), Bitwarden Secrets Manager, Azure Key Vault, or Infisical (0.16+).`,
}),
],
title: "SecretSpec",
Expand Down Expand Up @@ -214,6 +214,10 @@ Secrets can be stored in: keyring (default), dotenv files, environment variables
label: "Vault",
slug: "providers/vault",
},
{
label: "Bitwarden Password Manager (0.16+)",
slug: "providers/bw",
},
{
label: "OpenBao",
slug: "providers/openbao",
Expand Down
1 change: 1 addition & 0 deletions docs/src/content/docs/concepts/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ DATABASE_URL = { description = "Production database", providers = ["prod_vault"]
| [awssm](/providers/awssm/) | AWS Secrets Manager (requires the `awssm` build feature) ||||
| [vault](/providers/vault/) | HashiCorp Vault (requires the `vault` build feature) ||||
| [openbao](/providers/openbao/) (0.17+) | OpenBao (requires the `openbao` build feature; 0.16 uses `openbao://` through `vault`) ||||
| [bw](/providers/bw/) (0.16+) | Bitwarden Password Manager via the `bw` CLI (requires the `bw` build feature) ||||
| [bws](/providers/bws/) | Bitwarden Secrets Manager (requires the `bws` build feature) ||||
| [akv](/providers/akv/) | Azure Key Vault (requires the `akv` build feature) ||||
| [infisical](/providers/infisical/) (0.16+) | Infisical (requires the `infisical` build feature) ||||
Expand Down
200 changes: 200 additions & 0 deletions docs/src/content/docs/providers/bw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
---
title: Bitwarden Password Manager Provider
description: Bitwarden Password Manager secrets management integration
---

The `bw` provider integrates with Bitwarden Password Manager for secret management with vault-wide access to all item types.

:::note[Version compatibility]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 — Advertise the actual target release everywhere

The base already contains the released 0.16.0 section dated 2026-07-17, so this unreleased provider cannot be described as an upcoming 0.16 feature. Please update every new listing to the actual target release, including the homepage entries that currently omit a version entirely; otherwise users of 0.16 are told the unavailable provider is supported.

The Bitwarden Password Manager provider is an upcoming SecretSpec 0.16 feature and is not
available in SecretSpec 0.15.
:::

## Prerequisites

- Bitwarden CLI (`bw`)
- Bitwarden account
- Signed in via `bw login` and unlocked with `bw unlock`
- `BW_SESSION` environment variable set

## Configuration

### URI Format

#### Password Manager URIs
```
bw://[collection-id]
bw://[org@collection]
bw://?server=https://vault.company.com
bw://?type=login&field=password
```

- `collection-id`: Target collection ID
- `org@collection`: Organization and collection specification
- `type`: Item type (login, card, identity, sshkey, securenote)
- `field`: Specific field to extract

### Examples

```bash
# Password Manager - Personal vault
$ secretspec set API_KEY --provider bw://

# Password Manager - Organization collection
$ secretspec set DATABASE_URL --provider "bw://myorg@dev-secrets"

# Password Manager - Self-hosted instance
$ secretspec set TOKEN --provider "bw://?server=https://vault.company.com"

# Password Manager - Specific item type and field
$ secretspec get 'MyApp Database' --provider 'bw://?type=login&field=username'
```

## Usage

### Basic Commands

```bash
# Set a secret (Password Manager)
$ secretspec set DATABASE_URL
Enter value for DATABASE_URL: postgresql://localhost/mydb
✓ Secret DATABASE_URL saved to Bitwarden

# Get a secret from existing vault item
$ secretspec get 'MyApp Database' --provider 'bw://?type=login'

# Run with secrets
$ secretspec run -- npm start
```

### Item Type Configuration

The Bitwarden provider supports all Bitwarden item types with smart field detection:

#### Login Items (Default)
```bash
# Get password field (default)
$ secretspec get 'Database Login' --provider 'bw://?type=login'

# Get username field
$ secretspec get 'Database Login' --provider 'bw://?type=login&field=username'

# Get custom field
$ secretspec get 'API Service' --provider 'bw://?type=login&field=api_key'
```

#### Credit Card Items
```bash
# Get API key from custom field (field required)
$ secretspec get 'Stripe Payment' --provider 'bw://?type=card&field=api_key'

# Get card number
$ secretspec get 'Company Card' --provider 'bw://?type=card&field=number'
```

#### SSH Key Items
```bash
# Get private key (default)
$ secretspec get 'Deploy Key' --provider 'bw://?type=sshkey'

# Get passphrase
$ secretspec get 'Deploy Key' --provider 'bw://?type=sshkey&field=passphrase'
```

#### Identity Items
```bash
# Get custom field (field required)
$ secretspec get 'Employee Record' --provider 'bw://?type=identity&field=employee_id'

# Get email field
$ secretspec get 'Personal Identity' --provider 'bw://?type=identity&field=email'
```

#### Secure Note Items
```bash
# Get value from secure note
$ secretspec get 'Legacy Config' --provider 'bw://?type=securenote&field=config_value'
```

### Profile Configuration

```toml
# secretspec.toml
[development]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 — Use valid profile configuration in the provider guide

These tables do not match the project configuration schema: profiles are under [profiles.<name>], and profile provider selection is configured through the profile’s defaults.providers alias list rather than a provider string. Users following this example will get an invalid or misinterpreted secretspec.toml instead of collection-specific profiles.

provider = "bw://dev-secrets"

[production]
provider = "bw://prod-secrets"

```

### Environment Variables

#### Authentication
```bash
# Password Manager session
$ export BW_SESSION="your-session-key"
```

#### Configuration Defaults
```bash
# Set item type and field defaults
$ export BITWARDEN_DEFAULT_TYPE=login
$ export BITWARDEN_DEFAULT_FIELD=password

# Organization settings
$ export BITWARDEN_ORGANIZATION=myorg
$ export BITWARDEN_COLLECTION=dev-secrets

# Use defaults
$ secretspec get DATABASE_PASSWORD --provider bw://
```

### CI/CD Integration

#### Password Manager with Session Key
```bash
# Login and unlock (interactive)
$ bw login
$ bw unlock

# Export session for automation
$ export BW_SESSION="session-key-from-unlock"

# Use in CI/CD
$ secretspec run --provider bw://Production -- deploy
```

### Field Requirements by Item Type

| Item Type | Default Field | Field Required? | Notes |
|--------------|----------------|-----------------|--------------------------|
| Login | `password` | No | Falls back to username |
| SSH Key | `private_key` | No | Standard SSH key field |
| Card | None | **YES** | Must specify field |
| Identity | None | **YES** | Must specify field |
| Secure Note | Smart detect | No | Uses note content/fields |

## Error Handling

The provider includes comprehensive error handling with helpful guidance:

### CLI Installation
```
Bitwarden CLI (bw) is not installed.

To install it:
- npm: npm install -g @bitwarden/cli
- Homebrew: brew install bitwarden-cli
- Download: https://bitwarden.com/help/cli/
```

### Authentication Issues
- Clear distinction between "not logged in" vs "vault locked"
- Step-by-step guidance for `bw login` and `bw unlock`
- Session key setup instructions

### Item Access
- Graceful handling of missing items
- Field validation and suggestions
- Organization/collection permission guidance
1 change: 1 addition & 0 deletions docs/src/content/docs/quick-start.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ $ secretspec config init
awssm: AWS Secrets Manager
vault: HashiCorp Vault secret management
openbao: OpenBao secret management (0.17+)
bw: Bitwarden Password Manager (0.16+)
bws: Bitwarden Secrets Manager
akv: Azure Key Vault
infisical: Infisical secret management (0.16+)
Expand Down
17 changes: 17 additions & 0 deletions docs/src/content/docs/reference/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,22 @@ openbao://127.0.0.1:8200/secret?kv=1&tls=false
**Prerequisites**: OpenBao server, authentication credentials, build with `--features openbao` (0.17+)
**Storage**: KV path `secretspec/{project}/{profile}/{key}` with a `value` field

## Bitwarden Password Manager Provider (0.16+)

**URI**: `bw://[COLLECTION_ID]` - Stores secrets in a Bitwarden Password Manager vault via the `bw` CLI

```bash
bw:// # Personal vault
bw://collection-id # Specific collection
bw://myorg@dev-secrets # Organization collection
bw://?server=https://vault.company.com # Self-hosted server
bw://?type=login&field=username # Default item type and field
```

**Features**: Read/write, all vault item types (logins, cards, identities, SSH keys, secure notes), field selection, `ref = { item, field }` mapping in `secretspec.toml`
**Prerequisites**: Bitwarden CLI (`bw`), signed in and unlocked (`BW_SESSION` env var), build with `--features bw`
**Storage**: One vault item per secret; reads use per-type default fields unless `?field=` or a `ref` mapping selects one

## Bitwarden Secrets Manager Provider

**URI**: `bws://[SERVER_BASE@]PROJECT_UUID` - Stores secrets in Bitwarden Secrets Manager
Expand Down Expand Up @@ -290,6 +306,7 @@ export SECRETSPEC_PROVIDER="dotenv:///config/.env"
| AWSSM | ✅ AWS KMS | Cloud (AWS) | ✅ Yes |
| Vault | ✅ Vault encryption | Vault server | ✅ Yes |
| OpenBao (0.17+) | ✅ OpenBao encryption | OpenBao server | ✅ Yes |
| BW (0.16+) | ✅ End-to-end | Cloud (Bitwarden) or self-hosted | ✅ Yes |
| BWS | ✅ End-to-end | Cloud (Bitwarden) | ✅ Yes |
| AKV | ✅ Azure-managed | Cloud (Azure) | ✅ Yes |
| Infisical | ✅ Infisical-managed | Cloud (Infisical) or self-hosted | ✅ Yes |
5 changes: 4 additions & 1 deletion docs/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ const providerMetadata: ProviderMetadata[] = [
{ icon: 'apple', slug: 'keyring', label: 'macOS Keychain' },
{ icon: '1password', slug: 'onepassword', label: '1Password' },
{ icon: 'lastpass', slug: 'lastpass', label: 'LastPass' },
{ icon: 'bitwarden', slug: 'bws', label: 'Bitwarden' },
{ icon: 'bitwarden', slug: 'bw', label: 'Bitwarden Password Manager' },
{ icon: 'bitwarden', slug: 'bws', label: 'Bitwarden Secrets Manager' },
{ icon: 'vault', slug: 'vault', label: 'Vault' },
{ slug: 'openbao', label: 'OpenBao (0.17+)' },
{ slug: 'awssm', label: 'AWS Secrets Manager' },
Expand Down Expand Up @@ -141,6 +142,7 @@ $ secretspec init --from dotenv
$ secretspec config init
? Select your preferred provider backend:
<span class="tok-pun">›</span> keyring: Uses system keychain (Recommended)
bw: Bitwarden Password Manager
infisical: Infisical secret management
openbao: OpenBao secret management (0.17+)
<span class="tok-str">✓</span> Saved to ~/.config/secretspec/config.toml
Expand Down Expand Up @@ -209,6 +211,7 @@ $ secretspec run --profile production -- npm start
<pre is:raw class="landing-step-code"><code>$ secretspec config init
? Select your preferred provider backend:
> keyring: Uses system keychain (Recommended)
bw: Bitwarden Password Manager
infisical: Infisical secret management
openbao: OpenBao secret management (0.17+)</code></pre>
</div>
Expand Down
4 changes: 3 additions & 1 deletion secretspec/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ whoami = { workspace = true, optional = true }
linkme.workspace = true
jiff.workspace = true
secrecy.workspace = true
base64 = { workspace = true, optional = true }
bitwarden = { workspace = true, optional = true }
rustls = { workspace = true, optional = true }
google-cloud-secretmanager-v1 = { workspace = true, optional = true }
Expand All @@ -52,7 +53,7 @@ data-encoding.workspace = true
detect-coding-agent.workspace = true

[features]
default = ["cli", "keyring", "gcsm", "awssm", "vault", "openbao", "bws", "akv", "infisical"]
default = ["cli", "keyring", "gcsm", "awssm", "vault", "openbao", "bws", "akv", "infisical", "bw"]
cli = ["dep:toml_edit"]
keyring = ["dep:keyring", "dep:whoami"]
# Compile libdbus from source into the binary instead of linking the system
Expand All @@ -68,4 +69,5 @@ openbao = ["dep:reqwest"]
# name the features it uses rather than inherit them from a sibling.
infisical = ["dep:reqwest", "tokio/sync"]
bws = ["dep:bitwarden", "dep:rustls"]
bw = ["dep:base64"]
akv = ["dep:azure_core", "dep:azure_identity", "dep:azure_security_keyvault_secrets"]
3 changes: 3 additions & 0 deletions secretspec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ SecretSpec fixes this by separating secret **declaration** from secret **storage
- [AWS Secrets Manager](https://secretspec.dev/providers/awssm)
- [Vault](https://secretspec.dev/providers/vault)
- [OpenBao](https://secretspec.dev/providers/openbao) (0.17+)
- [Bitwarden Password Manager](https://secretspec.dev/providers/bw) (0.16+)
- [Bitwarden Secrets Manager](https://secretspec.dev/providers/bws)
- [Azure Key Vault](https://secretspec.dev/providers/akv)
- [Infisical](https://secretspec.dev/providers/infisical) (0.16+)
Expand Down Expand Up @@ -67,6 +68,7 @@ $ secretspec config init
awssm: AWS Secrets Manager
vault: HashiCorp Vault secret management
openbao: OpenBao secret management (0.17+)
bw: Bitwarden Password Manager (0.16+)
bws: Bitwarden Secrets Manager
akv: Azure Key Vault
infisical: Infisical secret management (0.16+)
Expand Down Expand Up @@ -153,6 +155,7 @@ SecretSpec supports multiple storage backends for secrets:
- **[AWS Secrets Manager](https://secretspec.dev/providers/awssm)** - AWS secret management
- **[Vault](https://secretspec.dev/providers/vault)** - HashiCorp Vault KV engine
- **[OpenBao](https://secretspec.dev/providers/openbao)** (0.17+) - OpenBao KV integration; SecretSpec 0.16 accepts `openbao://` through the Vault provider
- **[Bitwarden Password Manager](https://secretspec.dev/providers/bw)** (0.16+) - Bitwarden Password Manager vault via the `bw` CLI
- **[Bitwarden Secrets Manager](https://secretspec.dev/providers/bws)** - Bitwarden Secrets Manager integration
- **[Azure Key Vault](https://secretspec.dev/providers/akv)** - Azure secret management
- **[Infisical](https://secretspec.dev/providers/infisical)** (0.16+) - Infisical secret management
Expand Down
Loading
Loading