Skip to main content
╔══════════════════════════╗
║      ━━━━━(○)━━━━━       ║
║                          ║
║     T R I P W I R E      ║
║                          ║
║    Config validation     ║
║     that fails fast      ║
╚══════════════════════════╝

Schema-Driven Environment Variable Management for Python

Generate a TOML schema from your code, keep .env / .env.example / Python in lockstep, and catch missing or invalid environment variables at import time — not in production. Bundled with secret detection (45+ patterns) and git-history auditing for leaked secrets.

Stable since v1.0.0 — Development Status :: 5 - Production/Stable. Public API is locked under SemVer; breaking changes require a major version bump.

CI Security codecov PyPI version Python 3.11+ License: MIT Examples

Quick Start • Documentation • Runnable Examples • CLI Reference • API Docs • VS Code Extension • Discord


The Problem

Every Python developer has experienced this:

# Your code
import os
DATABASE_URL = os.getenv("DATABASE_URL")  # Returns None - no error yet

# 2 hours later in production...
host = DATABASE_URL.split('@')[1].split('/')[0]
# 💥 AttributeError: 'NoneType' object has no attribute 'split'

# Production is down. Users are angry. You're debugging at 2 AM.

See this problem in action →

The pain:

  • Environment variables fail at runtime, not at startup
  • No validation (wrong types, missing values, invalid formats)
  • .env files drift across team members
  • Secrets accidentally committed to git
  • No type safety for configuration

The Solution: TripWire

TripWire validates environment variables at import time and keeps your team in sync.

Before TripWire

import os

# Runtime crash waiting to happen
DATABASE_URL = os.getenv("DATABASE_URL")  # Could be None
PORT = int(os.getenv("PORT"))  # TypeError if PORT not set
DEBUG = os.getenv("DEBUG") == "true"  # Wrong! Returns False for "True", "1", etc.

See these anti-patterns → | Run: python examples/problems/02_int_conversion_error.py

After TripWire

from tripwire import env

# Import fails immediately if vars missing/invalid
DATABASE_URL: str = env.require("DATABASE_URL", format="postgresql")
PORT: int = env.require("PORT", min_val=1, max_val=65535)
DEBUG: bool = env.optional("DEBUG", default=False)

# Your app won't even start with bad config!

Try this example → | See all examples →

Key Benefits:

  • ✅ Import-time validation - Fail fast, not in production
  • ✅ Type safety - Automatic type coercion with validation
  • ✅ Team sync - Keep .env files consistent across team
  • ✅ Auto-documentation - Generate .env.example from code
  • ✅ Secret detection - 45+ platform-specific patterns (AWS, GitHub, Stripe, etc.)
  • ✅ Git history auditing - Find when secrets were leaked and generate remediation steps
  • ✅ Great error messages - Know exactly what's wrong and how to fix it

Quick Start

Installation

pip install tripwire-py

Note: The package name on PyPI is tripwire-py, but you import it as tripwire:

from tripwire import env  # Import name is 'tripwire'

Initialize Your Project

$ tripwire init

Welcome to TripWire! 🎯

✅ Created .env
✅ Created .env.example
✅ Updated .gitignore

Setup complete! ✅

Next steps:
  1. Edit .env with your configuration values
  2. Import in your code: from tripwire import env
  3. Use variables: API_KEY = env.require('API_KEY')

Basic Usage

# config.py
from tripwire import env

# Required variables (fail if missing)
API_KEY: str = env.require("API_KEY")
DATABASE_URL: str = env.require("DATABASE_URL", format="postgresql")

# Optional with defaults
DEBUG: bool = env.optional("DEBUG", default=False)
MAX_RETRIES: int = env.optional("MAX_RETRIES", default=3)

# Validated formats
EMAIL: str = env.require("ADMIN_EMAIL", format="email")
REDIS_URL: str = env.require("REDIS_URL", format="url")

# Now use them safely - guaranteed to be valid!
print(f"Connecting to {DATABASE_URL}")

Run this example → | Format validation → | Quick Start Guide →


Core Features

1. Import-Time Validation

Your app won't start with bad config.

from tripwire import env

# This line MUST succeed or ImportError is raised
API_KEY = env.require("API_KEY")
# No more runtime surprises!

2. Type Inference & Validation

Automatic type detection from annotations (v0.4.0+) - no need to specify type= twice!

from tripwire import env

# Type inferred from annotation
PORT: int = env.require("PORT", min_val=1, max_val=65535)
DEBUG: bool = env.optional("DEBUG", default=False)
TIMEOUT: float = env.optional("TIMEOUT", default=30.0)

