Metadata-Version: 2.4
Name: certreap
Version: 1.0.0
Summary: Revoke and delete Let's Encrypt certificates no nginx config references.
Project-URL: Homepage, https://www.veen.world/
Project-URL: Repository, https://github.com/kevinveenbirkenbach/certbot-reaper
Author-email: Kevin Veen-Birkenbach <kevin@veen.world>
License: MIT
License-File: LICENSE
Keywords: certbot,cleanup,cli,letsencrypt,nginx
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# ☠️ Certbot Reaper

[![GitHub Sponsors](https://img.shields.io/badge/Sponsor-GitHub%20Sponsors-blue?logo=github)](https://github.com/sponsors/kevinveenbirkenbach) [![Patreon](https://img.shields.io/badge/Support-Patreon-orange?logo=patreon)](https://www.patreon.com/c/kevinveenbirkenbach) [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20me%20a%20Coffee-Funding-yellow?logo=buymeacoffee)](https://buymeacoffee.com/kevinveenbirkenbach) [![PayPal](https://img.shields.io/badge/Donate-PayPal-blue?logo=paypal)](https://s.veen.world/paypaldonate)

**Certbot Reaper** is a Python utility that finds, revokes, and deletes unused Let's Encrypt certificates from your system – so you stay clean and secure. It compares existing certificates with active NGINX configurations and safely removes those that are no longer in use.

---

## 🧭 How it works

```mermaid
flowchart TD
    A["certreap"] --> B["walk the nginx config tree for *.conf"]
    B --> C["collect every domain-like string found"]
    A --> D["list the subdirectories of the cert dir"]
    D --> E["openssl x509 -text on cert.pem"]
    E --> F["read the DNS: entries"]
    F --> G{"any of them still referenced by nginx?"}
    C --> G
    G -- yes --> K["keep the certificate"]
    G -- no --> H{"--force?"}
    H -- no --> I{"confirm at the prompt?"}
    I -- no --> K
    I -- yes --> J
    H -- yes --> J["certbot revoke --delete-after-revoke"]
    J --> L["remove the certificate directory"]
```

A revoke that certbot rejects is reported, and the directory is removed anyway.

---

## 🚀 Features

- 🔍 Scans `/etc/letsencrypt/live` for all installed certificates
- 🧠 Checks if domain names are referenced in NGINX configs under `/etc/nginx/`
- 🗑️ Offers interactive or forced cleanup of unused certificates
- 🔁 Can be run manually or scheduled with `systemd` timers
- ⚙️ Supports `--force` and `--help` via `argparse`

---

## 📦 Installation

```bash
pip install certreap
```

pip is the single supported installation path.

---

## 🔧 Requirements

- **Python 3.10+** 🐍
- **`openssl`** on `PATH` — reads the DNS names out of each certificate
- **`certbot`** on `PATH` — performs the revocation

If either is missing, the command exits with code `127` and a one‑line error instead of a traceback. A missing `openssl` is **never** silently treated as "no certificates found".

---

## 🛠️ Usage

```bash
certreap               # Interactive mode (asks before deleting)
certreap --force       # Deletes unused certificates without confirmation
certreap --help        # Shows help message
```

Both scanned locations can be pointed elsewhere, which is what the integration tests use:

```bash
certreap --cert-dir /etc/letsencrypt/live --nginx-conf-dir /etc/nginx
```

### Exit codes

| Code | Meaning |
| --- | --- |
| `0` | Finished. A certificate certbot failed to revoke is reported and its directory is still removed. |
| `2` | Invalid command line arguments. |
| `127` | A required command is not installed. |

---

## 🧪 Development

```bash
make lint              # ruff check + ruff format --check
make format            # apply ruff format
make test              # unit + integration tests
make test-unit
make test-integration
make test-e2e          # install the package in a container and exercise the CLI
```

Tests run against the working tree — the `Makefile` puts `src/` on `PYTHONPATH`, so no install is needed. The integration tests stub `openssl` and `certbot` on `PATH`, so nothing real is ever revoked.

---

## 👤 Author

Developed by [Kevin Veen-Birkenbach](https://www.veen.world) 🧠
Feedback and contributions welcome!

---

## 📄 License

This project is licensed under the [MIT License](LICENSE).
Feel free to use, modify, and share it as you wish.
