Privacy-first configuration file anonymizer for DevOps, Cloud, and CI/CD workflows.
Ovatta is a CLI tool that anonymizes sensitive data in configuration files, making them safe to share, commit to version control, or use in documentation. Built with privacy in mind: all processing happens locally, no data ever leaves your machine, and secrets are never decoded or transmitted.
- ** Privacy-First**: 100% local processing, no network calls, secrets never decoded
- ** Multiple Formats**:
.env,.ini,.toml,.yaml,.yml,.json - ** Kubernetes Native**: Auto-detects K8s resources and handles multi-document YAML manifests (separated by
---) - ** Smart Detection**: Recognizes tokens, API keys, passwords, certificates, URLs, IPs, and more
- ** Three Profiles**:
full,medium,light- choose your anonymization level - ** Batch Processing**: Process multiple files at once with glob patterns
- ** Dry Run Mode**: Preview what will be redacted before making changes
- ** Validate Mode**: Check if files contain sensitive data (perfect for CI/CD)
- ** Exclude Patterns**: Skip specific keys/paths from redaction with wildcard support
- ** Scramble Option**: Shuffle values instead of replacing with
[REDACTED]
git clone https://github.com/yourusername/ovatta.git
cd ovatta
go build -o ovattago install github.com/yourusername/ovatta@latestgo get gopkg.in/yaml.v3
go get github.com/pelletier/go-toml/v2
go get github.com/spf13/cobraovatta [flags] [files...]| Flag | Short | Description | Default |
|---|---|---|---|
--input |
-i |
Input file (can be used multiple times) | - |
--output |
-o |
Output file (single file mode only) | stdout |
--output-dir |
- | Output directory (multiple files mode) | - |
--profile |
-p |
Anonymization profile: full, medium, light |
medium |
--exclude-key |
-e |
Comma-separated keys/paths to exclude (supports wildcards) | - |
--scramble |
-s |
Scramble values instead of [REDACTED] |
false |
--dry-run |
- | Preview changes without writing files | false |
--validate |
- | Validate files for sensitive data (exit 0 if clean, 1 if found) | false |
--force |
- | Overwrite existing output files | false |
--help |
-h |
Show help message | - |
Redacts only the most critical secrets:
password,pwd,secret
Use case: Internal documentation where most data can remain visible.
Redacts common sensitive fields and detectable tokens:
- All
lightprofile keys token,api_key,apikey,access_key,secret_key,private_keyclient_secret,auth,authorization,credentialsdb_password,database_password- Kubernetes-specific:
data,stringData, certificate fields - AWS keys (
AKIA...,ASIA...) - GitHub tokens (
ghp_...,gho_...,ghu_...) - Base64 encoded secrets (>50 chars)
- Long tokens (>30 chars)
Use case: Sharing configs with team members or in semi-public repositories.
Redacts everything from medium plus:
- All key-value pairs
- URLs and endpoints
- IP addresses
- Email addresses
Use case: Public documentation, open-source examples, or external sharing.
Ovatta automatically detects and redacts:
| Pattern | Example | Profiles |
|---|---|---|
| Passwords | password=secret123 |
light, medium, full |
| API Keys | api_key=sk_live_123... |
medium, full |
| AWS Keys | AKIAIOSFODNN7EXAMPLE |
medium, full |
| GitHub Tokens | ghp_1234567890abcdefghij... |
medium, full |
| Base64 Secrets | Y2VydGlmaWNhdGU... (>50 chars) |
medium, full |
| Long Tokens | Any alphanumeric string >30 chars | medium, full |
| URLs | https://api.example.com |
full only |
| IP Addresses | 192.168.1.1 |
full only |
| Emails | user@example.com |
full only |
# Anonymize and print to stdout
ovatta -i config.yaml
# Anonymize and save to file
ovatta -i config.yaml -o config-anon.yaml
# Using full profile with scramble
ovatta -i .env -o .env.anon -p full -s# Using multiple -i flags
ovatta -i config.yaml -i secret.yaml -i .env --output-dir ./anonymized
# Using glob pattern (shell expansion)
ovatta configs/*.yaml --output-dir ./safe-configs
# Mixed approach
ovatta -i app.toml configs/*.yaml secrets/*.json --output-dir ./output
# With force to overwrite
ovatta res/*.yaml --output-dir /tmp/results --force# Single K8s resource
ovatta deployment.yaml -o deployment-anon.yaml
# Multi-document YAML (separated by ---)
ovatta all-resources.yaml -o all-resources-anon.yaml
# Multiple K8s files
ovatta k8s/*.yaml --output-dir ./k8s-anonymized -p medium# Preview what will be redacted
ovatta --dry-run -i config.yaml
# Check multiple files
ovatta --dry-run secrets/*.yaml configs/*.json
# With specific profile
ovatta --dry-run -p full production.env# Exclude specific keys (anywhere in the file)
ovatta -i config.yaml -e "version,timestamp,public_key" -o output.yaml
# Exclude full paths (exact location)
ovatta -i config.yaml -e "data.api_token,metadata.name" -p full
# Exclude with wildcards - prefix*
ovatta -i config.yaml -e "app_*,public_*" --output-dir ./out
# Exclude with wildcards - *suffix
ovatta -i config.yaml -e "*_version,*_id" --output-dir ./out
# Exclude with wildcards - *contains*
ovatta -i config.yaml -e "*public*,*test*" --output-dir ./out
# Multiple files with excludes
ovatta res/*.yaml -e "data.api_token,kind,apiVersion" --output-dir /tmp/results# Validate single file
ovatta --validate config.yaml
echo $? # 0 = clean, 1 = sensitive data found
# Validate multiple files
ovatta --validate configs/*.yaml secrets/*.json
# Validate with specific profile
ovatta --validate -p full production.yaml
# Validate with excludes (ignore false positives)
ovatta --validate -e "version,public_key,*_id" deployment.yaml
# In CI/CD pipeline
ovatta --validate deployment/*.yaml || exit 1
# Validate anonymized files to ensure they're clean
ovatta --validate anonymized/*.yaml && echo "Safe to commit!"# Overwrite existing files
ovatta -i config.yaml -o config.yaml --force
# Scramble instead of [REDACTED]
ovatta -i .env --scramble --output-dir ./scrambled
# Light profile for internal docs
ovatta -i database.ini -o database-internal.ini -p light
# Full anonymization with excludes for public sharing
ovatta -i production.yaml -o example.yaml -p full -e "kind,apiVersion,metadata.name"
# Complex workflow: validate, then anonymize if needed
ovatta --validate config.yaml || ovatta -i config.yaml -o config-safe.yamlDATABASE_URL=postgres://user:password@localhost:5432/db
API_KEY=sk_live_1234567890abcdefghijklmnop
SECRET_TOKEN=super-secret-token-here[database]
host = localhost
password = secretpass123
port = 5432
[api]
key = your-api-key-here
endpoint = https://api.example.com[database]
host = "localhost"
password = "secretpass123"
port = 5432
[api]
key = "your-api-key-here"
token = "ghp_1234567890abcdefghijklmnop"database:
host: localhost
password: secretpass123
port: 5432
api:
key: your-api-key-here
token: ghp_1234567890abcdefghijklmnop{
"database": {
"host": "localhost",
"password": "secretpass123",
"port": 5432
},
"api": {
"key": "your-api-key-here",
"token": "ghp_1234567890abcdefghijklmnop"
}
}apiVersion: v1
kind: Secret
metadata:
name: db-credentials
data:
password: cGFzc3dvcmQxMjM=
api-key: YXBpa2V5MTIzNDU2
---
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
database_url: postgres://user:pass@localhost/db
api_token: sk_live_1234567890
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
template:
spec:
containers:
- name: app
env:
- name: SECRET_KEY
value: super-secret-key-123456Ovatta is designed with a zero-trust, privacy-first architecture that ensures your sensitive data never leaves your control:
Secrets are NEVER decoded or decrypted. This is a critical security principle:
- Base64-encoded secrets (like Kubernetes Secret data) are detected and redacted without ever being decoded
- Encrypted values remain encrypted throughout the entire process
- Pattern matching works on the encoded/encrypted form, never on plaintext
- Even if a secret is in JWT format, PGP-encrypted, or any other encoding, it's never decoded
This means:
- ✅ Zero risk of accidental exposure through decoding errors
- ✅ No plaintext secrets in memory at any point
- ✅ Safe to run on untrusted systems - even if someone intercepts the process, they only see encoded data
- ✅ Audit-friendly - logs and outputs never contain decoded secrets
Ovatta operates with complete isolation:
- ✅ 100% Local Processing: All operations happen on your machine, in-process
- ✅ No Network Calls: Zero external connections, no data transmission whatsoever
- ✅ No External Dependencies: No calls to APIs, databases, or cloud services
- ✅ No Logging: Sensitive data is never written to logs, temp files, or caches
- ✅ No Telemetry: No usage statistics, no phone home, no tracking
- ✅ Stateless: No persistent storage between runs
- ✅ Open Source: Full transparency - audit the code yourself
- Does not send data to any external service
- Does not decode, decrypt, or unwrap encoded/encrypted secrets
- Does not store or cache sensitive information
- Does not require internet connection
- Does not write secrets to temporary files
- Does not use analytics or telemetry
- Does not import secrets from secret managers (reads only from files you provide)
- Principle of Least Privilege: Only reads files you explicitly specify
- Defense in Depth: Multiple layers of pattern detection prevent secrets from slipping through
- Fail-Safe Defaults: Conservative profiles that redact more rather than less
- Complete Mediation: Every value is checked, nothing bypasses security checks
- Psychological Acceptability: Simple, transparent operation that users can understand and trust
- Always review dry-run output before processing files
- Use validate mode in CI/CD to catch accidental commits of secrets
- Use appropriate profiles - don't over-redact for internal use
- Store originals securely - keep unredacted files in secure locations
- Version control - commit only anonymized versions to public repos
- Use excludes wisely - exclude non-sensitive metadata to maintain readability
- Audit regularly - check output files to ensure sensitive data is redacted
$ ovatta --dry-run -i config.yaml -i secret.yaml
Processing: config.yaml
Processing: secret.yaml
╔════════════════════════════════════════╗
║ Ovatta Dry Run Report ║
╚════════════════════════════════════════╝
Profile: medium
Files processed: 2
Total entries to redact: 8
config.yaml (4 entries):
• database.password -> [REDACTED]
• api.token -> [REDACTED]
• api.secret_key -> [REDACTED]
• redis.url -> [REDACTED]
secret.yaml (4 entries):
• data.db-password -> [REDACTED]
• data.api-key -> [REDACTED]
• stringData.token -> [REDACTED]
• stringData.client-secret -> [REDACTED]
────────────────────────────────────────$ ovatta --validate config-anonymized.yaml secret-anonymized.yaml
╔════════════════════════════════════════╗
║ Ovatta Validation Report ║
╚════════════════════════════════════════╝
Profile: medium
Files to validate: 2
✓ config-anonymized.yaml - CLEAN
✓ secret-anonymized.yaml - CLEAN
────────────────────────────────────────
✓ All files are clean - no sensitive data found
$ echo $?
0$ ovatta --validate production.yaml staging.yaml
╔════════════════════════════════════════╗
║ Ovatta Validation Report ║
╚════════════════════════════════════════╝
Profile: medium
Files to validate: 2
✓ staging.yaml - CLEAN
✗ production.yaml - FOUND 5 SENSITIVE VALUE(S)
• database.password
• database.connection_string
• api.secret_key
• api.client_secret
• redis.password
────────────────────────────────────────
✗ Found 5 sensitive value(s) across 1 file(s)
$ echo $?
1$ ovatta --dry-run -i k8s-deployment.yaml -p full -e "kind,apiVersion,metadata.name"
Excluding patterns: [kind apiVersion metadata.name]
Processing: k8s-deployment.yaml
╔════════════════════════════════════════╗
║ Ovatta Dry Run Report ║
╚════════════════════════════════════════╝
Profile: full
Files processed: 1
Total entries to redact: 12
k8s-deployment.yaml (12 entries):
• metadata.namespace -> [REDACTED]
• metadata.labels.app -> [REDACTED]
• spec.replicas -> [REDACTED]
• spec.template.spec.containers[0].image -> [REDACTED]
• spec.template.spec.containers[0].env[0].value -> [REDACTED]
...
────────────────────────────────────────
# Notice: kind, apiVersion, metadata.name are NOT in the list!- Sanitize Infrastructure as Code: Anonymize Terraform/Ansible configs before sharing
- Clean Kubernetes Manifests: Prepare K8s resources for documentation
- Template Generation: Create safe example configs from production setups
- CloudFormation/ARM Templates: Remove secrets from infrastructure templates
name: Validate Configs
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Ovatta
run: |
wget https://github.com/yourusername/ovatta/releases/latest/download/ovatta
chmod +x ovatta
- name: Validate configuration files
run: |
./ovatta --validate configs/*.yaml deployment/*.yaml
- name: Check for secrets in anonymized files
run: |
./ovatta --validate -p full docs/examples/*.yamlvalidate-secrets:
stage: test
script:
- wget https://github.com/yourusername/ovatta/releases/latest/download/ovatta
- chmod +x ovatta
- ./ovatta --validate deployment/*.yaml || exit 1
only:
- merge_requests
- main#!/bin/bash
# .git/hooks/pre-commit
echo "Validating configuration files for secrets..."
./ovatta --validate configs/*.yaml deployment/*.yaml
if [ $? -ne 0 ]; then
echo " Secrets detected! Please anonymize before committing."
exit 1
fi
echo "No secrets detected"- Create Safe Examples: Generate realistic examples from production configs
- Tutorial Materials: Provide working configs without exposing secrets
- Troubleshooting: Share configs with support teams safely
- API Documentation: Include example requests/responses without credentials
- Starter Templates: Create realistic starter configs from your setup
- Example Configurations: Provide working examples that users can customize
- Testing Fixtures: Generate test data that looks realistic
- Documentation Examples: Include production-like configs in your docs
# 1. Validate current state
ovatta --validate config.yaml
# Exit code 1 - secrets found
# 2. Preview what will be changed
ovatta --dry-run config.yaml -p medium
# 3. Anonymize with specific excludes
ovatta -i config.yaml -o config-safe.yaml -p medium -e "version,region,env"
# 4. Verify the output is clean
ovatta --validate config-safe.yaml
# Exit code 0 - all clean!
# 5. Safe to commit
git add config-safe.yaml
git commit -m "Add example configuration"# Anonymize all environment configs
for env in dev staging prod; do
ovatta -i configs/${env}.yaml \
-o docs/examples/${env}-example.yaml \
-p full \
-e "environment,region,cluster_name" \
--force
done
# Validate all examples
ovatta --validate docs/examples/*.yaml#!/bin/bash
# deploy-check.sh
# Step 1: Validate deployment files don't contain hardcoded secrets
echo "Checking for hardcoded secrets..."
ovatta --validate deployment/*.yaml -p medium
if [ $? -ne 0 ]; then
echo "ERROR: Hardcoded secrets detected in deployment files!"
echo "Please use environment variables or secret management instead."
exit 1
fi
# Step 2: Create anonymized examples for documentation
echo "Generating documentation examples..."
ovatta deployment/*.yaml \
--output-dir docs/deployment-examples \
-p full \
-e "kind,apiVersion,metadata.name,metadata.namespace" \
--force
echo "Deployment validation passed!"Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with Cobra for CLI
- YAML parsing with gopkg.in/yaml.v3
- TOML parsing with go-toml