Development Getting Started#

Installation#

This project is managed by uv, which can be installed through these instructions.

The dev dependencies can be installed like so:

uv sync

This will create a virtual environment under .venv that you can optionally activate like so:

. .venv/bin/activate

Alternatively, you can prefix your commands with uv run, example: uv run certwrangler --help.

Then copy certwrangler.example.yaml to ~/.config/certwrangler.yaml and fill it out with your info.

The certwrangler CLI utility will be installed in the virtual environment, more information about the CLI can be found here: CLI Reference

Pre-commit#

This project uses pre-commit to enforce style and typing standards on the code. To setup pre-commit to run before each commit, run the following:

uv run pre-commit install --install-hooks

Bumping Dependencies#

Github actions, pre-commit, and uv dependencies can be bumped by running the following:

uv run tox -m update

Dev shell#

If certwrangler is installed with the development dependency groups (the default when using uv sync) then you’ll also have access to the dev-shell sub-command. This provides you with an IPython environment pre-loaded with the various certwrangler modules loaded, which is helpful for playing around with the various types to test out changes:

$ uv run certwrangler dev-shell
2026-06-02 15:23:16,490: INFO [certwrangler.utils, load_config(), line 128, thread MainThread] - Loading config from /home/<username>/.config/certwrangler.yaml
2026-06-02 15:23:16,587: INFO [certwrangler.utils, load_config(), line 151, thread MainThread] - Config loaded from '/home/<username>/.config/certwrangler.yaml'.
Welcome to certwrangler's development shell!
  Python 3.13.13 (main, May 10 2026, 19:26:54) [Clang 22.1.3 ] on linux.
Loaded certwrangler variables:
  ctx
  config
  controllers
  dns
  models
  reconcilers
Config loaded but not initialized, initialize with:
  config.initialize()
Tip: You can use Ctrl-O to force a new line in terminal IPython

In [1]:

Project layout#

./
├── .github/                   <- GitHub Actions
├── .vscode/
├── docs/                      <- Docs
├── script/                    <- Scripts used in packaging
├── src/                       <- The certwrangler python module
├── tests/                     <- Tests
├── .gitignore
├── .pre-commit-config.yaml
├── certwrangler.example.yaml  <- Example config file
├── LICENSE
├── pyproject.toml
├── README.md
└── uv.lock

Code layout#

src/
├── certwrangler
│   ├── solvers                <- Solver plugins
│   │   ├── dummy.py
│   │   ├── edgedns.py
│   │   ├── __init__.py
│   │   └── lexicon.py
│   ├── state_managers         <- State manager plugins
│   │   ├── dummy.py
│   │   ├── __init__.py
│   │   └── local.py
│   ├── stores                 <- Store plugins
│   │   ├── dummy.py
│   │   ├── __init__.py
│   │   ├── local.py
│   │   └── vault.py
│   ├── controllers.py         <- Account and cert ACME controller code
│   ├── daemon.py              <- Daemon/threading manager
│   ├── dns.py                 <- Misc DNS functions
│   ├── exceptions.py          <- Exception definitions
│   ├── http.py                <- HTTP server for metrics
│   ├── __init__.py
│   ├── metrics.py             <- Metrics registry
│   ├── models.py              <- Core models
│   ├── reconcilers.py         <- Reconciler functions
│   ├── shell.py               <- CLI
│   ├── types.py               <- Type annotations for pydantic
│   └── utils.py               <- App state and misc constants