# Lists and dicts
ALLOWED_HOSTS: list = env.require("ALLOWED_HOSTS")  # Handles CSV or JSON
FEATURE_FLAGS: dict = env.optional("FEATURE_FLAGS", default={})

# Choices/enums
ENVIRONMENT: str = env.require("ENVIRONMENT", choices=["dev", "staging", "prod"])

Type coercion example → | Range validation → | Choices validation → | Type Inference docs →

3. Format Validators

Built-in validators for common formats, plus advanced validators for security-critical configurations (v0.10.1+).

# Basic format validation
ADMIN_EMAIL: str = env.require("ADMIN_EMAIL", format="email")
API_URL: str = env.require("API_URL", format="url")
DATABASE_URL: str = env.require("DATABASE_URL", format="postgresql")
SERVER_IP: str = env.require("SERVER_IP", format="ipv4")

# Custom regex patterns
API_KEY: str = env.require("API_KEY", pattern=r"^sk-[a-zA-Z0-9]{32}$")

# Advanced URL validation (v0.10.1+)
from tripwire.validation import validate_url_components
API_ENDPOINT: str = env.require(
    "API_ENDPOINT",
    validator=lambda url: validate_url_components(
        url,
        protocols=["https"],  # HTTPS-only for security
        forbidden_ports=[22, 23, 3389],  # Block SSH/Telnet/RDP
        required_params=["api_key"]  # Enforce authentication
    )[0]
)

# DateTime validation for expiration dates (v0.10.1+)
from tripwire.validation import validate_datetime
CERT_EXPIRY: str = env.require(
    "CERT_EXPIRY",
    validator=lambda dt: validate_datetime(
        dt,
        formats=["ISO8601"],
        require_timezone=True,
        min_datetime="2025-01-01T00:00:00Z"
    )[0]
)

See all validators →

4. Secret Detection & Git Audit

Detect secrets in .env and audit git history for leaks.

# Auto-detect and audit all secrets
$ tripwire security audit --all

🔍 Auto-detecting secrets in .env file...
⚠️  Found 3 potential secret(s)

📊 Secret Leak Blast Radius
═══════════════════════════
🔍 Repository Secret Exposure
├─ 🔴 🚨 AWS_SECRET_ACCESS_KEY (47 occurrence(s))
│  ├─ Branches: origin/main, origin/develop
│  └─ Files: .env
├─ 🟡 ⚠️ STRIPE_SECRET_KEY (12 occurrence(s))
└─ 🟢 DATABASE_PASSWORD (0 occurrence(s))

📈 Summary: 2 leaked, 1 clean, 59 commits affected

Detects 45+ secret types: AWS, GitHub, Stripe, Azure, GCP, Slack, and more.

Learn more about Secret Management → | Git Audit Deep Dive →


Essential CLI Commands

# Initialize project
tripwire init

# Generate .env.example from code
tripwire generate

# Check for drift between .env and .env.example
tripwire check

# Sync .env with .env.example
tripwire sync

# Compare configurations (v0.4.0+)
tripwire diff .env .env.prod

# Scan for secrets (v0.8.0+)
tripwire security scan --strict

# Audit git history for secret leaks (v0.8.0+)
tripwire security audit --all

# Validate .env without running app
tripwire validate

# Plugin management (v0.10.0+)
tripwire plugin install vault
tripwire plugin list

Complete CLI Reference →


Framework Integration

FastAPI

from fastapi import FastAPI
from tripwire import env

# Validate at import time
DATABASE_URL: str = env.require("DATABASE_URL", format="postgresql")
SECRET_KEY: str = env.require("SECRET_KEY", secret=True, min_length=32)
DEBUG: bool = env.optional("DEBUG", default=False)

app = FastAPI(debug=DEBUG)

@app.on_event("startup")
async def startup():
    print(f"Connecting to {DATABASE_URL[:20]}...")

Run full FastAPI example →

Django

# settings.py
from tripwire import env

SECRET_KEY = env.require("DJANGO_SECRET_KEY", secret=True, min_length=50)
DEBUG = env.optional("DEBUG", default=False)
ALLOWED_HOSTS = env.optional("ALLOWED_HOSTS", default=["localhost"], type=list)

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': env.require("DB_NAME"),
        'USER': env.require("DB_USER"),
        'PASSWORD': env.require("DB_PASSWORD", secret=True),
        'HOST': env.optional("DB_HOST", default="localhost"),
        'PORT': env.optional("DB_PORT", default=5432),
    }
}

Run full Django example →

Flask

from flask import Flask
from tripwire import env

# Validate before app creation
DATABASE_URL: str = env.require("DATABASE_URL", format="postgresql")
SECRET_KEY: str = env.require("SECRET_KEY", secret=True)
DEBUG: bool = env.optional("DEBUG", default=False)

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = DATABASE_URL
app.config['SECRET_KEY'] = SECRET_KEY

