credential-bridge
A unified Python library for secrets management across HashiCorp Vault, the OS system keyring, and .env files.
Table of Contents
- Features
- Installation
- Quick Start
- CLI Usage
- Backends
- Custom Backends
- Error Handling
- Environment Variables
- Development
- Contributing
- License
Features
- Three backends: HashiCorp Vault (KV-v2), OS system keyring, and
.envfiles SecretsManagerfacade: switch backends without changing application code- Plugin architecture: register third-party backends with one call
- Typed exceptions: granular hierarchy rooted at
CredentialBridgeError - Typer + Rich CLI:
cb,vault-cli,keyring-cli,env-cli, andrun-wizardentry points - Cross-platform: works on Linux, macOS, and Windows
Installation
pip install credential-bridge
# With dev dependencies
pip install "credential-bridge[dev]"
Quick Start
Using SecretsManager
from credential_bridge import SecretsManager
# HashiCorp Vault (set VAULT_ADDR env var first)
sm = SecretsManager("vault", vault_token="s.xxx")
sm.add_secret("myapp/database", {"user": "admin", "pass": "s3cr3t"})
secret = sm.get_secret("myapp/database")
# System keyring
sm = SecretsManager("keyring", service_name="myapp")
sm.add_secret("api_key", {"api_key": "sk-abc123"})
# .env file
sm = SecretsManager("env", path=".env")
sm.add_secret("DATABASE", {"DB_HOST": "localhost", "DB_PORT": "5432"})
Using backends directly
from credential_bridge import VaultBackend, KeyringBackend, EnvFileBackend
# Vault with AppRole auth
vault = VaultBackend(
vault_url="https://vault.example.com",
vault_role_id="<role-id>",
vault_secret_id="<secret-id>",
)
vault.add_secret("myapp/db", {"password": "hunter2"})
# Keyring
kr = KeyringBackend(service_name="myapp")
kr.add_secret("token", {"value": "sk-abc123"})
# .env file
env = EnvFileBackend(path=".env")
env.add_secret("DATABASE", {"DB_HOST": "localhost", "DB_PORT": "5432"})
CLI Usage
# Show all commands
cb --help
# Vault
cb vault add myapp/db --secret user=admin --secret pass=s3cr3t
cb vault get myapp/db
cb vault list
cb vault delete myapp/db
# Keyring
cb keyring add api_key --secret api_key=sk-abc123 --service-name myapp
cb keyring get api_key --service-name myapp
cb keyring delete api_key --service-name myapp
# .env file
cb env add DATABASE --secret DB_HOST=localhost --secret DB_PORT=5432
cb env get DB_HOST
cb env list
cb env delete DB_HOST
# Interactive wizard
cb wizard
Backends
| Backend | Best use case |
|---|---|
| Vault | Production workloads requiring audit logs, dynamic secrets, fine-grained policies, and centralized governance |
| Keyring | Developer machines and CI environments where OS-level credential storage is available |
| .env file | Local development, Docker Compose setups, and twelve-factor apps that read config from the environment |
Custom Backends
Implement BaseSecretBackend and register it with SecretsManager:
from credential_bridge import BaseSecretBackend, SecretsManager
from credential_bridge.manager import register_backend
from typing import Any, Dict, List
class RedisBackend(BaseSecretBackend):
backend_name = "redis" # required — omitting raises TypeError
def add_secret(self, name: str, secret: Dict[str, Any]) -> None: ...
def get_secret(self, name: str) -> Dict[str, Any]: ...
def update_secret(self, name: str, secret: Dict[str, Any]) -> None: ...
def delete_secret(self, name: str) -> None: ...
def list_secrets(self, path: str = "") -> List[str]: ...
register_backend("redis", RedisBackend)
sm = SecretsManager("redis", host="localhost", port=6379)
Error Handling
All exceptions inherit from CredentialBridgeError:
CredentialBridgeError
├── BackendError
│ ├── VaultError
│ │ ├── VaultAuthError — bad token / AppRole credentials
│ │ ├── VaultConnectionError — unreachable server
│ │ └── VaultSecretNotFoundError — secret path does not exist
│ ├── KeyringError
│ └── EnvFileError
│ ├── EnvFileNotFoundError — key not found
│ └── EnvFileKeyExistsError — add_secret called on existing key
├── BackendNotRegisteredError
└── ConfigurationError
from credential_bridge import (
SecretsManager,
VaultAuthError,
VaultConnectionError,
VaultSecretNotFoundError,
CredentialBridgeError,
)
sm = SecretsManager("vault", vault_token="s.xxx")
try:
secret = sm.get_secret("myapp/database")
except VaultSecretNotFoundError:
print("Secret path does not exist")
except VaultAuthError:
print("Authentication failed — check your token or AppRole credentials")
except VaultConnectionError:
print("Could not reach Vault — check VAULT_ADDR")
except CredentialBridgeError as exc:
print(f"Unexpected error: {exc}")
Environment Variables
| Variable | Description |
|---|---|
VAULT_ADDR |
Vault server URL (e.g. https://vault.example.com) |
VAULT_TOKEN |
Token for Token auth method |
VAULT_ROLE_ID |
Role ID for AppRole auth method |
VAULT_SECRET_ID |
Secret ID for AppRole auth method |
Resolution order for VaultBackend: constructor argument → environment variable → ~/.vault_config.json.
Development
# Install in editable mode with dev dependencies
pip install -e ".[dev]"
# Run unit tests
pytest tests/unit/
# Run integration tests (requires external services)
pytest tests/integration/ -m integration
# Lint
ruff check src/
# Type check
mypy src/
# Serve docs locally
mkdocs serve
CI Pipeline
Every push to main and every pull request runs automatically via shared-workflows:
| Job | What it checks |
|---|---|
| Test | pytest on Python 3.8 / 3.10 / 3.12 x Ubuntu + Windows (runs after lint and typecheck pass) |
| Lint | ruff check + ruff format --check |
| Type Check | mypy src/ |
| Audit | pip-audit — all dependencies scanned for known CVEs |
| Coverage | pytest-cov — report posted to the Actions job summary |
Contributing
All contributions are welcome! Fork the repo, make your changes, and open a pull request. You can also open an issue with the label enhancement.
License
MIT — see LICENSE.txt for details.
Metadata
Release files for credential-bridge 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| credential_bridge-0.4.1.tar.gz | 1.1 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| credential_bridge-0.4.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.1 MB
Release files / credential_bridge-0.4.1.tar.gz
| Download URL | credential_bridge-0.4.1.tar.gz |
|---|---|
| Size | 1.1 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
aacae88c4172e602cdf6d9cc03161c6ddbd152eeacefce0dd20066fb6fef3f31
|
|
BLAKE2b-256 checksum How to use checksums |
cadaac7c29dc10f511c3eb3dfe3b4e5b13a3e6b7058a1b5e539362e04ccc095b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.15
|
Release files / credential_bridge-0.4.1-py3-none-any.whl
| Download URL | credential_bridge-0.4.1-py3-none-any.whl |
|---|---|
| Size | 36.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b57bb1ec90060b7f2ac201c03d43861d4f06a5397437e4841b1ecbf52134d1ea
|
|
BLAKE2b-256 checksum How to use checksums |
70c173c113668111b32c279be925ca1b32335d12504f35aabba993e536976e19
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.15
|