Machine-locked encrypted secret management library with Fernet encryption
Project description
WAuth — Machine-Locked Encrypted Secret Management
Store, rotate, back up, retrieve, and verify secrets encrypted with Fernet (AES-256), backed by SQLite — tied to the machine they were created on.
Key Features
- Fernet Encryption — AES-128-CBC via the
cryptographylibrary, with a 32-byte machine-derived key (SHA-256 of salted machine ID). - SQLite-Backed Persistence — Secrets stored in a local SQLite database via
wsqlite, with automatic directory creation. - Docker Secret Support — Read Docker Swarm/Compose secrets from
/run/secretswhen running inside containers. - Dual Driver Architecture — Factory pattern automatically selects between local encrypted vault and Docker secrets based on runtime environment.
- File & Text Support — Store both plaintext secrets and encrypted files (
.pem,.key, certificates). - Key Rotation — Rotate encryption keys with automatic re-encryption of all secrets via
rotate_key(). - TTL Support — Set time-to-live on secrets for automatic expiration.
- Backup & Restore — Export and import encrypted vault snapshots.
- Async Support —
async_set(),async_get(),async_delete(),async_backup(),async_restore()for async workflows. - Secret Verification —
valid()andasync_valid()for secure secret checking without exposing the actual value. - Custom Exceptions — Structured exception hierarchy (
DecryptionError,KeyNotFoundError,VaultError, etc.). - Structured Logging — Full
loguruintegration for diagnostics. - Configuration File — TOML-based configuration support.
- Pydantic Validation — All secret models validated with Pydantic v2 for type safety.
- Code Quality — Pylint score ≥ 9.5 across all modules, 98% test coverage, Google Style Docstrings, full type hints.
- Security Audited — Bandit scan with 0 medium/high findings. See SECURITY.md
Technical Stack
| Layer | Technology |
|---|---|
| Language | Python 3.9+ |
| Encryption | Fernet (AES-128-CBC) via cryptography |
| Storage | SQLite via wsqlite (ORM-backed by Pydantic) |
| Testing | pytest, pytest-cov |
| Linting | Pylint, Ruff, Black, MyPy |
| Logging | loguru |
| Documentation | Sphinx (furo theme), reStructuredText |
| CI/CD | GitHub Actions (lint, test, security, publish) |
Installation & Setup
From PyPI
pip install wauth
From Source
git clone https://github.com/wisrovi/wauth.git
cd wauth
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev,docs]"
Dependencies
Runtime:
cryptography — Fernet encryption
wsqlite — SQLite ORM with Pydantic integration
pydantic — Data validation and serialization
loguru — Structured logging
Development:
pytest — Testing framework
pytest-cov — Coverage reporting
black — Code formatting
ruff — Fast linter
mypy — Static type checking
Documentation:
sphinx — Documentation generator
furo — Modern Sphinx theme
sphinx-copybutton — Copy code blocks
myst-parser — Markdown support
Architecture & Workflow
Directory Structure
wauth/
├── __init__.py # WAuth class, functional API, async methods
├── core.py # CryptoEngine — Fernet encryption/decryption
├── vault.py # SecretModel (Pydantic) + Vault (SQLite + TTL)
├── utils.py # Machine ID detection & key derivation
├── exceptions.py # Custom exception hierarchy
├── deprecation.py # @deprecated decorator & warn_deprecated()
├── drivers/
│ ├── __init__.py # DriverFactory — auto-selects local or Docker
│ ├── local.py # LocalDriver — encrypted local + rotation
│ └── docker.py # DockerDriver — reads /run/secrets
├── test/
│ ├── conftest.py # Shared fixtures
│ ├── test_core.py # CryptoEngine (13 tests)
│ ├── test_vault.py # Vault & SecretModel (24 tests)
│ ├── test_utils.py # Utility functions (10 tests)
│ ├── test_drivers.py # Driver tests (17 tests)
│ ├── test_wauth.py # WAuth & API (30 tests)
│ ├── test_exceptions.py # Exception hierarchy (10 tests)
│ └── test_deprecation.py # Deprecation utilities (7 tests)
├── benchmarks/
│ └── performance.py # Performance benchmark suite
├── examples/
│ └── 00 base/
│ └── example.py # Basic usage example
├── docs/ # Sphinx documentation
├── README.md # This file
├── SECURITY.md # Security policy & threat model
├── VERSIONING.md # SemVer & LTS policy
├── MIGRATION.md # Migration guides between versions
├── PYLINT_REPORT.md # Static analysis quality report
└── pyproject.toml # Build configuration, dependencies
System Workflow
Application --> WAuth.set(key, value, ttl) --> CryptoEngine (Fernet AES) --> Vault (SQLite)
Application --> WAuth.get(key) --> Vault --> CryptoEngine --> Decrypt
Docker Container: DriverFactory checks /run/secrets, falls back to LocalDriver
Encryption Flow
App:set("API_KEY", "secret")
--> WAuth
--> CryptoEngine.encrypt()
--> Derive key (machine_id or custom_key)
--> Fernet.encrypt()
--> Vault.save()
--> SQLite
File Structure
wauth/
├── __init__.py # WAuth class
├── core.py # CryptoEngine (Fernet)
├── vault.py # SecretModel + Vault
├── utils.py # Machine ID detection
├── exceptions.py # Exception hierarchy
├── drivers/
│ ├── __init__.py # DriverFactory
│ ├── local.py # LocalDriver
│ └── docker.py # DockerDriver
└── test/ # pytest suite
Encryption Flow
# set() operation:
App:set("API_KEY", "secret")
--> WAuth
--> CryptoEngine.encrypt()
--> Derive key (machine_id + salt --> SHA-256)
--> Fernet.encrypt() --> token
--> Vault.save(token, "text", ttl)
--> SQLite INSERT
# get() operation:
App:get("API_KEY")
--> Vault.get(key)
--> SQLite SELECT
--> Check TTL (delete if expired)
--> CryptoEngine.decrypt(token)
--> Return plaintext
Configuration
Environment Variables
| Variable | Description | Default |
|---|---|---|
WAUTH_DB_PATH |
Custom SQLite database path | ~/.wisrovi/wauth.db |
TOML Configuration File
Create ~/.wauth.toml:
[wauth]
db_path = "~/.wisrovi/wauth.db"
custom_key = "my-encryption-key" # Optional
Usage:
from wauth import WAuth
auth = WAuth(config_path="~/.wauth.toml")
Database Path
By default, WAuth stores secrets in ~/.wisrovi/wauth.db. To use a custom path:
from wauth import WAuth
# Custom database path
auth = WAuth(db_path="/path/to/my_secrets.db")
auth.set("MY_KEY", "my-secret")
Important Security Note
Encryption keys are derived from the machine's unique identifier. This means:
- Secrets encrypted on Machine A cannot be decrypted on Machine B.
- This is intentional — it prevents accidental secret leakage across environments.
- For cross-machine secret sharing, use Docker secrets, environment variables, or the
custom_keyparameter.
Usage
Quick Start
from wauth import WAuth
# Initialize
auth = WAuth()
# Store a secret (automatically encrypted)
auth.set("TELEGRAM_TOKEN", "7483920:ABC-DEF-GHI")
# Retrieve the secret (auto-decrypted)
token = auth.get("TELEGRAM_TOKEN")
print(token) # 7483920:ABC-DEF-GHI
Storing Files
from wauth import WAuth
auth = WAuth()
# Encrypt and store a file (e.g., TLS certificate)
auth.set_file("TLS_CERT", "/etc/ssl/certs/my-cert.pem")
# Retrieve the file contents as bytes
cert_bytes = auth.get("TLS_CERT")
with open("/tmp/restored-cert.pem", "wb") as f:
f.write(cert_bytes)
Functional API
from wauth import set, get, set_file, delete, list_keys
# No need to instantiate WAuth
set("API_KEY", "sk-12345")
value = get("API_KEY")
# Store a file
set_file("SSH_KEY", "~/.ssh/id_rsa")
key_data = get("SSH_KEY")
# Delete a secret
delete("OLD_KEY")
# List all keys
keys = list_keys()
Delete Secrets
from wauth import WAuth
auth = WAuth()
auth.set("TEMP_TOKEN", "expires-soon")
auth.delete("TEMP_TOKEN") # Permanently removes the secret
Time-To-Live (TTL)
from wauth import WAuth
auth = WAuth()
# Secret expires after 1 hour (3600 seconds)
auth.set("SESSION_TOKEN", "abc123", ttl=3600)
# After 1 hour, get() returns None
Key Rotation
from wauth import WAuth
auth = WAuth()
auth.set("KEY1", "value1")
auth.set("KEY2", "value2")
# Rotate the encryption key — all secrets are re-encrypted
results = auth.rotate_key("new-encryption-key")
# {"KEY1": True, "KEY2": True}
Backup & Restore
from wauth import WAuth
auth = WAuth()
auth.set("IMPORTANT", "critical-data")
# Export all secrets (still encrypted in the backup)
backup_path = auth.backup("my_vault_backup.wauth")
# Later, or on another machine with the same key
count = auth.restore("my_vault_backup.wauth")
print(f"Restored {count} secrets")
Docker Integration
from wauth import WAuth
from wauth.drivers import DriverFactory
# In a Docker container, DriverFactory tries Docker secrets first
factory = DriverFactory()
value = factory.get_value("DB_PASSWORD")
# Returns Docker secret if available, falls back to local vault
Async Support
import asyncio
from wauth import WAuth
async def main():
auth = WAuth()
await auth.async_set("ASYNC_KEY", "async-value")
value = await auth.async_get("ASYNC_KEY")
await auth.async_delete("ASYNC_KEY")
print(value) # async-value
asyncio.run(main())
Exception Handling
from wauth import WAuth
from wauth.exceptions import DecryptionError, KeyNotFoundError, WAuthError
auth = WAuth(custom_key="my-key")
try:
value = auth.get("MISSING")
except KeyNotFoundError:
print("Key does not exist")
try:
# Decrypting with wrong key raises DecryptionError
auth._driver.engine.decrypt("invalid-token")
except DecryptionError:
print("Wrong key or corrupted data")
# Catch any WAuth error
try:
auth.delete("MISSING")
except WAuthError:
print("Any WAuth-related error")
Secure Secret Verification
When you only need to check if a value matches a stored secret, use valid() instead of get(). This prevents the secret from being exposed in your code or logs.
from wauth import WAuth
auth = WAuth()
auth.set("API_KEY", "secret123")
# LESS SECURE: Using get() exposes the secret
# api_key = auth.get("API_KEY") # DON'T DO THIS
# if api_key == user_input: # Secret is now in memory
# MORE SECURE: Using valid() never exposes the secret
user_input = input("Enter API key: ")
if auth.valid("API_KEY", user_input):
print("Access granted")
else:
print("Access denied")
# The secret never leaves the wauth library
The valid() method:
- Uses constant-time comparison to prevent timing attacks
- Never returns or exposes the actual secret
- Only returns
TrueorFalse
Async Secret Verification
import asyncio
from wauth import WAuth
async def main():
auth = WAuth()
auth.set("API_KEY", "secret123")
user_input = input("Enter API key: ")
if await auth.async_valid("API_KEY", user_input):
print("Access granted")
asyncio.run(main())
Running the Examples
# Basic usage
python "examples/00 base/example.py"
# Secure secret verification (NEW in v0.5.0)
python "examples/18 valid method/example.py"
# Async secret verification
python "examples/19 async valid/example.py"
# Security comparison: get() vs valid()
python "examples/20 security comparison/example.py"
Examples
| # | Example | Description |
|---|---|---|
| 00 | base | Basic usage of WAuth |
| 01 | file storage | Storing encrypted files |
| 02 | TTL expiration | Time-to-live secrets |
| 03 | key rotation | Rotating encryption keys |
| 04 | backup and restore | Vault backup/restore |
| 05 | async API | Async secret operations |
| 06 | exception handling | Handling WAuth errors |
| 07 | functional API | Functional-style API |
| 08 | Docker secrets | Docker integration |
| 09 | config file | TOML configuration |
| 10 | logging integration | Loguru logging |
| 11 | migration from env | Migration from env vars |
| 12 | multiple vaults | Multiple database files |
| 13 | security best practices | Security guidelines |
| 14 | web app integration | Web framework integration |
| 15 | CI-CD pipeline | CI/CD integration |
| 16 | bulk operations | Bulk secret operations |
| 17 | pipeline | WPipe integration |
| 18 | valid method | NEW Secure secret verification |
| 19 | async valid | NEW Async verification |
| 20 | security comparison | NEW get() vs valid() |
Testing & Quality
# Run tests with coverage
make test # pytest with coverage report
# Lint all code
make lint-all # Pylint on source and tests
# Run benchmarks
python benchmarks/performance.py
# Full quality check
make quality # lint + test + format check
Quality Metrics
| Metric | Score | Target |
|---|---|---|
| Version | 0.5.0 | Latest |
| Pylint (wauth/) | 9.95/10 | ≥ 9.5 |
| Pylint (test/) | 9.89/10 | ≥ 9.5 |
| Test Coverage | 98% | ≥ 95% |
| Tests | 129+ passing | 100% |
| Bandit Security | 0 medium/high | 0 |
LTS Status
WAuth v0.5.0 is the latest stable release:
- 24 months of security backports
- Stable public API with deprecation guarantees
- SemVer-compliant versioning
- See VERSIONING.md
- See MIGRATION.md
- See SECURITY.md
Documentation
make html # Build Sphinx docs
make serve # Preview docs locally
Online Documentation:
| Site | URL |
|---|---|
| Full API Docs | https://wauth.readthedocs.io/en/latest/ |
| GitHub Pages | https://wisrovi.github.io/wauth/ |
| PyPI | https://pypi.org/project/wauth/ |
Full API reference is available at docs/_build/html/index.html.
Integration: WPipe
Combine WAuth with WPipe for powerful stateful pipelines with secure secret management.
| Library | Purpose |
|---|---|
| WAuth | Encrypted secret storage with Fernet |
| WPipe | Stateful pipelines with conditional logic |
Example: Access control pipeline:
from wauth import WAuth
from wpipe import Pipeline, Condition, state
# Store secrets with WAuth
auth = WAuth(db_path="secrets.db", custom_key="my-key")
auth.set("USER_PASSWORD", "secure-pass")
# Use in WPipe pipeline
@state(name="authenticate")
def authenticate(credentials):
auth = WAuth(db_path="secrets.db", custom_key="my-key")
real_pass = auth.get("USER_PASSWORD")
return {"access_granted": credentials.secret == real_pass}
pipeline = Pipeline(pipeline_name="access_control")
pipeline.set_steps([authenticate, Condition(...)])
See examples/17 pipeline/ for the full example.
🛡️ The Wisrovi Philosophy: Productive Quality
This project is more than just a wrapper; it's a layer of strategic abstraction.
In today's development ecosystem, the technical complexity of core tools (like Docker, Kafka, or YOLO) often becomes an obstacle to innovation. The wisrovi SUITE was born with a clear mission: to tame technology.
Why use this library instead of the core tools?
-
Critical Acceleration: We reduce the learning curve and development time by a factor of 3.3x. What used to take hours of configuration is now resolved in minutes.
-
Professional Opinion: We don't just wrap features; we natively integrate best practices for security, error handling, and resilience. * Business Focus: By eliminating the boilerplate and unnecessary complexity, we allow developers to focus on creating value, not battling with the infrastructure.
-
Production Standard: Every component of this suite is designed to be Production-Ready. We don't build tools for prototypes, but for solutions that scale in the real world.
“We didn't reinvent the wheel; we built the high-performance vehicle that enables businesses to reach the finish line faster.”
Author
William Rodríguez — wisrovi
Technology Evangelist & Open Source Advocate
WAuth is designed for developers who need simple, secure, machine-locked secret storage. Perfect for local development environments, CI/CD pipelines, and single-node deployments. LTS since v1.6.0.
🛠️ Roadmap & Future Integration (Updated May 2026)
We are entering the Distributed AI Era. The wisrovi suite is evolving to support massive-scale machine learning orchestration:
- ⚡ Core Stability: Maintenance and performance optimization across all
w-libraries. - 🤖 Coming Soon:
wtrain: Distributed Evolutionary Training for YOLO models. - 🛰️ Coming Soon:
wcelery: High-performance task distribution with nativewauthsecurity. - 🎓 Wisrovi Academy: Official certification and advanced tutorials are on the way.
Status: Active development. New major releases coming this quarter.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wauth-0.5.0.tar.gz.
File metadata
- Download URL: wauth-0.5.0.tar.gz
- Upload date:
- Size: 48.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
27a007fa1142a5179a8612d22571e0e261468d6ffef5d4acff0a820567ff829c
|
|
| MD5 |
322274aec0339194fa8a636c994f082e
|
|
| BLAKE2b-256 |
29e201bb2164f92b173300593af59279a5e8119faa727474fb6fe4324fc2547c
|
File details
Details for the file wauth-0.5.0-py3-none-any.whl.
File metadata
- Download URL: wauth-0.5.0-py3-none-any.whl
- Upload date:
- Size: 26.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5f48c6e4120ec42e4a0fef4ef43ef52e87417dc36e2393e889d32fd9d7c9c04a
|
|
| MD5 |
19d8dc4edfff5c76b0890186e6d1b4f4
|
|
| BLAKE2b-256 |
5274bfe0bf1d9c29e30d631f57e71d6c2d4f379c097226d6cb77f48bc986c78f
|