Run full Flask example →

More framework examples → | See all framework integrations →


The Schema-Driven Workflow

This is what TripWire is for. The env.require() API is the runtime tip; the schema lifecycle is the iceberg. One TOML file is the source of truth — your code, your .env, your .env.example, and your CI all read from it.

# .tripwire.toml
[project]
name = "my-app"
version = "1.0.0"

[variables.DATABASE_URL]
type = "string"
required = true
format = "postgresql"
description = "PostgreSQL connection"
secret = true

[variables.PORT]
type = "int"
required = false
default = 8000
min = 1024
max = 65535

[environments.production]
strict_secrets = true

The schema commands form a directional pipeline — from-* ingests, to-* exports:

# Bootstrap a schema from existing code (scans env.require() calls)
tripwire schema from-code

# Or bootstrap from an existing .env.example
tripwire schema from-example

# Export an .env.example from schema (committable, reviewable)
tripwire schema to-example

# Generate environment-specific .env files
tripwire schema to-env --environment production

# Render Markdown / HTML docs from schema
tripwire schema to-docs

# Validate any .env against the schema (use in CI)
tripwire schema validate --environment production

# Check for drift between schema, code, and .env
tripwire schema check

Configuration as Code guide →


Extending TripWire (Plugin System)

For teams that pull configuration from external secret stores, TripWire ships an extension point. The bundled plugins are stable but not the headline of the project — most users never need them.

Bundled Plugins

# Install plugins from official registry
tripwire plugin install vault           # HashiCorp Vault
tripwire plugin install aws-secrets     # AWS Secrets Manager
tripwire plugin install azure-keyvault  # Azure Key Vault
tripwire plugin install remote-config   # Generic HTTP endpoint

Using Plugins

from tripwire import TripWire
from tripwire.plugins.sources import VaultEnvSource, AWSSecretsSource

# HashiCorp Vault
vault = VaultEnvSource(
    url="https://vault.company.com",
    token="hvs.xxx",
    mount_point="secret",
    path="myapp/config"
)

# AWS Secrets Manager
aws = AWSSecretsSource(
    secret_name="myapp/production",
    region_name="us-east-1"
    # Uses AWS credentials from environment or IAM role
)

# Use with TripWire
env = TripWire(sources=[vault, aws])
DATABASE_URL = env.require("DATABASE_URL")
API_KEY = env.require("API_KEY")

Plugin Commands

# Search for plugins
tripwire plugin search vault

# List installed plugins
tripwire plugin list

# Update a plugin
tripwire plugin update vault --version 0.2.0

# Remove a plugin
tripwire plugin remove vault

Authentication

HashiCorp Vault:

  • Token authentication: VAULT_TOKEN env var
  • AppRole authentication: VAULT_ROLE_ID + VAULT_SECRET_ID
  • Kubernetes auth: Automatic when running in K8s

AWS Secrets Manager:

  • IAM credentials: AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY
  • IAM role: Automatic when running on EC2/ECS/Lambda
  • AWS CLI profile: Respects AWS_PROFILE env var

Azure Key Vault:

  • Service Principal: AZURE_CLIENT_ID + AZURE_TENANT_ID + AZURE_CLIENT_SECRET
  • Managed Identity: Automatic when running on Azure VMs/App Service
  • Azure CLI: Uses az login credentials

Remote HTTP Endpoint:

  • Bearer token: Authorization: Bearer <token> header
  • API key: Custom header authentication
  • mTLS: Client certificate authentication

Learn more about Plugin Development →


CI/CD Integration

GitHub Actions

name: Validate Environment
on: [push, pull_request]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      - run: pip install tripwire-py
      - run: tripwire generate --check
      - run: tripwire security scan --strict
      - run: tripwire schema validate --strict

More CI/CD examples →


Comparison with Alternatives

Feature TripWire python-decouple environs pydantic-settings python-dotenv
Import-time validation ✅ ❌ ⚠️ ⚠️ ❌
Type coercion ✅ ⚠️ Basic ✅ ✅ ❌
Format validators ✅ ❌ ✅ ✅ ❌
.env.example generation ✅ ❌ ❌ ❌ ❌
Team sync (drift detection) ✅ ❌ ❌ ❌ ❌
Secret detection (45+ patterns) ✅ ❌ ❌ ❌ ❌
Git history auditing ✅ ❌ ❌ ❌ ❌
Plugin system (cloud secrets) ✅ ❌ ❌ ❌ ❌
CLI tools ✅ ❌ ❌ ❌ ⚠️
Multi-environment ✅ ✅ ✅ ✅ ✅

