Certificate Bundle for OTA Updates
The OTA process makes two separate TLS connections to two different hosts that use different root CAs:
| Step | Host | Purpose | Root CA |
|---|---|---|---|
| 1 | api.github.com | Fetch release metadata (tag name, asset URLs) | USERTrust ECC |
| 2 | release-assets.githubusercontent.com | Download firmware.bin | ISRG Root X1 (Let's Encrypt) |
Step 1 uses HTTPClient. Step 2 uses esp_https_ota.
Both share the same embedded certificate (server_cert_pem_start) configured in ota_update.cpp.
Therefore the single embedded cert file must cover both root CAs.
Why two CAs?
The firmware download URL https://github.com/.../firmware.bin returns an HTTP 302 redirect
to release-assets.githubusercontent.com. If only the USERTrust cert is embedded, Step 1 passes
but Step 2 fails with mbedTLS error -0x2700 (certificate verification failed).
Active bundle: cert/github_bundle.pemβ
Contains 3 root CA certificates (as of March 2026):
| # | CN | Used by | Valid until | SHA1 Fingerprint |
|---|---|---|---|---|
| 1 | USERTrust ECC Certification Authority | api.github.com, github.com | 2038-01-18 | D1:CB:CA:5D:B2:D5:2A:7F:69:3B:67:4D:E5:F0:5A:1D:0C:95:7D:F0 |
| 2 | USERTrust RSA Certification Authority | api.github.com, github.com (fallback) | 2038-01-18 | 2B:8F:1B:57:33:0D:BB:A2:D0:7A:6C:51:F7:0E:E9:0D:DA:B9:AD:8E |
| 3 | ISRG Root X1 | release-assets.githubusercontent.com | 2035-06-04 | CA:BD:2A:79:A1:07:6A:31:F2:1D:25:36:35:CB:03:9D:43:29:A5:E8 |
Rebuilding github_bundle.pemβ
Run through these steps whenever a certificate expires or GitHub switches CA provider.