# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## What this is `openvpncertupdate` is a single-file Python + curses TUI tool for managing OpenVPN user certificates via EasyRSA 3.2.x. It lists expiring/expired certs, re-issues them with fresh keys, creates new certs, and delivers configs via Cryptgeon (one-time password URL) and email. ## Running ```bash pip install -r requirements.txt # just: cryptography>=41 python3 openvpncertupdate.py ``` Edit the `SETTINGS` block at the top of `openvpncertupdate.py` before first run. ## Tests ```bash python3 -m pytest tests/ -v python3 -m pytest tests/test_password.py -v # single file python3 -m pytest tests/test_pki.py::test_sorted_ascending -v # single test ``` ## File layout — sections inside `openvpncertupdate.py` | Section | Key symbols | |---|---| | SETTINGS | all-caps constants | | PKI | `CertInfo`, `load_expiring_certs()`, `_parse_index_line()` | | PASSWORD | `generate_password()` | | EASYRSA | `EasyRSAError`, `revoke_issued()`, `build_client_full()`, `gen_crl()`, `copy_crl()` | | METADATA | `load_metadata()`, `save_email()`, `get_email()` | | CONFIG | `build_ovpn()` → `vpn-configs/_/` | | CRYPTGEON | `CryptgeonError`, `create_note()` | | MAILER | `send_email()` | | TUI WIDGETS | `InputField`, `clamp()`, `draw_box()`, `init_colors()`, `COLOR_*` | | TUI DIALOGS | `show_confirm()`, `show_cert_form()`, `CertFormResult` | | TUI SCREEN | `show_main_screen()`, `Action`, `ScreenResult` | | APP | `CursesApp` | | ENTRY POINT | `main()` | ## Re-issue workflow 1. `revoke-issued ` — archives old key + CSR to `pki/revoked/` 2. `build-client-full --passout=pass:` — generates new key + cert 3. CRL **not** auto-updated during renewal; use "Regenerate CRL" menu item or `r` hotkey ## Key constraints - EasyRSA called with `--batch`; `--passin=pass:` omitted when `CA_PASSPHRASE=""` - Cryptgeon: `raw_key=os.urandom(32)`, `aes_key=SHA-256(raw_key)`, AES-256-GCM, URL fragment=`base64url(raw_key)` - `copy_crl()` does `chmod 644` after copy - Password: pos 1=uppercase, pos 2=lowercase (no j), pos 3-27=alphanumeric, pos 28=lowercase (no j); `oO01lI` banned everywhere - Inline file path: `/inline/private/.inline` - User emails stored in `/openvpncertupdate-metadata.json`