What Makes TripWire Different?

While all these libraries handle environment variables, TripWire focuses on the complete developer workflow:

  • Prevent production failures with import-time validation
  • Keep teams in sync with automated .env.example generation
  • Protect secrets with detection and git history auditing
  • Streamline onboarding with CLI tools for env management

TripWire is designed for teams that want comprehensive config management, not just loading .env files.

When to Choose Each Library

Choose TripWire When:

  • You need guaranteed import-time validation to prevent production starts with invalid config
  • Your team struggles with .env file drift and keeping documentation current
  • Security is paramount and you need secret detection/git history auditing
  • You want automated .env.example generation from your code
  • You prefer comprehensive CLI tools for environment management

Choose python-dotenv When:

  • You need a minimal, zero-config .env loader
  • You're building a simple script or small project
  • Minimal dependencies are a priority

Choose environs When:

  • You need comprehensive type validation powered by marshmallow
  • You're already using marshmallow in your project

Choose pydantic-settings When:

  • Your project already uses Pydantic for data validation
  • You need settings to integrate seamlessly with FastAPI

Choose python-decouple When:

  • You want strict separation of config from code with minimal overhead
  • You need zero dependencies

Acknowledgments

TripWire builds on the excellent work of the Python community, particularly:

  • python-dotenv for reliable .env file parsing
  • The validation patterns pioneered by environs and pydantic
  • The config separation philosophy of python-decouple

What's In Scope (and What Isn't)

TripWire is a Python library, not a platform. Its job is to make environment-variable management bulletproof for a Python codebase. Anything that requires a server, a hosted dashboard, or a paid tier lives outside that scope until adoption signals demand otherwise.

In scope — actively maintained:

  • Import-time validation, type inference, format validators
  • The schema lifecycle (from-code, from-example, to-example, to-env, to-docs, validate, check)
  • Secret detection (45+ patterns) and git-history auditing
  • Static analysis (tripwire analyze usage / deadcode / dependencies)
  • Plugin extension point + the four bundled cloud plugins (Vault, AWS, Azure, Remote HTTP)
  • VS Code extension (separate repository)

Not in scope — won't ship without strong external demand:

  • Hosted Web UI / SaaS dashboard
  • Encrypted .env file format
  • Compliance reporting (SOC2, HIPAA)
  • Additional cloud-secret plugins beyond the bundled four

If you have a strong use case for one of the "not in scope" items, open an issue — repeated, specific demand is what moves these into scope.


Documentation

Complete documentation is available at docs/README.md:

Getting Started

Guides

Reference

Advanced


Contributing

We welcome contributions! See our development workflow:

# Clone and setup
git clone https://github.com/Daily-Nerd/TripWire.git
cd tripwire
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate
pip install -e ".[dev]"

# Run tests
pytest

# Run linter
ruff check .

# Format code
black .

See CONTRIBUTING.md for detailed guidelines.


License

MIT License - see LICENSE file for details.


Support


TripWire - Environment variables that just work. 🎯

Stop debugging production crashes. Start shipping with confidence.

Metadata

Release files for tripwire-py 1.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tripwire-py 1.0.2
File Size Uploaded
tripwire_py-1.0.2.tar.gz 489.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tripwire-py 1.0.2
File Interpreter ABI Platform
tripwire_py-1.0.2-py3-none-any.whl Python 3 none any Details

Total release size: 728.8 kB

Release files / tripwire_py-1.0.2.tar.gz

Download URL tripwire_py-1.0.2.tar.gz
Size 489.5 kB
Tags Source
SHA-256 checksum
How to use checksums
07a6c0db816a9f160fe1e949a1758bbca7492a84c9647418a44ac2c2bc2a3e2c
BLAKE2b-256 checksum
How to use checksums
b91c6e656e38893053140de50b27e68049e7ab4d7527e54e10c1c1505be469ac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 22, 2026.

Transparency log

Release files / tripwire_py-1.0.2-py3-none-any.whl

Download URL tripwire_py-1.0.2-py3-none-any.whl
Size 239.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8bcd7ec251a15102bf03484aae2dab4948316e4075a7a4c334365a877739fca0
BLAKE2b-256 checksum
How to use checksums
b3e94271848ceca7645ac33a86e8cd18de040833e8a399eb781c8f54a2a5a94e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 22, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.2 This release

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.13.0

2 release files

0.12.4

2 release files

0.12.3

2 release files

0.12.2

2 release files

0.12.1

2 release files

0.12.0

2 release files

0.11.1

2 release files

0.11.0

2 release files

0.10.4

2 release files

0.10.3

2 release files

0.10.2

2 release files

0.10.1

2 release files

0.10.0

2 release files

0.9.0

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page