A bash CLI that extends the
Bitwarden Secrets Manager
bws binary with the tooling needed to manage secrets across a
release lifecycle: bulk tagging, versioned cloning, structured
metadata, expiry tracking, and provider-driven rotation.
The official bws CLI provides single-secret CRUD. bwx adds
the operational layer on top:
- Release tags — bind secrets to deployment versions; tag or untag an entire project in one command
- Structured metadata —
file:,expires:,provider:, andrelease-tag:fields in the BWS note, parsed and validated bybwxcommands - Import — export all tagged secrets to a deployment directory with symlinks and UUID-based storage
- Expiry checking — pre-release gate that blocks deployments when credentials are near expiration
- Rotation — provider-driven rotation with 13 built-in drivers (8 fully automated, 5 interactive) covering AWS IAM, Tailscale, GitHub, Docker Hub, Grafana, Bitwarden, TLS certificates, MQTT, random passwords, and a generic prompt fallback
- Versioned cloning —
cloneincrements_v1→_v2for safe key rotation with downgrade support - Caching — TTL-based local cache for API responses; one API call per session, not per command
- Zero install dependencies — bash + Docker;
jq,bws,curl,openssl, andawsare wrapped via Docker containers when not natively installed
bwx is process-agnostic but tilted toward a specific operational
pattern:
develop → tag → import → deploy → rotate (on schedule)
↑ |
└────── check ───────┘
-
Develop — secrets are created and updated in BWS via
bwx secret create,bwx secret set value, etc. Each secret's note carriesfile:(deployment filename) andrelease-tag:entries. -
Tag — before a release,
bwx tag project 2026.07.01.01stamps every secret in the project with the release tag. Old tags are preserved for downgrade. -
Import — at deployment time,
bwx import 2026.07.01.01 .secretsexports exactly the secrets tagged for that release. Each secret becomes a file named by itsfile:property, with values stored in.by-uuid/for deduplication. -
Deploy — the application reads secrets from
.secrets/(Docker secrets, env files, mounted volumes — whatever the app expects). -
Check —
bwx check expiry --exit-on-expiringruns as a pre-release gate. Secrets with anexpires:date within the warning window (default: 14 days) block the release until rotated. -
Rotate —
bwx rotate SECRETreads theprovider:field from the secret's note and calls the matching driver. Automated providers (password-generate, aws-iam, openssl-selfsigned, mqtt-password, tailscale-oauth, bitwarden-api-key, grafana-service-account, docker-registry) run without operator input; interactive providers (github-pat, anthropic-api-key, letsencrypt-manual, tailscale-manual, prompt) guide the operator through a paste-and-validate flow. The framework updates the BWS value, sets the newexpires:date, and preserves all other metadata. Provider credentials can be stored as BWS secrets themselves (project:secretreferences), read from files, or passed via environment variables. See doc/providers.md for the full reference.
Secrets that do not expire (SSH keys, GPG passphrases) skip steps
5–6 entirely. They are tagged and imported like any other secret
but have no expires: or provider: metadata. Secrets like
database passwords that should rotate can now use the
password-generate provider for fully automated rotation.
export BWS_ACCESS_TOKEN="your-token-here"
bwx secret get value my_secret_v1 # get a value
bwx secret set value my_secret_v1 "pw" # set a value
bwx tag project 2026.07.01.01 # tag all secrets
bwx import 2026.07.01.01 .secrets # export to disk
bwx check expiry # any expiring?
bwx rotate --all # rotate what's dueFor unattended callers (cron, ssh host command, systemd), store the
token instead of exporting it — a non-interactive shell never reads
past the interactive guard in ~/.bashrc, so an exported token is
invisible to exactly the automation that needs it:
pbpaste | bwx config set bws-access-token # stored at mode 0600
bwx config list # what resolves from wherebwx secret get PROP SECRET Get a property (value, note, id, key, filename, tags, mode, ...)
bwx secret set PROP SECRET V Set a property (value, note, key, filename, mode)
bwx secret list List all secrets in a project (JSON)
bwx secret show SECRET Show full secret details (JSON)
bwx secret ls List secrets (summary format)
bwx secret create KEY VALUE Create a new secret
bwx secret clone SECRET Clone with version increment (_v1 → _v2)
bwx secret delete SECRET Delete a secret
bwx project list List all projects
bwx project show PROJECT Show project details
bwx project id PROJECT Resolve name to UUID
bwx project name PROJECT Resolve UUID to name
bwx project ls List projects (summary)
bwx project default id Default project UUID
bwx project default name Default project name
bwx tag project TAG Tag all secrets in the project
bwx tag unproject TAG Remove tag from all secrets
bwx tag add SECRET TAG Tag one secret
bwx tag remove SECRET TAG Untag one secret
bwx tag list List all tags
bwx tag secrets TAG List secrets with TAG
bwx import TAG DIR [PROJECT] Export tagged secrets to a directory
bwx check expiry [--exit-on-expiring] Pre-release expiry gate
bwx note validate SECRET Validate note fields and provider config
bwx rotate SECRET Rotate via provider driver
bwx rotate --all Rotate all expiring secrets
bwx provider info NAME Show expected config fields for a provider
bwx provider info --list List all available providers
bwx config list Show entries and which source is effective
bwx config path [NAME] Print the config directory or one file path
bwx config get NAME Print a stored value
bwx config set NAME [VALUE] Store a value (reads stdin when VALUE omitted)
bwx raw [bws-args...] Pass-through to upstream bws
bwx completion bash Bash tab completion
bwx completion zsh Zsh tab completion
Each secret's BWS note carries structured properties:
file: app-password
note: Database credential for the web service
expires: 2026-09-20
provider: prompt
release-tag: 2026.06.24.01
release-tag: 2026.07.01.01| Property | Purpose |
|---|---|
file: |
Deployment filename (used by bwx import) |
expires: |
Expiration date (checked by bwx check expiry) |
provider: |
Rotation driver (used by bwx rotate) |
release-tag: |
One or more deployment version bindings |
mode: |
Octal file mode for bwx import (e.g. 0644 for a non-root container mount); defaults to 0600 |
git clone https://github.com/1121citrus/bwx.git ~/.local/lib/bwx
export PATH="${HOME}/.local/lib/bwx/bin:${PATH}"
eval "$(bwx completion bash)"brew tap 1121citrus/bwx https://github.com/1121citrus/bwx
brew install bwxOn macOS the system shell is Bash 3.2. If installed via Homebrew, ensure the Homebrew bash is first in your PATH:
brew install bash
export PATH="$(brew --prefix)/bin:${PATH}"If you use pkgsrc/pkgin instead of Homebrew, install Bash with:
pkgin install bash
export PATH="/opt/pkg/bin:${PATH}"Then verify the shell version:
bash --versiongit clone https://github.com/1121citrus/bwx.git vendor/bwx
export PATH="${PWD}/vendor/bwx/bin:${PATH}"Pin the checkout to a tag or commit when you need reproducible vendoring.
- Bash 4.0+ (macOS ships 3.2 — install a newer shell with Homebrew or pkgin)
- Docker — used as a fallback when external tools are not natively installed
- Full subcommand reference — every command with examples and expected output
- Rotation providers — provider reference, credential passing, config fields, and input scrubbing
- Extending bwx — architecture, adding subcommands, adding note properties, writing custom providers
- Include modules — shared runtime modules used by
bin/bwx - Logging configuration — log levels, prolog controls, and status constants
- Security policy — attack surface, dependency scanning, token handling