chtypes 2.0.0.dev8__py3-none-any.whl → 2.0.0.dev10__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.
- chtypes/__init__.py +8 -0
- chtypes/__main__.py +31 -2
- chtypes/_advisory.py +38 -0
- chtypes/_codes.py +2 -1
- chtypes/_decode.py +3 -2
- chtypes/_input.py +37 -2
- chtypes/_ocifetch/__init__.py +5 -1
- chtypes/_ocifetch/_channel.py +6 -0
- chtypes/_ocifetch/_constants.py +6 -1
- chtypes/_ocifetch/_ensure.py +289 -89
- chtypes/_ocifetch/_errors.py +23 -1
- chtypes/_ocifetch/_http.py +96 -15
- chtypes/_ocifetch/_referrers.py +123 -2
- chtypes/_ocifetch/_retired.py +107 -2
- chtypes/_ocifetch/_revocation.py +642 -0
- chtypes/errors.py +26 -0
- chtypes/library.py +1 -1
- chtypes/registry.py +45 -3
- {chtypes-2.0.0.dev8.dist-info → chtypes-2.0.0.dev10.dist-info}/METADATA +1 -1
- chtypes-2.0.0.dev10.dist-info/RECORD +44 -0
- chtypes-2.0.0.dev8.dist-info/RECORD +0 -42
- {chtypes-2.0.0.dev8.dist-info → chtypes-2.0.0.dev10.dist-info}/WHEEL +0 -0
- {chtypes-2.0.0.dev8.dist-info → chtypes-2.0.0.dev10.dist-info}/entry_points.txt +0 -0
- {chtypes-2.0.0.dev8.dist-info → chtypes-2.0.0.dev10.dist-info}/licenses/LICENSE +0 -0
chtypes/__init__.py
CHANGED
|
@@ -69,6 +69,7 @@ from .errors import (
|
|
|
69
69
|
CODE_ARTIFACT_INCOMPATIBLE,
|
|
70
70
|
CODE_ARTIFACT_MISSING,
|
|
71
71
|
CODE_ARTIFACT_PINNED,
|
|
72
|
+
CODE_ARTIFACT_REVOKED,
|
|
72
73
|
CODE_ARTIFACT_UNPUBLISHED,
|
|
73
74
|
CODE_ARTIFACT_UNTRUSTED,
|
|
74
75
|
CODE_CACHE_UNUSABLE,
|
|
@@ -77,17 +78,20 @@ from .errors import (
|
|
|
77
78
|
CODE_SOURCE_RETIRED,
|
|
78
79
|
CODE_SOURCE_UNAUTHORIZED,
|
|
79
80
|
CODE_SOURCE_UNREACHABLE,
|
|
81
|
+
Advisory,
|
|
80
82
|
ArtifactCorruptError,
|
|
81
83
|
ArtifactError,
|
|
82
84
|
ArtifactIncompatibleError,
|
|
83
85
|
ArtifactMissingError,
|
|
84
86
|
ArtifactPinnedError,
|
|
87
|
+
ArtifactRevokedError,
|
|
85
88
|
ArtifactUnpublishedError,
|
|
86
89
|
ArtifactUntrustedError,
|
|
87
90
|
CacheUnusableError,
|
|
88
91
|
CallError,
|
|
89
92
|
ChtypesError,
|
|
90
93
|
InternalError,
|
|
94
|
+
Revocation,
|
|
91
95
|
SchemaError,
|
|
92
96
|
SourceForbiddenError,
|
|
93
97
|
SourceIncompatibleError,
|
|
@@ -130,6 +134,7 @@ __all__ = [
|
|
|
130
134
|
"CODE_ARTIFACT_INCOMPATIBLE",
|
|
131
135
|
"CODE_ARTIFACT_MISSING",
|
|
132
136
|
"CODE_ARTIFACT_PINNED",
|
|
137
|
+
"CODE_ARTIFACT_REVOKED",
|
|
133
138
|
"CODE_ARTIFACT_UNPUBLISHED",
|
|
134
139
|
"CODE_ARTIFACT_UNTRUSTED",
|
|
135
140
|
"CODE_CACHE_UNUSABLE",
|
|
@@ -139,11 +144,13 @@ __all__ = [
|
|
|
139
144
|
"CODE_SOURCE_UNAUTHORIZED",
|
|
140
145
|
"CODE_SOURCE_UNREACHABLE",
|
|
141
146
|
"EXPORT_NONE",
|
|
147
|
+
"Advisory",
|
|
142
148
|
"ArtifactCorruptError",
|
|
143
149
|
"ArtifactError",
|
|
144
150
|
"ArtifactIncompatibleError",
|
|
145
151
|
"ArtifactMissingError",
|
|
146
152
|
"ArtifactPinnedError",
|
|
153
|
+
"ArtifactRevokedError",
|
|
147
154
|
"ArtifactUnpublishedError",
|
|
148
155
|
"ArtifactUntrustedError",
|
|
149
156
|
"AtMergeEntry",
|
|
@@ -183,6 +190,7 @@ __all__ = [
|
|
|
183
190
|
"Reason",
|
|
184
191
|
"Registry",
|
|
185
192
|
"Resolved",
|
|
193
|
+
"Revocation",
|
|
186
194
|
"RowResult",
|
|
187
195
|
"Schema",
|
|
188
196
|
"SchemaDescription",
|
chtypes/__main__.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
chtypes fetch <spelling>... | --all [--platform <os-arch>] [--cache <dir>] [--lock <file>]
|
|
5
5
|
[--frozen] [--offline] [--update] [--strict]
|
|
6
|
+
[--allow-revoked] [--require-revocation-check]
|
|
6
7
|
chtypes verify [--cache <dir>] [--strict] re-verify every installed build
|
|
7
8
|
chtypes list [--cache <dir>] [--offline] [--strict]
|
|
8
9
|
chtypes where [--cache <dir>] [--strict] [--all] the cache root; --all: every search dir
|
|
@@ -37,7 +38,7 @@ from collections.abc import Sequence
|
|
|
37
38
|
|
|
38
39
|
from . import __version__ as chtypes_version
|
|
39
40
|
from . import _ocifetch as fetch_layer
|
|
40
|
-
from ._codes import CODE_ARTIFACT_CORRUPT
|
|
41
|
+
from ._codes import CODE_ARTIFACT_CORRUPT, CODE_ARTIFACT_REVOKED
|
|
41
42
|
from ._ocifetch import _channel
|
|
42
43
|
from ._ocifetch import _constants as C
|
|
43
44
|
from ._ocifetch._ensure import _translate_transport_error, detect_host_platform, missing_notes
|
|
@@ -148,6 +149,19 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
148
149
|
"(or set CHTYPES_OFFLINE=1)"
|
|
149
150
|
),
|
|
150
151
|
)
|
|
152
|
+
fetch.add_argument(
|
|
153
|
+
"--allow-revoked",
|
|
154
|
+
action="store_true",
|
|
155
|
+
help=(
|
|
156
|
+
"use a build a verified revocation refuses, with one warning "
|
|
157
|
+
"(this call only; never --lock or --update)"
|
|
158
|
+
),
|
|
159
|
+
)
|
|
160
|
+
fetch.add_argument(
|
|
161
|
+
"--require-revocation-check",
|
|
162
|
+
action="store_true",
|
|
163
|
+
help="a revocation status that cannot be read is the transport's error, not a warning",
|
|
164
|
+
)
|
|
151
165
|
|
|
152
166
|
verify = sub.add_parser(
|
|
153
167
|
"verify",
|
|
@@ -249,6 +263,8 @@ def _options(args: argparse.Namespace, *, platform: str | None = None):
|
|
|
249
263
|
lock_path=lock_path,
|
|
250
264
|
lock_write=lock_write,
|
|
251
265
|
update=getattr(args, "update", False),
|
|
266
|
+
allow_revoked=getattr(args, "allow_revoked", False),
|
|
267
|
+
require_revocation_check=getattr(args, "require_revocation_check", False),
|
|
252
268
|
)
|
|
253
269
|
return fetch._to_options(platform)
|
|
254
270
|
|
|
@@ -348,10 +364,23 @@ def _cmd_verify(args: argparse.Namespace) -> int:
|
|
|
348
364
|
return EXIT_OK
|
|
349
365
|
_say_notes(options)
|
|
350
366
|
failed = 0
|
|
367
|
+
revoked = 0
|
|
351
368
|
for r in results:
|
|
352
|
-
if not
|
|
369
|
+
if r.revocation is not None:
|
|
370
|
+
# A build a stored revocation refuses (docs/guides/fetch-v1.md,
|
|
371
|
+
# "Build-level revocation"); it is reported whatever its bytes say.
|
|
372
|
+
revoked += 1
|
|
373
|
+
_say(f"REVOKED {r.version} {r.platform} {r.dir}: {r.detail}")
|
|
374
|
+
elif not r.ok:
|
|
353
375
|
failed += 1
|
|
354
376
|
_say(f"chtypes: FAILED {r.version} {r.platform} {r.dir}: {r.detail}")
|
|
377
|
+
if revoked:
|
|
378
|
+
# Exit 11 wins over a mismatch's 1: a revocation is the more specific finding.
|
|
379
|
+
_say(
|
|
380
|
+
f"chtypes: {revoked} of {len(results)} installed build(s) are revoked "
|
|
381
|
+
f"[{CODE_ARTIFACT_REVOKED}]"
|
|
382
|
+
)
|
|
383
|
+
return C.ERROR_EXIT_CODES[CODE_ARTIFACT_REVOKED]
|
|
355
384
|
if failed:
|
|
356
385
|
_say(f"chtypes: {failed} of {len(results)} installed build(s) FAILED verification")
|
|
357
386
|
return C.ERROR_EXIT_CODES[CODE_ARTIFACT_CORRUPT]
|
chtypes/_advisory.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""What a `CHTYPES_ARTIFACT_REVOKED` error carries for programs
|
|
2
|
+
(docs/guides/fetch-v1.md, "Build-level revocation"; public issue #625).
|
|
3
|
+
|
|
4
|
+
A leaf module, so the fetch layer's errors and the public ones share the two
|
|
5
|
+
types without importing each other."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
|
|
11
|
+
__all__ = ["Advisory", "Revocation"]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class Advisory:
|
|
16
|
+
"""One advisory a revocation names, every string made safe to print. A
|
|
17
|
+
signed revocation's carries its `revoked_at`; a 410's carries the `build` it
|
|
18
|
+
names, newest first. A field the source did not send is "". `class_` is the
|
|
19
|
+
advisory's `class` (a keyword in Python)."""
|
|
20
|
+
|
|
21
|
+
id: str = ""
|
|
22
|
+
url: str = ""
|
|
23
|
+
class_: str = ""
|
|
24
|
+
summary: str = ""
|
|
25
|
+
build: str = ""
|
|
26
|
+
superseded_by: str = ""
|
|
27
|
+
revoked_at: str = ""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class Revocation:
|
|
32
|
+
"""`signed` is true for a verified, signed revocation of `manifest`, whose
|
|
33
|
+
one advisory it is, and false for a 410's body, which is unsigned, names no
|
|
34
|
+
manifest (`manifest` is "") and lists one advisory per revoked build."""
|
|
35
|
+
|
|
36
|
+
signed: bool = False
|
|
37
|
+
manifest: str = ""
|
|
38
|
+
advisories: tuple[Advisory, ...] = ()
|
chtypes/_codes.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Code generated by scripts/fetch-v1/gen-constants.py from
|
|
2
2
|
# spec/fetch-v1/constants.json. DO NOT EDIT.
|
|
3
|
-
# Source sha256:
|
|
3
|
+
# Source sha256: 5aa3d14a5d8b60399040a8242f003135fa5f6c2c402de7d4e2db4c768a3366ae
|
|
4
4
|
"""The shared error codes (docs/guides/fetch-v1.md §8), one per entry of the
|
|
5
5
|
fetch constants' `errors`, in their order. `chtypes.errors` re-exports them
|
|
6
6
|
(docs/reference/bindings-v1.md §4, the codes)."""
|
|
@@ -21,3 +21,4 @@ CODE_SOURCE_INCOMPATIBLE: Final[str] = "CHTYPES_SOURCE_INCOMPATIBLE"
|
|
|
21
21
|
CODE_ARTIFACT_INCOMPATIBLE: Final[str] = "CHTYPES_ARTIFACT_INCOMPATIBLE"
|
|
22
22
|
CODE_CACHE_UNUSABLE: Final[str] = "CHTYPES_CACHE_UNUSABLE"
|
|
23
23
|
CODE_SOURCE_RETIRED: Final[str] = "CHTYPES_SOURCE_RETIRED"
|
|
24
|
+
CODE_ARTIFACT_REVOKED: Final[str] = "CHTYPES_ARTIFACT_REVOKED"
|
chtypes/_decode.py
CHANGED
|
@@ -431,8 +431,9 @@ def decode_batch(raw: bytes, export: bytes | None) -> BatchResult:
|
|
|
431
431
|
export_declined = _bytes(obj, "export_declined", where)
|
|
432
432
|
if export_declined:
|
|
433
433
|
# A declined export has no payload and no spans, whatever buffer the call
|
|
434
|
-
# returned: the library
|
|
435
|
-
#
|
|
434
|
+
# returned: the library returns a NULL out_export for it (builds up to
|
|
435
|
+
# 20261011.105019 returned a zero-length buffer), and the document's
|
|
436
|
+
# export_declined decides, never the buffer.
|
|
436
437
|
export, spans = None, None
|
|
437
438
|
return BatchResult(
|
|
438
439
|
outcome=Outcome.of(_text(obj, "outcome", where)),
|
chtypes/_input.py
CHANGED
|
@@ -45,6 +45,31 @@ def body_bytes(value: bytes, what: str = "body") -> bytes:
|
|
|
45
45
|
raise TypeError(f"{what} must be bytes, not {type(value).__name__}")
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
_NON_UTF8_HINT = (
|
|
49
|
+
"this ABI generation cannot carry a non-UTF-8 value in a JSON input; "
|
|
50
|
+
"write the bytes as unhex('<hex>') in the expression instead"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _is_utf8(text: str) -> bool:
|
|
55
|
+
"""False for a `str` that cannot be UTF-8 encoded (a lone surrogate, as
|
|
56
|
+
`surrogateescape` makes of undecodable bytes)."""
|
|
57
|
+
try:
|
|
58
|
+
text.encode("utf-8")
|
|
59
|
+
except UnicodeEncodeError:
|
|
60
|
+
return False
|
|
61
|
+
return True
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _refuse_non_utf8(what: str, part: str, key: str) -> Exception:
|
|
65
|
+
# The key is shown escaped (ASCII only), never as the raw text.
|
|
66
|
+
return misuse(
|
|
67
|
+
f"{what}: the key {json.dumps(key)} is not valid UTF-8, and {_NON_UTF8_HINT}"
|
|
68
|
+
if part == "key"
|
|
69
|
+
else f"{what}: the value for key {json.dumps(key)} is not valid UTF-8, and {_NON_UTF8_HINT}"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
48
73
|
def _string_map(mapping: Mapping[str, str], what: str) -> dict[str, str]:
|
|
49
74
|
out: dict[str, str] = {}
|
|
50
75
|
for key, value in mapping.items():
|
|
@@ -53,6 +78,12 @@ def _string_map(mapping: Mapping[str, str], what: str) -> dict[str, str]:
|
|
|
53
78
|
f"{what} maps strings to strings only; got {type(key).__name__} -> "
|
|
54
79
|
f"{type(value).__name__} for {key!r} (a value is never rewritten)"
|
|
55
80
|
)
|
|
81
|
+
# json.dumps would write a lone surrogate as a \udXXX escape, a value
|
|
82
|
+
# the library does not read as the bytes the caller meant: refuse.
|
|
83
|
+
if not _is_utf8(key):
|
|
84
|
+
raise _refuse_non_utf8(what, "key", key)
|
|
85
|
+
if not _is_utf8(value):
|
|
86
|
+
raise _refuse_non_utf8(what, "value", key)
|
|
56
87
|
out[key] = value
|
|
57
88
|
return out
|
|
58
89
|
|
|
@@ -75,6 +106,8 @@ def settings_json(
|
|
|
75
106
|
if session_timezone is not None:
|
|
76
107
|
if not isinstance(session_timezone, str):
|
|
77
108
|
raise TypeError(f"session_timezone must be str, not {type(session_timezone).__name__}")
|
|
109
|
+
if not _is_utf8(session_timezone):
|
|
110
|
+
raise misuse(f"the zone: the value is not valid UTF-8, and {_NON_UTF8_HINT}")
|
|
78
111
|
if _ZONE_KEY in merged:
|
|
79
112
|
raise misuse(
|
|
80
113
|
f"the {_ZONE_KEY} option and a {_ZONE_KEY} settings key were both given; "
|
|
@@ -141,10 +174,12 @@ def server_profile_json(profile: ServerProfile) -> bytes:
|
|
|
141
174
|
if profile.timezone is not None:
|
|
142
175
|
if not isinstance(profile.timezone, str):
|
|
143
176
|
raise TypeError(f"timezone must be str, not {type(profile.timezone).__name__}")
|
|
177
|
+
if not _is_utf8(profile.timezone):
|
|
178
|
+
raise misuse(f"server profile: the timezone is not valid UTF-8, and {_NON_UTF8_HINT}")
|
|
144
179
|
if profile.timezone != "":
|
|
145
180
|
doc["timezone"] = profile.timezone
|
|
146
181
|
if profile.settings is not None:
|
|
147
|
-
doc["settings"] = _string_map(profile.settings, "settings")
|
|
182
|
+
doc["settings"] = _string_map(profile.settings, "server profile settings")
|
|
148
183
|
if profile.macros is not None:
|
|
149
|
-
doc["macros"] = _string_map(profile.macros, "macros")
|
|
184
|
+
doc["macros"] = _string_map(profile.macros, "server profile macros")
|
|
150
185
|
return _dump(doc)
|
chtypes/_ocifetch/__init__.py
CHANGED
|
@@ -4,7 +4,7 @@ Not part of this package's public API: every name here is subject to change
|
|
|
4
4
|
without notice until the v1 switch. `_constants` is generated; the fetch
|
|
5
5
|
logic lives in sibling modules. The seam (docs/guides/fetch-v1.md "The seam")
|
|
6
6
|
is the only surface anything outside this package may use: `ensure`,
|
|
7
|
-
`resolve_installed`, `list_installed`, `verify_installed` and
|
|
7
|
+
`resolve_installed`, `check_installed`, `list_installed`, `verify_installed` and
|
|
8
8
|
`fetch_signed`, plus the `Request`/`Options`/`Resolved`/
|
|
9
9
|
`VerifyResult` shapes they take and return; and beside it the CLI's `resolve`
|
|
10
10
|
and `prune` and the registry's in-use `hold` (§1, "In use"). Every other module here
|
|
@@ -25,9 +25,11 @@ from chtypes._ocifetch._ensure import (
|
|
|
25
25
|
Request,
|
|
26
26
|
Resolved,
|
|
27
27
|
VerifyResult,
|
|
28
|
+
check_installed,
|
|
28
29
|
ensure,
|
|
29
30
|
fetch_signed,
|
|
30
31
|
list_installed,
|
|
32
|
+
reset_revocation_memo_for_tests,
|
|
31
33
|
resolve_installed,
|
|
32
34
|
verify_installed,
|
|
33
35
|
)
|
|
@@ -43,11 +45,13 @@ __all__ = [
|
|
|
43
45
|
"Resolved",
|
|
44
46
|
"Superseded",
|
|
45
47
|
"VerifyResult",
|
|
48
|
+
"check_installed",
|
|
46
49
|
"ensure",
|
|
47
50
|
"fetch_signed",
|
|
48
51
|
"hold",
|
|
49
52
|
"list_installed",
|
|
50
53
|
"prune",
|
|
54
|
+
"reset_revocation_memo_for_tests",
|
|
51
55
|
"resolve",
|
|
52
56
|
"resolve_each",
|
|
53
57
|
"resolve_installed",
|
chtypes/_ocifetch/_channel.py
CHANGED
|
@@ -133,6 +133,10 @@ class Channel:
|
|
|
133
133
|
# lookups see only records signed with it. "" does neither (the v1 contract
|
|
134
134
|
# and every production channel).
|
|
135
135
|
own_fingerprint: str = ""
|
|
136
|
+
# Build-level revocation (_revocation.py; docs/guides/fetch-v1.md, "Build-level
|
|
137
|
+
# revocation"): generation 2, the dev channel and the production one. The v1
|
|
138
|
+
# contract has none of it.
|
|
139
|
+
revocation: bool = False
|
|
136
140
|
|
|
137
141
|
|
|
138
142
|
# What a 2.0.0-dev SDK speaks, and what every process speaks unless a test
|
|
@@ -149,6 +153,7 @@ DEV_CHANNEL: Final = Channel(
|
|
|
149
153
|
overridable=False,
|
|
150
154
|
pinnable=False,
|
|
151
155
|
own_fingerprint=CHS_ABI_FINGERPRINT.removeprefix("sha256:"),
|
|
156
|
+
revocation=True,
|
|
152
157
|
)
|
|
153
158
|
|
|
154
159
|
# The v1 contract the fetch-v1 conformance cases specify, from the generated
|
|
@@ -183,6 +188,7 @@ PROD_V2_CHANNEL: Final = Channel(
|
|
|
183
188
|
keys=C.RELEASE_KEYS,
|
|
184
189
|
overridable=True,
|
|
185
190
|
pinnable=True,
|
|
191
|
+
revocation=True,
|
|
186
192
|
)
|
|
187
193
|
|
|
188
194
|
_lock = threading.Lock()
|
chtypes/_ocifetch/_constants.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Code generated by scripts/fetch-v1/gen-constants.py from
|
|
2
2
|
# spec/fetch-v1/constants.json. DO NOT EDIT.
|
|
3
|
-
# Source sha256:
|
|
3
|
+
# Source sha256: 5aa3d14a5d8b60399040a8242f003135fa5f6c2c402de7d4e2db4c768a3366ae
|
|
4
4
|
"""The v1 fetch constants, generated: see docs/guides/fetch-v1.md."""
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
@@ -46,6 +46,7 @@ ARTIFACT_TYPE: Final[str] = "application/vnd.wavehouse.chtypes.artifact.v1"
|
|
|
46
46
|
GOLDENS_ARTIFACT_TYPE: Final[str] = "application/vnd.wavehouse.chtypes.goldens.v1"
|
|
47
47
|
FIXTURES_ARTIFACT_TYPE: Final[str] = "application/vnd.wavehouse.chtypes.sdk-fetch-fixtures.v1"
|
|
48
48
|
CHANNEL_ARTIFACT_TYPE: Final[str] = "application/vnd.wavehouse.chtypes.channel.v1"
|
|
49
|
+
REVOCATION_ARTIFACT_TYPE: Final[str] = "application/vnd.wavehouse.chtypes.revocation.v1"
|
|
49
50
|
|
|
50
51
|
DSSE_PAYLOAD_TYPE: Final[str] = "application/vnd.in-toto+json"
|
|
51
52
|
DSSE_MAX_SIGNATURES: Final[int] = 4
|
|
@@ -56,6 +57,7 @@ PREDICATE_TYPE_ARTIFACT: Final[str] = "https://artifacts.wavehouse.dev/spec/arti
|
|
|
56
57
|
PREDICATE_TYPE_GOLDENS: Final[str] = "https://artifacts.wavehouse.dev/spec/goldens/v1"
|
|
57
58
|
PREDICATE_TYPE_FIXTURES: Final[str] = "https://artifacts.wavehouse.dev/spec/sdk-fetch-fixtures/v1"
|
|
58
59
|
PREDICATE_TYPE_CHANNEL: Final[str] = "https://artifacts.wavehouse.dev/spec/channel/v1"
|
|
60
|
+
PREDICATE_TYPE_REVOCATION: Final[str] = "https://artifacts.wavehouse.dev/spec/revocation/v1"
|
|
59
61
|
|
|
60
62
|
KEYID_ALGORITHM: Final[str] = "sha256-first16hex"
|
|
61
63
|
RELEASE_KEYS: Final[tuple[dict[str, str], ...]] = (
|
|
@@ -87,6 +89,7 @@ RETIRED_STATUSES: Final[tuple[int, ...]] = (410,)
|
|
|
87
89
|
|
|
88
90
|
CONNECT_TIMEOUT_S: Final[float] = 30.0
|
|
89
91
|
IDLE_READ_TIMEOUT_S: Final[float] = 60.0
|
|
92
|
+
REVOCATION_CHECK_S: Final[float] = 5.0
|
|
90
93
|
|
|
91
94
|
MANIFEST_MAX_BYTES: Final[int] = 4194304
|
|
92
95
|
BUNDLE_MAX_BYTES: Final[int] = 1048576
|
|
@@ -109,6 +112,7 @@ PROD_V2_BASES: Final[tuple[str, ...]] = ("https://registry.wavehouse.dev/chtypes
|
|
|
109
112
|
PROD_V2_SYSTEM_DIRS: Final[tuple[str, ...]] = ("/usr/local/share/chtypes/v2", "/opt/chtypes/v2")
|
|
110
113
|
CACHE_UNPACKED_DIR: Final[str] = "unpacked/sha256"
|
|
111
114
|
CACHE_VERIFIED_RECORD: Final[str] = "verified.json"
|
|
115
|
+
CACHE_REVOCATION_RECORD: Final[str] = "revocation.json"
|
|
112
116
|
CACHE_ANNOTATION_PREFIX: Final[str] = "dev.wavehouse.chtypes."
|
|
113
117
|
|
|
114
118
|
LOCK_SCHEMA: Final[int] = 3
|
|
@@ -132,4 +136,5 @@ ERROR_EXIT_CODES: Final[dict[str, int]] = {
|
|
|
132
136
|
"CHTYPES_ARTIFACT_INCOMPATIBLE": 8,
|
|
133
137
|
"CHTYPES_CACHE_UNUSABLE": 9,
|
|
134
138
|
"CHTYPES_SOURCE_RETIRED": 10,
|
|
139
|
+
"CHTYPES_ARTIFACT_REVOKED": 11,
|
|
135
140
|
}
|