A secure drop-in replacement for python-decouple with startup validation
Project description
secretsguardian
A secure drop-in replacement for python-decouple with startup validation.
Install
pip install secretsguardian
One-line migration from python-decouple
# Before
from decouple import config
# After — identical behaviour, one line change
from secretsguardian import config
Zero other changes required. Every existing call works identically.
Quick start — Django settings.py
from secretsguardian import config, require
# Block startup immediately if any of these are missing from the environment
require('SECRET_KEY', 'DATABASE_URL', 'STRIPE_KEY', 'EMAIL_HOST_PASSWORD')
# Validate strength at startup — never lazily during a request
SECRET_KEY = config('SECRET_KEY', min_length=50, reject_insecure=True)
DATABASE_URL = config('DATABASE_URL')
STRIPE_KEY = config('STRIPE_KEY', min_length=32)
EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD')
# All existing decouple usage works without any changes
DEBUG = config('DEBUG', default=False, cast=bool)
PORT = config('PORT', default=8000, cast=int)
Feature 1 — Secret Strength Validation
config() accepts two new optional parameters:
min_length=int
Raises WeakSecretError if the resolved value is shorter than the given number of characters.
SECRET_KEY = config('SECRET_KEY', min_length=50)
Error output:
[secretsguardian] SECRET_KEY failed validation: too short.
Minimum length is 50 characters. Update your .env file.
reject_insecure=True
Raises InsecureSecretError if the value starts with django-insecure-.
SECRET_KEY = config('SECRET_KEY', reject_insecure=True)
Error output:
[secretsguardian] SECRET_KEY failed validation: insecure default value detected.
Django's default 'django-insecure-' key must not be used in production.
Generate a new key and update your .env file.
Both parameters can be combined:
SECRET_KEY = config('SECRET_KEY', min_length=50, reject_insecure=True)
Feature 2 — Startup Secrets Checklist
require() accepts any number of secret name strings. It checks every listed secret at startup, collects all missing names, and raises a single MissingSecretsError listing them together.
require('SECRET_KEY', 'DATABASE_URL', 'STRIPE_KEY', 'EMAIL_HOST_PASSWORD')
Error output when several secrets are missing:
[secretsguardian] Application startup blocked. The following required secrets are missing:
- DATABASE_URL
- STRIPE_KEY
- EMAIL_HOST_PASSWORD
Add these to your .env file before starting the application.
Call require() before any config() calls in settings.py.
Parameter Reference
config(key, *, default=..., cast=None, min_length=None, reject_insecure=False)
| Parameter | Type | Default | Description |
|---|---|---|---|
key |
str |
required | Environment variable name |
default |
any | — | Fallback value when key is absent (passed to decouple) |
cast |
callable | None |
Type cast function e.g. int, bool (passed to decouple) |
min_length |
int |
None |
Minimum character length for the resolved value |
reject_insecure |
bool |
False |
Reject values starting with django-insecure- |
require(*secret_names)
| Parameter | Type | Description |
|---|---|---|
*secret_names |
str |
One or more environment variable names to check |
Exceptions
All exceptions inherit from EnvGuardianError, so you can catch them all with a single clause:
from secretsguardian.exceptions import EnvGuardianError
try:
require('SECRET_KEY')
SECRET_KEY = config('SECRET_KEY', min_length=50, reject_insecure=True)
except EnvGuardianError as e:
# Handle all secretsguardian errors in one place
raise SystemExit(e)
| Exception | Raised when |
|---|---|
EnvGuardianError |
Base class for all secretsguardian errors |
WeakSecretError |
Value is shorter than min_length |
InsecureSecretError |
Value starts with django-insecure- |
MissingSecretsError |
One or more required secrets are absent |
Test mode
When any of the following environment variables are present, all secretsguardian validation is skipped silently. The package behaves identically to plain decouple.
| Variable | Value that activates test mode |
|---|---|
ENV |
test |
DJANGO_ENV |
test |
TESTING |
true |
This means your test suite can use placeholder or missing values without any changes:
ENV=test pytest
or in pytest.ini / conftest.py:
import os
os.environ.setdefault("ENV", "test")
Security guarantees
- Secret values never appear in any error message, log, or output — only secret names.
- No network calls anywhere in the package. All validation runs locally.
- All validation happens at app startup, never lazily during a live request.
- Errors are human-readable and tell you exactly what is wrong and how to fix it.
License
MIT. secretsguardian is a drop-in replacement for python-decouple with added security validation. It imports python-decouple as a dependency; no source code is copied.
Project details
Release history Release notifications | RSS feed
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 secretsguardian-0.1.0.tar.gz.
File metadata
- Download URL: secretsguardian-0.1.0.tar.gz
- Upload date:
- Size: 7.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c3191a99317035daddb3bf95aa3a80d8c56877a5bd1245d70a6455bb1d6cb5b
|
|
| MD5 |
6848d075fb3fe871846889f01c13fe09
|
|
| BLAKE2b-256 |
fe08a3a903c9ccbb2d220ab90d06ad86eefa9165c1febb3e79f65916327d0c21
|
File details
Details for the file secretsguardian-0.1.0-py3-none-any.whl.
File metadata
- Download URL: secretsguardian-0.1.0-py3-none-any.whl
- Upload date:
- Size: 6.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2f087824e2a12b7c9718c5bd9e43d0c3e66c8225048c9f2248e9dbff65c669ad
|
|
| MD5 |
5cf1a632d2343304e4a71ee6692a7d48
|
|
| BLAKE2b-256 |
0eaeb4ba9bcb66ce2acdbf02c3ef93c22060b0f1b3fa4f1d5682e1de84693b93
|