Skip to main content

Machine-locked encrypted secret management library with Fernet encryption

Project description

WAuth — Machine-Locked Encrypted Secret Management

PyPI version Python 3.9+ Pylint Score Test Coverage License: MIT LTS

Store, rotate, back up, retrieve, and verify secrets encrypted with Fernet (AES-256), backed by SQLite — tied to the machine they were created on.

IMG-20260507-WA0007

Key Features

  • Fernet Encryption — AES-128-CBC via the cryptography library, 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/secrets when 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 Supportasync_set(), async_get(), async_delete(), async_backup(), async_restore() for async workflows.
  • Secret Verificationvalid() and async_valid() for secure secret checking without exposing the actual value.
  • Custom Exceptions — Structured exception hierarchy (DecryptionError, KeyNotFoundError, VaultError, etc.).
  • Structured Logging — Full loguru integration 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_key parameter.

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 True or False

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:

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

🔗 LinkedIn
🐙 GitHub


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 native wauth security.
  • 🎓 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

wauth-0.5.0.tar.gz (48.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wauth-0.5.0-py3-none-any.whl (26.8 kB view details)

Uploaded Python 3

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

Hashes for wauth-0.5.0.tar.gz
Algorithm Hash digest
SHA256 27a007fa1142a5179a8612d22571e0e261468d6ffef5d4acff0a820567ff829c
MD5 322274aec0339194fa8a636c994f082e
BLAKE2b-256 29e201bb2164f92b173300593af59279a5e8119faa727474fb6fe4324fc2547c

See more details on using hashes here.

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

Hashes for wauth-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5f48c6e4120ec42e4a0fef4ef43ef52e87417dc36e2393e889d32fd9d7c9c04a
MD5 19d8dc4edfff5c76b0890186e6d1b4f4
BLAKE2b-256 5274bfe0bf1d9c29e30d631f57e71d6c2d4f379c097226d6cb77f48bc986c78f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page