Metadata-Version: 2.5
Name: foglock
Version: 0.1.0
Summary: Encrypt passwords and other secrets in config files: Fernet tokens, a master key file, key rotation, a CLI and Dynaconf support.
Project-URL: Homepage, https://github.com/frolpaxa/foglock
Project-URL: Repository, https://github.com/frolpaxa/foglock
Project-URL: Documentation, https://foglock.readthedocs.io
Project-URL: Issues, https://github.com/frolpaxa/foglock/issues
Author: Paul
License-Expression: MIT
License-File: LICENSE
Keywords: config,cryptography,dynaconf,encryption,fernet,passwords,secrets
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: System :: Systems Administration
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: cryptography>=3.1
Provides-Extra: docs
Requires-Dist: myst-parser; extra == 'docs'
Requires-Dist: shibuya; extra == 'docs'
Requires-Dist: sphinx>=7.0; extra == 'docs'
Provides-Extra: dynaconf
Requires-Dist: dynaconf>=3.2; extra == 'dynaconf'
Provides-Extra: test
Requires-Dist: dynaconf>=3.2; extra == 'test'
Requires-Dist: pytest; extra == 'test'
Description-Content-Type: text/markdown

# foglock

[![PyPI](https://img.shields.io/pypi/v/foglock.svg)](https://pypi.org/project/foglock/)
[![Python](https://img.shields.io/pypi/pyversions/foglock.svg)](https://pypi.org/project/foglock/)
[![Docs](https://readthedocs.org/projects/foglock/badge/?version=latest)](https://foglock.readthedocs.io)
[![Tests](https://github.com/frolpaxa/foglock/actions/workflows/ci.yml/badge.svg)](https://github.com/frolpaxa/foglock/actions/workflows/ci.yml)
[![License](https://img.shields.io/pypi/l/foglock.svg)](LICENSE)

**Keep passwords out of your config files, and keep the config files in git.**

```toml
[database]
user = "app"
password = "@enc gAAAAABm9x2k...Q7c="
```

Secrets are stored encrypted, right where they are used. The key lives in one file on
the server, outside the repository. Your app decrypts the values when it reads its
settings.

Documentation: [foglock.readthedocs.io](https://foglock.readthedocs.io)

- **Standard cryptography.** Fernet (AES-128-CBC with HMAC-SHA256) from
  [`cryptography`](https://cryptography.io). Each value gets a random IV, so equal
  passwords encrypt differently, and a modified value fails loudly instead of decrypting
  to garbage.
- **Works with any format.** An encrypted value is just a string starting with `@enc `,
  so it fits in TOML, YAML, JSON, INI and `.env` files alike.
- **Key rotation built in.** Add a new key, re-encrypt every file in one command, and
  retire the old key. Old values keep working in between.
- **Checks without revealing anything.** `foglock validate` confirms every value on a
  server decrypts with its key, and prints no secrets.
- **Dynaconf support.** One call and `@enc` values decrypt as settings are read.

## Install

```bash
pip install foglock
pip install "foglock[dynaconf]"   # with Dynaconf integration
```

Requires Python 3.9 or newer.

## Quick start

```bash
foglock genkey      # writes ./master.key (mode 0400); keep it out of git
foglock encrypt     # type the secret twice at a hidden prompt
```

```text
@enc gAAAAABm9x2k...Q7c=
```

Paste the value into your config, then decrypt it in your app:

```python
from foglock import Foglock

lock = Foglock.from_env()         # finds the key, see below
lock.decrypt(config["database"]["password"])

settings = lock.decrypt_tree(config)   # or decrypt every @enc value at once
```

With Dynaconf:

```python
from dynaconf import Dynaconf
from foglock.dynaconf import register

register()
settings = Dynaconf(settings_files=["settings.toml"])
settings.database.password   # plain text
```

## Where the key comes from

The first of these that is set wins:

1. `--key-file PATH` on the command line, or `Foglock.from_env("PATH")` in code
2. `FOGLOCK_KEY`: the key itself (comma-separated for several keys), handy in CI
3. `FOGLOCK_KEY_FILE`: path to the key file, e.g. `/etc/myapp/master.key`
4. `./master.key`

## Command line

| Command | What it does |
| --- | --- |
| `foglock genkey` | Create a key file. `--stdout` prints the key instead. |
| `foglock encrypt` | Encrypt a secret typed at a hidden prompt, or piped in with `--stdin`. |
| `foglock decrypt VALUE` | Print the secret behind one value. |
| `foglock validate FILE...` | Check every `@enc` value in the files decrypts. Prints `file:line: ok`, never the secret. |
| `foglock genkey --rotate` | Add a new key in front of the old ones. |
| `foglock rotate FILE...` | Re-encrypt every `@enc` value with the newest key. |

## Rotating the key

```bash
foglock genkey --rotate               # master.key now holds the new key and the old one
foglock rotate config/*.toml          # re-encrypt with the new key
foglock validate config/*.toml
# then delete the old key (the last line) from master.key
```

`rotate` checks every value before it touches a file, so a value it cannot decrypt
leaves all the files as they were.

## What it is not

- **Not for storing user passwords.** Passwords your users log in with must be
  *hashed* (argon2, bcrypt, scrypt), never encrypted. foglock is for secrets your
  app itself has to use, such as database passwords and API tokens.
- **Not a secrets manager.** There is no server, no access control and no audit log.
  Anyone who has the key file can read every value. If you need those things, use
  Vault, AWS Secrets Manager or similar.

## License

[MIT](LICENSE)
