cert-utils 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cert_utils/__init__.py +8 -0
- cert_utils/compat/__init__.py +0 -0
- cert_utils/compat/cryptography.py +172 -0
- cert_utils/compat/openssl.py +860 -0
- cert_utils/compat/pyopenssl.py +207 -0
- cert_utils/compat/signatures.py +114 -0
- cert_utils/conditionals.py +121 -0
- cert_utils/convert.py +307 -0
- cert_utils/core.py +3075 -0
- cert_utils/errors.py +54 -0
- cert_utils/letsencrypt-certs/deprecated/e5-cross.pem +26 -0
- cert_utils/letsencrypt-certs/deprecated/e5.pem +17 -0
- cert_utils/letsencrypt-certs/deprecated/e6-cross.pem +26 -0
- cert_utils/letsencrypt-certs/deprecated/e6.pem +17 -0
- cert_utils/letsencrypt-certs/deprecated/e7-cross.pem +26 -0
- cert_utils/letsencrypt-certs/deprecated/e7.pem +17 -0
- cert_utils/letsencrypt-certs/deprecated/e8-cross.pem +26 -0
- cert_utils/letsencrypt-certs/deprecated/e8.pem +17 -0
- cert_utils/letsencrypt-certs/deprecated/e9-cross.pem +26 -0
- cert_utils/letsencrypt-certs/deprecated/e9.pem +17 -0
- cert_utils/letsencrypt-certs/deprecated/fakeleintermediatex1.pem +28 -0
- cert_utils/letsencrypt-certs/deprecated/fakelerootx1.pem +29 -0
- cert_utils/letsencrypt-certs/deprecated/isrg-root-ocsp-x1.pem +28 -0
- cert_utils/letsencrypt-certs/deprecated/isrg-root-x1-cross-signed.pem +31 -0
- cert_utils/letsencrypt-certs/deprecated/isrg-root-x2-cross-signed.pem +26 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-e1.pem +17 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-e2.pem +17 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-r3-cross-signed.pem +26 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-r3.pem +30 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-r4-cross-signed.pem +26 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-r4.pem +30 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-x1-cross-signed.pem +27 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-x2-cross-signed.pem +27 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-x3-cross-signed.pem +27 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-x4-cross-signed.pem +27 -0
- cert_utils/letsencrypt-certs/deprecated/letsencrypt-stg-root-x2-signed-by-x1.pem +27 -0
- cert_utils/letsencrypt-certs/deprecated/letsencryptauthorityx1.pem +32 -0
- cert_utils/letsencrypt-certs/deprecated/letsencryptauthorityx2.pem +32 -0
- cert_utils/letsencrypt-certs/deprecated/letsencryptauthorityx3.pem +32 -0
- cert_utils/letsencrypt-certs/deprecated/letsencryptauthorityx4.pem +32 -0
- cert_utils/letsencrypt-certs/deprecated/r10.pem +29 -0
- cert_utils/letsencrypt-certs/deprecated/r11.pem +29 -0
- cert_utils/letsencrypt-certs/deprecated/r12.pem +29 -0
- cert_utils/letsencrypt-certs/deprecated/r13.pem +29 -0
- cert_utils/letsencrypt-certs/deprecated/r14.pem +29 -0
- cert_utils/letsencrypt-certs/deprecated/trustid-x3-root.p7c +0 -0
- cert_utils/letsencrypt-certs/deprecated/trustid-x3-root.pem +20 -0
- cert_utils/letsencrypt-certs/int-ye1.pem +16 -0
- cert_utils/letsencrypt-certs/int-ye2.pem +16 -0
- cert_utils/letsencrypt-certs/int-ye3.pem +16 -0
- cert_utils/letsencrypt-certs/int-yr1.pem +28 -0
- cert_utils/letsencrypt-certs/int-yr2.pem +28 -0
- cert_utils/letsencrypt-certs/int-yr3.pem +28 -0
- cert_utils/letsencrypt-certs/isrg-root-x2.pem +14 -0
- cert_utils/letsencrypt-certs/isrgrootx1.pem +31 -0
- cert_utils/letsencrypt-certs/isrgrootx1.pkix +0 -0
- cert_utils/letsencrypt-certs/letsencrypt-stg-root-x1.pem +32 -0
- cert_utils/letsencrypt-certs/letsencrypt-stg-root-x2.pem +15 -0
- cert_utils/letsencrypt-certs/root-x2-by-x1.pem +26 -0
- cert_utils/letsencrypt-certs/root-ye-by-x2.pem +17 -0
- cert_utils/letsencrypt-certs/root-ye.pem +12 -0
- cert_utils/letsencrypt-certs/root-yr-by-x1.pem +34 -0
- cert_utils/letsencrypt-certs/root-yr.pem +30 -0
- cert_utils/letsencrypt_info/__init__.py +6 -0
- cert_utils/letsencrypt_info/certificates.py +1074 -0
- cert_utils/letsencrypt_info/rate_limits.py +414 -0
- cert_utils/letsencrypt_info/types.py +120 -0
- cert_utils/model.py +181 -0
- cert_utils/network.py +28 -0
- cert_utils/py.typed +0 -0
- cert_utils/scripts/__init__.py +0 -0
- cert_utils/scripts/cert_info.py +332 -0
- cert_utils/utils.py +388 -0
- cert_utils-1.2.0.dist-info/METADATA +259 -0
- cert_utils-1.2.0.dist-info/RECORD +79 -0
- cert_utils-1.2.0.dist-info/WHEEL +5 -0
- cert_utils-1.2.0.dist-info/entry_points.txt +2 -0
- cert_utils-1.2.0.dist-info/licenses/LICENSE.txt +224 -0
- cert_utils-1.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
"""Versioned public production policy snapshot, not a live quota counter.
|
|
2
|
+
|
|
3
|
+
Values are JSON-compatible. See docs/rate-limits.md for modelling decisions,
|
|
4
|
+
rounded refill figures, exemption boundaries and migration from schema 1.
|
|
5
|
+
Importing this module does not load the bundled CA certificates.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# local
|
|
9
|
+
from .types import Catalogue
|
|
10
|
+
|
|
11
|
+
RATE_LIMITS: Catalogue = {
|
|
12
|
+
"schema_version": 2,
|
|
13
|
+
"policy_environment": "production",
|
|
14
|
+
"source": {
|
|
15
|
+
"url": "https://letsencrypt.org/docs/rate-limits/",
|
|
16
|
+
"document_last_modified": "2026-08-05",
|
|
17
|
+
"checked_on": "2026-10-28",
|
|
18
|
+
"checkout": "bb7e7238",
|
|
19
|
+
"path": "content/en/docs/rate-limits.md",
|
|
20
|
+
},
|
|
21
|
+
"limits": {
|
|
22
|
+
"registrations_per_ip": {
|
|
23
|
+
"kind": "token_bucket",
|
|
24
|
+
"title": "New Registrations per IP Address",
|
|
25
|
+
"capacity": 10,
|
|
26
|
+
"unit": "account",
|
|
27
|
+
"scope": {
|
|
28
|
+
"dimensions": ["source_ip"],
|
|
29
|
+
"key_rules": {"source_ip": "exact IPv4 or IPv6 address"},
|
|
30
|
+
},
|
|
31
|
+
"nominal_window_seconds": 10800,
|
|
32
|
+
"refill": {
|
|
33
|
+
"tokens": 1,
|
|
34
|
+
"period_seconds": 1080,
|
|
35
|
+
"precision": "published",
|
|
36
|
+
},
|
|
37
|
+
"cost_per_event": 1,
|
|
38
|
+
"counted_event": "account_created",
|
|
39
|
+
"enforced_on": "new_account",
|
|
40
|
+
"renewal_exemptions": {
|
|
41
|
+
"ari_replacement": "not_applicable",
|
|
42
|
+
"same_identifier_set": "not_applicable",
|
|
43
|
+
},
|
|
44
|
+
"override_scopes": [],
|
|
45
|
+
"source_url": (
|
|
46
|
+
"https://letsencrypt.org/docs/rate-limits/#new-registrations-per-ip-address"
|
|
47
|
+
),
|
|
48
|
+
},
|
|
49
|
+
"registrations_per_ipv6_range": {
|
|
50
|
+
"kind": "token_bucket",
|
|
51
|
+
"title": "New Registrations per IPv6 Range",
|
|
52
|
+
"capacity": 500,
|
|
53
|
+
"unit": "account",
|
|
54
|
+
"scope": {
|
|
55
|
+
"dimensions": ["source_ipv6_48"],
|
|
56
|
+
"key_rules": {
|
|
57
|
+
"source_ipv6_48": "IPv6 address masked to /48; additional to per-IP quota"
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
"nominal_window_seconds": 10800,
|
|
61
|
+
"refill": {
|
|
62
|
+
"tokens": 1,
|
|
63
|
+
"period_seconds": 22,
|
|
64
|
+
"precision": "published_rounded",
|
|
65
|
+
},
|
|
66
|
+
"cost_per_event": 1,
|
|
67
|
+
"counted_event": "account_created",
|
|
68
|
+
"enforced_on": "new_account",
|
|
69
|
+
"renewal_exemptions": {
|
|
70
|
+
"ari_replacement": "not_applicable",
|
|
71
|
+
"same_identifier_set": "not_applicable",
|
|
72
|
+
},
|
|
73
|
+
"override_scopes": [],
|
|
74
|
+
"source_url": (
|
|
75
|
+
"https://letsencrypt.org/docs/rate-limits/#new-registrations-per-ipv6-range"
|
|
76
|
+
),
|
|
77
|
+
},
|
|
78
|
+
"orders_per_account": {
|
|
79
|
+
"kind": "token_bucket",
|
|
80
|
+
"title": "New Orders per Account",
|
|
81
|
+
"capacity": 300,
|
|
82
|
+
"unit": "order",
|
|
83
|
+
"scope": {"dimensions": ["account"], "key_rules": {}},
|
|
84
|
+
"nominal_window_seconds": 10800,
|
|
85
|
+
"refill": {
|
|
86
|
+
"tokens": 1,
|
|
87
|
+
"period_seconds": 36,
|
|
88
|
+
"precision": "published",
|
|
89
|
+
},
|
|
90
|
+
"cost_per_event": 1,
|
|
91
|
+
"counted_event": "order_created",
|
|
92
|
+
"enforced_on": "new_order",
|
|
93
|
+
"renewal_exemptions": {
|
|
94
|
+
"ari_replacement": "documented_exempt",
|
|
95
|
+
"same_identifier_set": "exempt",
|
|
96
|
+
},
|
|
97
|
+
"override_scopes": ["account"],
|
|
98
|
+
"source_url": (
|
|
99
|
+
"https://letsencrypt.org/docs/rate-limits/#new-orders-per-account"
|
|
100
|
+
),
|
|
101
|
+
},
|
|
102
|
+
"certificates_per_registered_domain": {
|
|
103
|
+
"kind": "token_bucket",
|
|
104
|
+
"title": "New Certificates per Registered Domain",
|
|
105
|
+
"capacity": 50,
|
|
106
|
+
"unit": "certificate",
|
|
107
|
+
"scope": {
|
|
108
|
+
"dimensions": ["registered_domain_or_ip_range"],
|
|
109
|
+
"key_rules": {
|
|
110
|
+
"dns": (
|
|
111
|
+
"Public Suffix List registered "
|
|
112
|
+
"domain; not simply the last two labels"
|
|
113
|
+
),
|
|
114
|
+
"ipv4": "exact address",
|
|
115
|
+
"ipv6": "/64 network",
|
|
116
|
+
"sharing": "global across accounts",
|
|
117
|
+
"multi_identifier": "evaluate each distinct registered-domain/IP-range bucket",
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
"nominal_window_seconds": 604800,
|
|
121
|
+
"refill": {
|
|
122
|
+
"tokens": 1,
|
|
123
|
+
"period_seconds": 12120,
|
|
124
|
+
"precision": "published_rounded",
|
|
125
|
+
},
|
|
126
|
+
"cost_per_event": 1,
|
|
127
|
+
"counted_event": "certificate_issued",
|
|
128
|
+
"enforced_on": "new_order",
|
|
129
|
+
"renewal_exemptions": {
|
|
130
|
+
"ari_replacement": "documented_exempt",
|
|
131
|
+
"same_identifier_set": "exempt",
|
|
132
|
+
},
|
|
133
|
+
"override_scopes": ["registered_domain", "account"],
|
|
134
|
+
"source_url": (
|
|
135
|
+
"https://letsencrypt.org/docs/rate-limits/#new-certificates-per-registered-domain"
|
|
136
|
+
),
|
|
137
|
+
},
|
|
138
|
+
"certificates_per_exact_identifier_set": {
|
|
139
|
+
"kind": "token_bucket",
|
|
140
|
+
"title": "New Certificates per Exact Set of Identifiers",
|
|
141
|
+
"capacity": 5,
|
|
142
|
+
"unit": "certificate",
|
|
143
|
+
"scope": {
|
|
144
|
+
"dimensions": ["identifier_set"],
|
|
145
|
+
"key_rules": {
|
|
146
|
+
"normalization": "case-insensitive and order-independent identifiers",
|
|
147
|
+
"sharing": "global across accounts",
|
|
148
|
+
"ip_canonicalization": (
|
|
149
|
+
"not specified by this source; do "
|
|
150
|
+
"not infer string equivalence rules"
|
|
151
|
+
),
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
"nominal_window_seconds": 604800,
|
|
155
|
+
"refill": {
|
|
156
|
+
"tokens": 1,
|
|
157
|
+
"period_seconds": 122400,
|
|
158
|
+
"precision": "published_rounded",
|
|
159
|
+
},
|
|
160
|
+
"cost_per_event": 1,
|
|
161
|
+
"counted_event": "certificate_issued",
|
|
162
|
+
"enforced_on": "new_order",
|
|
163
|
+
"renewal_exemptions": {
|
|
164
|
+
"ari_replacement": "documented_exempt",
|
|
165
|
+
"same_identifier_set": "not_exempt",
|
|
166
|
+
},
|
|
167
|
+
"override_scopes": [],
|
|
168
|
+
"source_url": (
|
|
169
|
+
"https://letsencrypt.org/docs/rate-limits/#new-certificates-per-exact-set-of-identifiers"
|
|
170
|
+
),
|
|
171
|
+
},
|
|
172
|
+
"authorization_failures_per_identifier_account": {
|
|
173
|
+
"kind": "token_bucket",
|
|
174
|
+
"title": "Authorization Failures per Identifier per Account",
|
|
175
|
+
"capacity": 5,
|
|
176
|
+
"unit": "failure",
|
|
177
|
+
"scope": {
|
|
178
|
+
"dimensions": ["account", "identifier"],
|
|
179
|
+
"key_rules": {"identifier": "DNS name or IP address"},
|
|
180
|
+
},
|
|
181
|
+
"nominal_window_seconds": 3600,
|
|
182
|
+
"refill": {
|
|
183
|
+
"tokens": 1,
|
|
184
|
+
"period_seconds": 720,
|
|
185
|
+
"precision": "published",
|
|
186
|
+
},
|
|
187
|
+
"cost_per_event": 1,
|
|
188
|
+
"counted_event": "authorization_failed",
|
|
189
|
+
"enforced_on": "new_order",
|
|
190
|
+
"renewal_exemptions": {
|
|
191
|
+
"ari_replacement": "documented_exempt",
|
|
192
|
+
"same_identifier_set": "not_exempt",
|
|
193
|
+
},
|
|
194
|
+
"override_scopes": [],
|
|
195
|
+
"source_url": (
|
|
196
|
+
"https://letsencrypt.org/docs/rate-limits/#authorization-failures-per-identifier-per-account"
|
|
197
|
+
),
|
|
198
|
+
},
|
|
199
|
+
"consecutive_authorization_failures": {
|
|
200
|
+
"kind": "consecutive_failure_guard",
|
|
201
|
+
"title": (
|
|
202
|
+
"Consecutive Authorization " "Failures per Identifier per Account"
|
|
203
|
+
),
|
|
204
|
+
"capacity": 1152,
|
|
205
|
+
"unit": "failure",
|
|
206
|
+
"scope": {
|
|
207
|
+
"dimensions": ["account", "identifier"],
|
|
208
|
+
"key_rules": {"identifier": "DNS name or IP address"},
|
|
209
|
+
},
|
|
210
|
+
"nominal_window_seconds": None,
|
|
211
|
+
"refill": {
|
|
212
|
+
"tokens": 1,
|
|
213
|
+
"period_seconds": 86400,
|
|
214
|
+
"precision": "published",
|
|
215
|
+
},
|
|
216
|
+
"cost_per_event": 1,
|
|
217
|
+
"counted_event": "consecutive_authorization_failed",
|
|
218
|
+
"enforced_on": "new_order",
|
|
219
|
+
"renewal_exemptions": {
|
|
220
|
+
"ari_replacement": "documented_exempt",
|
|
221
|
+
"same_identifier_set": "not_exempt",
|
|
222
|
+
},
|
|
223
|
+
"override_scopes": [],
|
|
224
|
+
"source_url": (
|
|
225
|
+
"https://letsencrypt.org/docs/rate-limits/#consecutive-authorization-failures-per-identifier-per-account"
|
|
226
|
+
),
|
|
227
|
+
"reset_on": "authorization_succeeded",
|
|
228
|
+
"exhaustion_action": "identifier_issuance_paused",
|
|
229
|
+
"recovery": (
|
|
230
|
+
"self_service_unpause_link; automatic "
|
|
231
|
+
"unpause semantics not specified"
|
|
232
|
+
),
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
"endpoints": {
|
|
236
|
+
"/acme/new-nonce": {
|
|
237
|
+
"kind": "endpoint_bucket",
|
|
238
|
+
"unit": "request",
|
|
239
|
+
"cost_per_event": 1,
|
|
240
|
+
"scope": ["source_ip"],
|
|
241
|
+
"refill": {
|
|
242
|
+
"tokens": 20,
|
|
243
|
+
"period_seconds": 1,
|
|
244
|
+
"precision": "published",
|
|
245
|
+
},
|
|
246
|
+
"capacity": 10,
|
|
247
|
+
"exhaustion_http_status": 503,
|
|
248
|
+
"retry_after_header": True,
|
|
249
|
+
"source_url": (
|
|
250
|
+
"https://letsencrypt.org/docs/rate-limits/#overall-requests-limit"
|
|
251
|
+
),
|
|
252
|
+
},
|
|
253
|
+
"/acme/new-account": {
|
|
254
|
+
"kind": "endpoint_bucket",
|
|
255
|
+
"unit": "request",
|
|
256
|
+
"cost_per_event": 1,
|
|
257
|
+
"scope": ["source_ip"],
|
|
258
|
+
"refill": {
|
|
259
|
+
"tokens": 5,
|
|
260
|
+
"period_seconds": 1,
|
|
261
|
+
"precision": "published",
|
|
262
|
+
},
|
|
263
|
+
"capacity": 15,
|
|
264
|
+
"exhaustion_http_status": 503,
|
|
265
|
+
"retry_after_header": True,
|
|
266
|
+
"source_url": (
|
|
267
|
+
"https://letsencrypt.org/docs/rate-limits/#overall-requests-limit"
|
|
268
|
+
),
|
|
269
|
+
},
|
|
270
|
+
"/acme/new-order": {
|
|
271
|
+
"kind": "endpoint_bucket",
|
|
272
|
+
"unit": "request",
|
|
273
|
+
"cost_per_event": 1,
|
|
274
|
+
"scope": ["source_ip"],
|
|
275
|
+
"refill": {
|
|
276
|
+
"tokens": 300,
|
|
277
|
+
"period_seconds": 1,
|
|
278
|
+
"precision": "published",
|
|
279
|
+
},
|
|
280
|
+
"capacity": 200,
|
|
281
|
+
"exhaustion_http_status": 503,
|
|
282
|
+
"retry_after_header": True,
|
|
283
|
+
"source_url": (
|
|
284
|
+
"https://letsencrypt.org/docs/rate-limits/#overall-requests-limit"
|
|
285
|
+
),
|
|
286
|
+
},
|
|
287
|
+
"/acme/revoke-cert": {
|
|
288
|
+
"kind": "endpoint_bucket",
|
|
289
|
+
"unit": "request",
|
|
290
|
+
"cost_per_event": 1,
|
|
291
|
+
"scope": ["source_ip"],
|
|
292
|
+
"refill": {
|
|
293
|
+
"tokens": 10,
|
|
294
|
+
"period_seconds": 1,
|
|
295
|
+
"precision": "published",
|
|
296
|
+
},
|
|
297
|
+
"capacity": 100,
|
|
298
|
+
"exhaustion_http_status": 503,
|
|
299
|
+
"retry_after_header": True,
|
|
300
|
+
"source_url": (
|
|
301
|
+
"https://letsencrypt.org/docs/rate-limits/#overall-requests-limit"
|
|
302
|
+
),
|
|
303
|
+
},
|
|
304
|
+
"/acme/renewal-info": {
|
|
305
|
+
"kind": "endpoint_bucket",
|
|
306
|
+
"unit": "request",
|
|
307
|
+
"cost_per_event": 1,
|
|
308
|
+
"scope": ["source_ip"],
|
|
309
|
+
"refill": {
|
|
310
|
+
"tokens": 1000,
|
|
311
|
+
"period_seconds": 1,
|
|
312
|
+
"precision": "published",
|
|
313
|
+
},
|
|
314
|
+
"capacity": 100,
|
|
315
|
+
"exhaustion_http_status": 503,
|
|
316
|
+
"retry_after_header": True,
|
|
317
|
+
"source_url": (
|
|
318
|
+
"https://letsencrypt.org/docs/rate-limits/#overall-requests-limit"
|
|
319
|
+
),
|
|
320
|
+
},
|
|
321
|
+
"/acme/*": {
|
|
322
|
+
"kind": "endpoint_bucket",
|
|
323
|
+
"unit": "request",
|
|
324
|
+
"cost_per_event": 1,
|
|
325
|
+
"scope": ["source_ip"],
|
|
326
|
+
"refill": {
|
|
327
|
+
"tokens": 250,
|
|
328
|
+
"period_seconds": 1,
|
|
329
|
+
"precision": "published",
|
|
330
|
+
},
|
|
331
|
+
"capacity": 125,
|
|
332
|
+
"exhaustion_http_status": 503,
|
|
333
|
+
"retry_after_header": True,
|
|
334
|
+
"source_url": (
|
|
335
|
+
"https://letsencrypt.org/docs/rate-limits/#overall-requests-limit"
|
|
336
|
+
),
|
|
337
|
+
},
|
|
338
|
+
"/directory": {
|
|
339
|
+
"kind": "endpoint_bucket",
|
|
340
|
+
"unit": "request",
|
|
341
|
+
"cost_per_event": 1,
|
|
342
|
+
"scope": ["source_ip"],
|
|
343
|
+
"refill": {
|
|
344
|
+
"tokens": 40,
|
|
345
|
+
"period_seconds": 1,
|
|
346
|
+
"precision": "published",
|
|
347
|
+
},
|
|
348
|
+
"capacity": 40,
|
|
349
|
+
"exhaustion_http_status": 503,
|
|
350
|
+
"retry_after_header": True,
|
|
351
|
+
"source_url": (
|
|
352
|
+
"https://letsencrypt.org/docs/rate-limits/#overall-requests-limit"
|
|
353
|
+
),
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
"endpoint_matching": {
|
|
357
|
+
"patterns": "literal paths and /acme/* prefix pattern",
|
|
358
|
+
"overlap_policy": (
|
|
359
|
+
"not specified by source; do not " "assume precedence or additive buckets"
|
|
360
|
+
),
|
|
361
|
+
"renewal_exemption": (
|
|
362
|
+
"transport/load-balancer applicability not "
|
|
363
|
+
"specified; do not disable request pacing for ARI"
|
|
364
|
+
),
|
|
365
|
+
},
|
|
366
|
+
"constraints": {
|
|
367
|
+
"identifiers_per_certificate": {
|
|
368
|
+
"kind": "profile_constraint",
|
|
369
|
+
"unit": "identifier",
|
|
370
|
+
"scope": ["certificate", "profile"],
|
|
371
|
+
"maximum_by_profile": {
|
|
372
|
+
"classic": 100,
|
|
373
|
+
"tlsserver": 25,
|
|
374
|
+
"shortlived": 25,
|
|
375
|
+
},
|
|
376
|
+
"source_url": "https://letsencrypt.org/docs/profiles/",
|
|
377
|
+
"checked_on": "2026-10-28",
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
"renewals": {
|
|
381
|
+
"ari_replacement": {
|
|
382
|
+
"conditions": [
|
|
383
|
+
"order explicitly replaces an existing certificate using ARI",
|
|
384
|
+
"at least one identifier overlaps the replaced certificate",
|
|
385
|
+
"certificate has not already been replaced using ARI",
|
|
386
|
+
],
|
|
387
|
+
"exempt_from": (
|
|
388
|
+
"documented certificate-issuance rate "
|
|
389
|
+
"limits; not an arbitrary ARI-capable request"
|
|
390
|
+
),
|
|
391
|
+
"server_decides_eligibility": True,
|
|
392
|
+
},
|
|
393
|
+
"same_identifier_set": {
|
|
394
|
+
"normalization": "ignore capitalization and identifier ordering",
|
|
395
|
+
"exempt_limit_ids": [
|
|
396
|
+
"orders_per_account",
|
|
397
|
+
"certificates_per_registered_domain",
|
|
398
|
+
],
|
|
399
|
+
"other_limits": "still apply unless the server grants an exemption",
|
|
400
|
+
},
|
|
401
|
+
},
|
|
402
|
+
"retry_policy": {
|
|
403
|
+
"honor_retry_after": True,
|
|
404
|
+
"multiple_exhausted_limits": "wait until the latest applicable retry time",
|
|
405
|
+
"revocation_resets_quota": False,
|
|
406
|
+
"quota_is_shared": (
|
|
407
|
+
"local estimates cannot observe other "
|
|
408
|
+
"clients or account-specific overrides"
|
|
409
|
+
),
|
|
410
|
+
},
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
# Public historical name, now explicitly schema version 2.
|
|
414
|
+
LIMITS = RATE_LIMITS
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Shared TypedDict definitions for certificate and rate-limit metadata."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from typing import List
|
|
5
|
+
from typing import Optional
|
|
6
|
+
from typing import Tuple
|
|
7
|
+
from typing import Union
|
|
8
|
+
|
|
9
|
+
from typing_extensions import Literal
|
|
10
|
+
from typing_extensions import Required
|
|
11
|
+
from typing_extensions import TypedDict
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Source(TypedDict):
|
|
15
|
+
url: str
|
|
16
|
+
document_last_modified: str
|
|
17
|
+
checked_on: str
|
|
18
|
+
checkout: str
|
|
19
|
+
path: str
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Refill(TypedDict):
|
|
23
|
+
tokens: int
|
|
24
|
+
period_seconds: int
|
|
25
|
+
precision: str
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Scope(TypedDict):
|
|
29
|
+
dimensions: List[str]
|
|
30
|
+
key_rules: Dict[str, str]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Limit(TypedDict, total=False):
|
|
34
|
+
kind: Required[Literal["token_bucket", "consecutive_failure_guard"]]
|
|
35
|
+
title: Required[str]
|
|
36
|
+
capacity: Required[int]
|
|
37
|
+
unit: Required[str]
|
|
38
|
+
scope: Required[Scope]
|
|
39
|
+
nominal_window_seconds: Required[Optional[int]]
|
|
40
|
+
refill: Required[Refill]
|
|
41
|
+
counted_event: Required[str]
|
|
42
|
+
cost_per_event: Required[int]
|
|
43
|
+
enforced_on: Required[str]
|
|
44
|
+
renewal_exemptions: Required[Dict[str, str]]
|
|
45
|
+
override_scopes: Required[List[str]]
|
|
46
|
+
source_url: Required[str]
|
|
47
|
+
reset_on: str
|
|
48
|
+
exhaustion_action: str
|
|
49
|
+
recovery: str
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class Endpoint(TypedDict):
|
|
53
|
+
kind: str
|
|
54
|
+
unit: str
|
|
55
|
+
cost_per_event: int
|
|
56
|
+
scope: List[str]
|
|
57
|
+
refill: Refill
|
|
58
|
+
capacity: int
|
|
59
|
+
exhaustion_http_status: int
|
|
60
|
+
retry_after_header: bool
|
|
61
|
+
source_url: str
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class Constraint(TypedDict):
|
|
65
|
+
kind: str
|
|
66
|
+
unit: str
|
|
67
|
+
scope: List[str]
|
|
68
|
+
maximum_by_profile: Dict[str, int]
|
|
69
|
+
source_url: str
|
|
70
|
+
checked_on: str
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class Renewal(TypedDict, total=False):
|
|
74
|
+
conditions: List[str]
|
|
75
|
+
exempt_from: str
|
|
76
|
+
server_decides_eligibility: bool
|
|
77
|
+
normalization: str
|
|
78
|
+
exempt_limit_ids: List[str]
|
|
79
|
+
other_limits: str
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class Catalogue(TypedDict):
|
|
83
|
+
schema_version: int
|
|
84
|
+
policy_environment: str
|
|
85
|
+
source: Source
|
|
86
|
+
limits: Dict[str, Limit]
|
|
87
|
+
endpoints: Dict[str, Endpoint]
|
|
88
|
+
endpoint_matching: Dict[str, str]
|
|
89
|
+
constraints: Dict[str, Constraint]
|
|
90
|
+
renewals: Dict[str, Renewal]
|
|
91
|
+
retry_policy: Dict[str, Union[str, bool]]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
CERT_CA_PAYLOAD = TypedDict(
|
|
95
|
+
"CERT_CA_PAYLOAD",
|
|
96
|
+
{
|
|
97
|
+
"display_name": Required[str],
|
|
98
|
+
"url_pem": Required[str],
|
|
99
|
+
"is_trusted_root": Optional[bool],
|
|
100
|
+
"is_untrusted_root": Optional[bool],
|
|
101
|
+
"is_self_signed": Optional[bool],
|
|
102
|
+
"signed_by": Required[str],
|
|
103
|
+
"is_active": Required[bool],
|
|
104
|
+
"is_retired": Optional[bool],
|
|
105
|
+
"is_backup": Optional[bool],
|
|
106
|
+
"key_technology": Required[str],
|
|
107
|
+
"cert.fingerprints": Optional[Dict[str, str]],
|
|
108
|
+
".enddate": Optional[Tuple[int, ...]],
|
|
109
|
+
"compatibility": Optional[
|
|
110
|
+
Dict[str, Tuple[str, Optional[str], Optional[str]]]
|
|
111
|
+
], # platform: (min, max, note)
|
|
112
|
+
"alternates": Optional[List[str]],
|
|
113
|
+
"alternate_of": Optional[str],
|
|
114
|
+
"letsencrypt_serial": Optional[str],
|
|
115
|
+
# loaded via deprecated processors
|
|
116
|
+
"cert_pem": Optional[str],
|
|
117
|
+
"cert_pem_md5": Optional[str],
|
|
118
|
+
},
|
|
119
|
+
total=False,
|
|
120
|
+
)
|