primarysource 0.1.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.
- primarysource/__init__.py +5 -0
- primarysource/bbf/__init__.py +56 -0
- primarysource/bbf/_xml.py +63 -0
- primarysource/bbf/catalogue.json +777 -0
- primarysource/bbf/catalogue.py +137 -0
- primarysource/bbf/cli.py +79 -0
- primarysource/bbf/datamodel.py +281 -0
- primarysource/bbf/importer.py +151 -0
- primarysource/bbf/library.py +380 -0
- primarysource/bbf/server.py +72 -0
- primarysource/budget.py +63 -0
- primarysource/c2sp/__init__.py +53 -0
- primarysource/c2sp/_fetch.py +5 -0
- primarysource/c2sp/catalogue.json +231 -0
- primarysource/c2sp/catalogue.py +95 -0
- primarysource/c2sp/cli.py +62 -0
- primarysource/c2sp/importer.py +90 -0
- primarysource/c2sp/library.py +309 -0
- primarysource/c2sp/markdown.py +31 -0
- primarysource/c2sp/server.py +68 -0
- primarysource/cavp/__init__.py +42 -0
- primarysource/cavp/cli.py +84 -0
- primarysource/cavp/data/vectors.toml +47 -0
- primarysource/cavp/importer.py +156 -0
- primarysource/cavp/model.py +279 -0
- primarysource/cavp/parse.py +269 -0
- primarysource/cavp/paths.py +17 -0
- primarysource/cavp/server.py +67 -0
- primarysource/cavp/tools.py +169 -0
- primarysource/cli.py +107 -0
- primarysource/cursor.py +82 -0
- primarysource/cve/__init__.py +5 -0
- primarysource/cve/__main__.py +6 -0
- primarysource/cve/cli.py +144 -0
- primarysource/cve/collection.py +20 -0
- primarysource/cve/config.py +58 -0
- primarysource/cve/errors.py +16 -0
- primarysource/cve/kernel.py +232 -0
- primarysource/cve/mcp.py +139 -0
- primarysource/cve/parse.py +318 -0
- primarysource/cve/py.typed +0 -0
- primarysource/cve/service.py +760 -0
- primarysource/cve/store.py +214 -0
- primarysource/cve/sync.py +438 -0
- primarysource/cve/views.py +304 -0
- primarysource/debpolicy/__init__.py +16 -0
- primarysource/debpolicy/catalogue.py +263 -0
- primarysource/debpolicy/cli.py +12 -0
- primarysource/debpolicy/server.py +11 -0
- primarysource/draft/__init__.py +11 -0
- primarysource/draft/catalogue.py +117 -0
- primarysource/draft/cli.py +149 -0
- primarysource/draft/collection.py +21 -0
- primarysource/draft/datatracker.py +158 -0
- primarysource/draft/importer.py +163 -0
- primarysource/draft/licence.py +89 -0
- primarysource/draft/names.py +64 -0
- primarysource/draft/net.py +133 -0
- primarysource/draft/segment.py +192 -0
- primarysource/draft/server.py +91 -0
- primarysource/draft/status.py +146 -0
- primarysource/draft/store.py +199 -0
- primarysource/draft/tracked.json +104 -0
- primarysource/draft/views.py +430 -0
- primarysource/draft/xmlmeta.py +74 -0
- primarysource/ecma/__init__.py +52 -0
- primarysource/ecma/_pdf.py +143 -0
- primarysource/ecma/catalogue.json +22 -0
- primarysource/ecma/catalogue.py +100 -0
- primarysource/ecma/cli.py +67 -0
- primarysource/ecma/identify.py +25 -0
- primarysource/ecma/importer.py +108 -0
- primarysource/ecma/library.py +240 -0
- primarysource/ecma/server.py +67 -0
- primarysource/errors.py +29 -0
- primarysource/fetch.py +360 -0
- primarysource/fsutil.py +150 -0
- primarysource/gitdocs/__init__.py +15 -0
- primarysource/gitdocs/catalogue.py +116 -0
- primarysource/gitdocs/cli.py +12 -0
- primarysource/gitdocs/server.py +8 -0
- primarysource/home.py +65 -0
- primarysource/iana/__init__.py +32 -0
- primarysource/iana/catalogue.json +244 -0
- primarysource/iana/catalogue.py +69 -0
- primarysource/iana/cli.py +69 -0
- primarysource/iana/importer.py +157 -0
- primarysource/iana/model.py +325 -0
- primarysource/iana/reader.py +315 -0
- primarysource/iana/server.py +67 -0
- primarysource/iana/store.py +83 -0
- primarysource/iana/xmlsafe.py +47 -0
- primarysource/identify.py +186 -0
- primarysource/ieee/__init__.py +58 -0
- primarysource/ieee/_html.py +49 -0
- primarysource/ieee/catalogue.json +74 -0
- primarysource/ieee/catalogue.py +123 -0
- primarysource/ieee/cli.py +79 -0
- primarysource/ieee/identify.py +24 -0
- primarysource/ieee/ieee802.py +47 -0
- primarysource/ieee/importer.py +305 -0
- primarysource/ieee/posix.py +233 -0
- primarysource/ieee/server.py +79 -0
- primarysource/index.py +446 -0
- primarysource/iso/__init__.py +51 -0
- primarysource/iso/cli.py +98 -0
- primarysource/iso/designation.py +95 -0
- primarysource/iso/identify.py +100 -0
- primarysource/iso/importer.py +224 -0
- primarysource/iso/library.py +249 -0
- primarysource/iso/server.py +69 -0
- primarysource/itu/__init__.py +41 -0
- primarysource/itu/__main__.py +8 -0
- primarysource/itu/asn1mod.py +478 -0
- primarysource/itu/catalog.py +472 -0
- primarysource/itu/cli.py +445 -0
- primarysource/itu/collection.py +20 -0
- primarysource/itu/corpus.py +118 -0
- primarysource/itu/errata.py +16 -0
- primarysource/itu/formal.py +565 -0
- primarysource/itu/identify.py +26 -0
- primarysource/itu/index.py +490 -0
- primarysource/itu/mcp.py +474 -0
- primarysource/itu/py.typed +0 -0
- primarysource/itu/sources.py +600 -0
- primarysource/itu/text.py +609 -0
- primarysource/kconfig/__init__.py +16 -0
- primarysource/kconfig/catalogue.py +98 -0
- primarysource/kconfig/cli.py +12 -0
- primarysource/kconfig/server.py +8 -0
- primarysource/model.py +63 -0
- primarysource/nist/__init__.py +5 -0
- primarysource/nist/collection.py +46 -0
- primarysource/nist/data/acquired-nist.json +1657 -0
- primarysource/nist/data/nist-catalog.json +12090 -0
- primarysource/nist/data/sources.toml +1996 -0
- primarysource/nist/identify.py +41 -0
- primarysource/nist/ingest/__init__.py +1 -0
- primarysource/nist/ingest/acquire_nist.py +128 -0
- primarysource/nist/ingest/controls.py +82 -0
- primarysource/nist/ingest/discover_nist.py +218 -0
- primarysource/nist/ingest/extract.py +48 -0
- primarysource/nist/ingest/fetch.py +211 -0
- primarysource/nist/ingest/index.py +14 -0
- primarysource/nist/ingest/oscal.py +480 -0
- primarysource/nist/ingest/prose_layouts.py +236 -0
- primarysource/nist/ingest/sources.py +94 -0
- primarysource/nist/ingest/status.py +255 -0
- primarysource/nist/models.py +7 -0
- primarysource/nist/paths.py +33 -0
- primarysource/nist/server/__init__.py +3 -0
- primarysource/nist/server/corpus.py +38 -0
- primarysource/nist/server/mcp.py +19 -0
- primarysource/nist/server/tools.py +35 -0
- primarysource/nist/spec.py +38 -0
- primarysource/pdfcorpus/__init__.py +16 -0
- primarysource/pdfcorpus/corpus.py +159 -0
- primarysource/pdfcorpus/fts.py +175 -0
- primarysource/pdfcorpus/index.py +70 -0
- primarysource/pdfcorpus/models.py +30 -0
- primarysource/pdfcorpus/server.py +96 -0
- primarysource/pdfcorpus/spec.py +37 -0
- primarysource/pdfcorpus/split.py +391 -0
- primarysource/pdfcorpus/tools.py +244 -0
- primarysource/pep/__init__.py +15 -0
- primarysource/pep/catalogue.py +191 -0
- primarysource/pep/cli.py +12 -0
- primarysource/pep/server.py +8 -0
- primarysource/pkgdocs/__init__.py +19 -0
- primarysource/pkgdocs/fetch.py +356 -0
- primarysource/pkgdocs/index.py +89 -0
- primarysource/pkgdocs/model.py +76 -0
- primarysource/pkgdocs/readers/__init__.py +1 -0
- primarysource/pkgdocs/readers/_common.py +134 -0
- primarysource/pkgdocs/readers/asciidoc.py +160 -0
- primarysource/pkgdocs/readers/latex.py +315 -0
- primarysource/pkgdocs/readers/markdown.py +173 -0
- primarysource/pkgdocs/readers/pod.py +132 -0
- primarysource/pkgdocs/readers/rst.py +239 -0
- primarysource/pkgdocs/readers/scdoc.py +108 -0
- primarysource/pkgdocs/runtime.py +552 -0
- primarysource/pkgdocs/serving.py +158 -0
- primarysource/pkgdocs/store.py +167 -0
- primarysource/pms/__init__.py +16 -0
- primarysource/pms/catalogue.py +88 -0
- primarysource/pms/cli.py +12 -0
- primarysource/pms/server.py +8 -0
- primarysource/py.typed +0 -0
- primarysource/readers/__init__.py +10 -0
- primarysource/readers/bookmarks.py +192 -0
- primarysource/readers/html.py +1312 -0
- primarysource/readers/markdown.py +189 -0
- primarysource/readers/pdf.py +187 -0
- primarysource/readers/xml.py +230 -0
- primarysource/refs.py +87 -0
- primarysource/registry.py +161 -0
- primarysource/render.py +49 -0
- primarysource/rfc/__init__.py +3 -0
- primarysource/rfc/__main__.py +6 -0
- primarysource/rfc/cite.py +204 -0
- primarysource/rfc/cli.py +903 -0
- primarysource/rfc/collection.py +19 -0
- primarysource/rfc/config.py +138 -0
- primarysource/rfc/errata.py +203 -0
- primarysource/rfc/fsutil.py +82 -0
- primarysource/rfc/graph.py +213 -0
- primarysource/rfc/index/__init__.py +1 -0
- primarysource/rfc/index/chunker.py +281 -0
- primarysource/rfc/index/embed.py +166 -0
- primarysource/rfc/index/hybrid.py +305 -0
- primarysource/rfc/index/lexical.py +223 -0
- primarysource/rfc/index/rfcxml.py +673 -0
- primarysource/rfc/index/search.py +508 -0
- primarysource/rfc/index/txtseg.py +585 -0
- primarysource/rfc/library.py +294 -0
- primarysource/rfc/mcp/__init__.py +1 -0
- primarysource/rfc/mcp/server.py +381 -0
- primarysource/rfc/packs/__init__.py +1 -0
- primarysource/rfc/packs/builder.py +228 -0
- primarysource/rfc/packs/collections/all-current-txt.json +10 -0
- primarysource/rfc/packs/collections/essentials.json +19 -0
- primarysource/rfc/packs/collections/fyi-current.json +10 -0
- primarysource/rfc/packs/collections/standards-current.json +10 -0
- primarysource/rfc/packs/collections/year-2025.json +10 -0
- primarysource/rfc/packs/collections/year-2026.json +10 -0
- primarysource/rfc/packs/collections.py +163 -0
- primarysource/rfc/packs/docsetexport.py +163 -0
- primarysource/rfc/packs/epub.py +159 -0
- primarysource/rfc/packs/export.py +306 -0
- primarysource/rfc/packs/htmlexport.py +82 -0
- primarysource/rfc/packs/serve.py +143 -0
- primarysource/rfc/packs/zimexport.py +171 -0
- primarysource/rfc/query.py +466 -0
- primarysource/rfc/subjects.py +457 -0
- primarysource/rfc/sync/__init__.py +1 -0
- primarysource/rfc/sync/downloader.py +132 -0
- primarysource/rfc/sync/manifest.py +162 -0
- primarysource/rfc/sync/scanner.py +49 -0
- primarysource/rfc/sync/state.py +464 -0
- primarysource/rfc/views.py +1067 -0
- primarysource/rpmdocs/__init__.py +15 -0
- primarysource/rpmdocs/catalogue.py +117 -0
- primarysource/rpmdocs/cli.py +12 -0
- primarysource/rpmdocs/server.py +8 -0
- primarysource/scaffold.py +223 -0
- primarysource/serve.py +75 -0
- primarysource/settings.py +152 -0
- primarysource/usb/__init__.py +5 -0
- primarysource/usb/collection.py +37 -0
- primarysource/usb/data/acquired.json +2025 -0
- primarysource/usb/data/sources-usb.toml +852 -0
- primarysource/usb/data/sources.toml +10 -0
- primarysource/usb/data/usb-catalog.json +1663 -0
- primarysource/usb/data/usb-classification.json +1986 -0
- primarysource/usb/data/usb-corpus-build.json +608 -0
- primarysource/usb/data/usb-pdf-choice.json +839 -0
- primarysource/usb/data/usb-split-measure.json +1803 -0
- primarysource/usb/ingest/__init__.py +1 -0
- primarysource/usb/ingest/acquire.py +130 -0
- primarysource/usb/ingest/discover_usb.py +88 -0
- primarysource/usb/ingest/extract.py +48 -0
- primarysource/usb/ingest/fetch.py +197 -0
- primarysource/usb/ingest/index.py +14 -0
- primarysource/usb/ingest/sources.py +85 -0
- primarysource/usb/models.py +7 -0
- primarysource/usb/paths.py +33 -0
- primarysource/usb/server/__init__.py +3 -0
- primarysource/usb/server/corpus.py +38 -0
- primarysource/usb/server/mcp.py +19 -0
- primarysource/usb/server/tools.py +35 -0
- primarysource/usb/spec.py +20 -0
- primarysource/usbids/__init__.py +56 -0
- primarysource/usbids/cli.py +65 -0
- primarysource/usbids/importer.py +272 -0
- primarysource/usbids/parse.py +283 -0
- primarysource/usbids/server.py +55 -0
- primarysource/usbids/store.py +80 -0
- primarysource/usbids/tools.py +174 -0
- primarysource/zim/__init__.py +5 -0
- primarysource/zim/__main__.py +8 -0
- primarysource/zim/_libzim.py +37 -0
- primarysource/zim/catalog.py +594 -0
- primarysource/zim/cli.py +555 -0
- primarysource/zim/collection.py +41 -0
- primarysource/zim/errors.py +100 -0
- primarysource/zim/groups.py +30 -0
- primarysource/zim/library.py +668 -0
- primarysource/zim/manage.py +636 -0
- primarysource/zim/markdown.py +1247 -0
- primarysource/zim/mcp.py +201 -0
- primarysource/zim/purl.py +130 -0
- primarysource/zim/py.typed +0 -0
- primarysource/zim/refs.py +90 -0
- primarysource/zim/service.py +1182 -0
- primarysource/zim/snapshot.py +197 -0
- primarysource/zim/sync.py +269 -0
- primarysource/zim/views.py +230 -0
- primarysource-0.1.0.dist-info/METADATA +118 -0
- primarysource-0.1.0.dist-info/RECORD +303 -0
- primarysource-0.1.0.dist-info/WHEEL +4 -0
- primarysource-0.1.0.dist-info/entry_points.txt +2 -0
- primarysource-0.1.0.dist-info/licenses/LICENSE +203 -0
- primarysource-0.1.0.dist-info/licenses/NOTICE +51 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
{
|
|
2
|
+
"repo": "C2SP/C2SP",
|
|
3
|
+
"commit": "09f3e519fe79bcbdccadcf821182e3b488a14d71",
|
|
4
|
+
"raw_base": "https://raw.githubusercontent.com/C2SP/C2SP/",
|
|
5
|
+
"terms": {
|
|
6
|
+
"licence": "CC-BY-4.0",
|
|
7
|
+
"placement": "layer",
|
|
8
|
+
"terms_url": "https://github.com/C2SP/C2SP/blob/09f3e519fe79bcbdccadcf821182e3b488a14d71/.github/README.md",
|
|
9
|
+
"terms_read": "2026-11-12",
|
|
10
|
+
"summary": "All C2SP specifications are licensed CC BY 4.0 (the repository's README); attribution required. Code and data in the repository are BSD-1-Clause."
|
|
11
|
+
},
|
|
12
|
+
"specs": [
|
|
13
|
+
{
|
|
14
|
+
"id": "BLAKE3",
|
|
15
|
+
"path": "BLAKE3.md",
|
|
16
|
+
"title": "The BLAKE3 Hashing Framework",
|
|
17
|
+
"size": 32004,
|
|
18
|
+
"sha256": "85c336e9900538e572333dd16ddde9f605c000ec8293001dd4c2642193b9c07e"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "FLOE",
|
|
22
|
+
"path": "FLOE.md",
|
|
23
|
+
"title": "Fast Lightweight Online Encryption (FLOE)",
|
|
24
|
+
"size": 19400,
|
|
25
|
+
"sha256": "7808f6995f098400009e363674013d71a4d980ad30fc3ca46cb34aae1fafc044"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "XAES-256-GCM",
|
|
29
|
+
"path": "XAES-256-GCM.md",
|
|
30
|
+
"title": "The XAES-256-GCM extended-nonce AEAD",
|
|
31
|
+
"size": 14100,
|
|
32
|
+
"sha256": "bcce71811d53d1d19f0298dc52746fd4f92f42bc4aec55abad5d2bd1ab9b2810"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "age",
|
|
36
|
+
"path": "age.md",
|
|
37
|
+
"title": "age",
|
|
38
|
+
"size": 27736,
|
|
39
|
+
"sha256": "b0a767b91a184c536e8a04002f7bba7521388fee3989f5891e23cc1aaa00232f"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"id": "age-plugin",
|
|
43
|
+
"path": "age-plugin.md",
|
|
44
|
+
"title": "Plugin system for age",
|
|
45
|
+
"size": 25210,
|
|
46
|
+
"sha256": "83623f2d3942fb99c0d59b45c4f9d8bad2b29bf5af178abade558b6fc8341c95"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "chacha20-poly1305-siv",
|
|
50
|
+
"path": "chacha20-poly1305-siv.md",
|
|
51
|
+
"title": "ChaCha20-Poly1305-SIV",
|
|
52
|
+
"size": 42410,
|
|
53
|
+
"sha256": "b93e3c9bf3e63669b059947f89bef7f3f55c502a59ef2798b8f084086fb2854b"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"id": "chacha8rand",
|
|
57
|
+
"path": "chacha8rand.md",
|
|
58
|
+
"title": "ChaCha8Rand",
|
|
59
|
+
"size": 18839,
|
|
60
|
+
"sha256": "09610ec86f76f1fb0160f354b68801be7fdf9948a04daa2dc88544286b33cd8d"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"id": "chunked-encryption",
|
|
64
|
+
"path": "chunked-encryption.md",
|
|
65
|
+
"title": "Chunked encryption",
|
|
66
|
+
"size": 18366,
|
|
67
|
+
"sha256": "4c2efb7e9930d4df06835f64c111e13c475afb6eed314b85fbbaac59a8227201"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"id": "cocktail-dkg",
|
|
71
|
+
"path": "cocktail-dkg.md",
|
|
72
|
+
"title": "COCKTAIL Distributed Key Generation",
|
|
73
|
+
"size": 148605,
|
|
74
|
+
"sha256": "e7ded83da0b54d9bf0052368ddcd2ca9ad6e257ed7fd17abf14f7e69f44e717d"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"id": "det-keygen",
|
|
78
|
+
"path": "det-keygen.md",
|
|
79
|
+
"title": "Deterministic Key Generation",
|
|
80
|
+
"size": 20994,
|
|
81
|
+
"sha256": "d78969dbe85eb5461c5cbf9136617c85887bed884884ff66a0da8398e411f11a"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"id": "https-bastion",
|
|
85
|
+
"path": "https-bastion.md",
|
|
86
|
+
"title": "HTTPS Bastion",
|
|
87
|
+
"size": 7053,
|
|
88
|
+
"sha256": "a3939dd7075f3578fb996f87feafc96e5d6edc71b895c6e965343c0b0565d07c"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"id": "httpsig-pq",
|
|
92
|
+
"path": "httpsig-pq.md",
|
|
93
|
+
"title": "Post-Quantum Algorithms for HTTP Message Signatures",
|
|
94
|
+
"size": 47902,
|
|
95
|
+
"sha256": "363ed0952d224a4f4ab1276eb1aa1fe88a046f35e046265eaa4c37dc362027e2"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"id": "jq255",
|
|
99
|
+
"path": "jq255.md",
|
|
100
|
+
"title": "The jq255e and jq255s Groups, Key Exchange and Signatures",
|
|
101
|
+
"size": 60656,
|
|
102
|
+
"sha256": "d59fff0aae35128adb3753f72e7f23873d08685951619c9bf0939c4820810021"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"id": "kopis",
|
|
106
|
+
"path": "kopis.md",
|
|
107
|
+
"title": "The Kopis Key Encapsulation Mechanism",
|
|
108
|
+
"size": 14607,
|
|
109
|
+
"sha256": "9707bb224408d1ea48f5748c8492472c8968ca2f5318a79ee2594bfcb768c6bd"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"id": "mtc-tlog",
|
|
113
|
+
"path": "mtc-tlog.md",
|
|
114
|
+
"title": "Merkle Tree Certificates With Tiled Transparency Logs",
|
|
115
|
+
"size": 8019,
|
|
116
|
+
"sha256": "d4cb827114feb7e5bdf5a5083eddc8805afcf135ee102543319711e607abf978"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"id": "passkey-record",
|
|
120
|
+
"path": "passkey-record.md",
|
|
121
|
+
"title": "Passkey records",
|
|
122
|
+
"size": 6770,
|
|
123
|
+
"sha256": "5e33609021db3023bec58c5b9512c7fb9a9f7ffc52853afb9a20356d8f1a7c31"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"id": "phc-strings",
|
|
127
|
+
"path": "phc-strings.md",
|
|
128
|
+
"title": "PHC Strings",
|
|
129
|
+
"size": 16430,
|
|
130
|
+
"sha256": "b53e4460a558486388233f8ca5cd30ef1f0543c870b7e0bfeecc282c2fbc2bd9"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"id": "sequencehash",
|
|
134
|
+
"path": "sequencehash.md",
|
|
135
|
+
"title": "SequenceHash and SequenceMAC",
|
|
136
|
+
"size": 32689,
|
|
137
|
+
"sha256": "63a3db48455cf3643203ac8665720042ae1f62498e374351b0d228ec4157f101"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"id": "signed-note",
|
|
141
|
+
"path": "signed-note.md",
|
|
142
|
+
"title": "Note",
|
|
143
|
+
"size": 7841,
|
|
144
|
+
"sha256": "bd9bf002294385e0cf3c79aac74b193a3831ccc7b32073fb00baa727653816ae"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"id": "static-ct-api",
|
|
148
|
+
"path": "static-ct-api.md",
|
|
149
|
+
"title": "The Static Certificate Transparency API",
|
|
150
|
+
"size": 14179,
|
|
151
|
+
"sha256": "e7173ca6fc226ada35d4bffb2cb8bf5a68f6ba7f12a3057fbb696fc6d32b6b5a"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"id": "tlog-checkpoint",
|
|
155
|
+
"path": "tlog-checkpoint.md",
|
|
156
|
+
"title": "Transparency Log Checkpoints",
|
|
157
|
+
"size": 5999,
|
|
158
|
+
"sha256": "528317ad4dabb0568a1c20728b74fdab6fbad99749cc2fb089e8fe92c8ddf00e"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"id": "tlog-cosignature",
|
|
162
|
+
"path": "tlog-cosignature.md",
|
|
163
|
+
"title": "Transparency Log Cosignatures",
|
|
164
|
+
"size": 10715,
|
|
165
|
+
"sha256": "9cf3f0ef2690319697fcbb4d1e823b593d90a1769912fdcb859ab89a3f924fbe"
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"id": "tlog-list",
|
|
169
|
+
"path": "tlog-list.md",
|
|
170
|
+
"title": "Transparency Log Lists",
|
|
171
|
+
"size": 2295,
|
|
172
|
+
"sha256": "9c83ee9dbe91b33fdd628f97e35aac7044ff092cfb9cd2e1458721651a0c5934"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"id": "tlog-mirror",
|
|
176
|
+
"path": "tlog-mirror.md",
|
|
177
|
+
"title": "Transparency Log Mirrors",
|
|
178
|
+
"size": 23273,
|
|
179
|
+
"sha256": "1aef508bd5fd8c4a341c307c5aa446ce4888a32ba6109c7c6da177a502c049e9"
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"id": "tlog-policy",
|
|
183
|
+
"path": "tlog-policy.md",
|
|
184
|
+
"title": "Transparency log trust policy",
|
|
185
|
+
"size": 9389,
|
|
186
|
+
"sha256": "2a8745ba91627d976ff2922f18c29a1ecd3567a03107749d5ae164cc5825bbd3"
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"id": "tlog-proof",
|
|
190
|
+
"path": "tlog-proof.md",
|
|
191
|
+
"title": "Transparency Log Proofs",
|
|
192
|
+
"size": 5733,
|
|
193
|
+
"sha256": "2b46a3a156ddb477fd88091bdb83b984135d9c42cba53cbed867d13d99afd5e8"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"id": "tlog-tiles",
|
|
197
|
+
"path": "tlog-tiles.md",
|
|
198
|
+
"title": "Tiled Transparency Logs",
|
|
199
|
+
"size": 12447,
|
|
200
|
+
"sha256": "951b7c96d57b1563a427ecc2c916a44daf4b12b0fa5d0d5188f9c0868855ab1b"
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"id": "tlog-witness",
|
|
204
|
+
"path": "tlog-witness.md",
|
|
205
|
+
"title": "Transparency Log Witness Protocol",
|
|
206
|
+
"size": 14392,
|
|
207
|
+
"sha256": "c9eaf9f403f0e8b3e61229d19f174022b9d80a3851c8c008bfc0ca9ee1180b89"
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"id": "upki-revocation",
|
|
211
|
+
"path": "upki-revocation.md",
|
|
212
|
+
"title": "UPKI Revocation",
|
|
213
|
+
"size": 37013,
|
|
214
|
+
"sha256": "e79976dfed9d73cf37ad1a1a7bc2d2e72dadd7ebbc57c4c0664d0181b0f90ab1"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"id": "vrf-r255",
|
|
218
|
+
"path": "vrf-r255.md",
|
|
219
|
+
"title": "ristretto255 VRF",
|
|
220
|
+
"size": 9450,
|
|
221
|
+
"sha256": "411c9aed374b4bf342f2afc277e3a3da3ffa22fb7be1f5fe3f4ffb1c4220a4c6"
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"id": "well-known-ssh-hosts",
|
|
225
|
+
"path": "well-known-ssh-hosts.md",
|
|
226
|
+
"title": ".well-known/ssh-known-hosts",
|
|
227
|
+
"size": 2187,
|
|
228
|
+
"sha256": "9b1573f4d78c56e8cee08928a601cb58ff3a880db3fbffb374fc448958d7a66f"
|
|
229
|
+
}
|
|
230
|
+
]
|
|
231
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""The C2SP catalogue: which specifications, at which commit, with which sha256. Metadata only, never text.
|
|
2
|
+
|
|
3
|
+
`catalogue.json` is package data. It pins one commit of https://github.com/C2SP/C2SP and, for each top-level
|
|
4
|
+
specification file at that commit, its size and sha256. The importer refuses any file whose digest differs, so a
|
|
5
|
+
rewritten history or a tampered mirror cannot change what a ref reads.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import re
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from importlib import resources
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from primarysource.errors import LibError
|
|
17
|
+
|
|
18
|
+
DOC_ID = re.compile(r"[A-Za-z0-9][A-Za-z0-9-]{0,62}")
|
|
19
|
+
COMMIT = re.compile(r"[0-9a-f]{40}")
|
|
20
|
+
SHA256 = re.compile(r"[0-9a-f]{64}")
|
|
21
|
+
HOSTS = ("raw.githubusercontent.com",)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True, slots=True)
|
|
25
|
+
class Terms:
|
|
26
|
+
"""What the publisher allows, and therefore where the text may be placed."""
|
|
27
|
+
|
|
28
|
+
licence: str
|
|
29
|
+
placement: str
|
|
30
|
+
terms_url: str
|
|
31
|
+
terms_read: str
|
|
32
|
+
summary: str
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(frozen=True, slots=True)
|
|
36
|
+
class Spec:
|
|
37
|
+
"""One specification file at the pinned commit."""
|
|
38
|
+
|
|
39
|
+
id: str
|
|
40
|
+
path: str
|
|
41
|
+
title: str
|
|
42
|
+
size: int
|
|
43
|
+
sha256: str
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass(frozen=True, slots=True)
|
|
47
|
+
class Catalogue:
|
|
48
|
+
repo: str
|
|
49
|
+
commit: str
|
|
50
|
+
raw_base: str
|
|
51
|
+
terms: Terms
|
|
52
|
+
specs: tuple[Spec, ...]
|
|
53
|
+
|
|
54
|
+
def spec(self, doc_id: str) -> Spec:
|
|
55
|
+
for candidate in self.specs:
|
|
56
|
+
if candidate.id == doc_id:
|
|
57
|
+
return candidate
|
|
58
|
+
known = ", ".join(s.id for s in self.specs)
|
|
59
|
+
raise LibError(f"no C2SP specification {doc_id!r} in the catalogue", f"known: {known}")
|
|
60
|
+
|
|
61
|
+
def url(self, spec: Spec) -> str:
|
|
62
|
+
return f"{self.raw_base}{self.commit}/{spec.path}"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def parse_catalogue(raw: dict[str, Any]) -> Catalogue:
|
|
66
|
+
"""Validate a catalogue document. Raises `ValueError`: a malformed catalogue is a packaging bug."""
|
|
67
|
+
commit = raw["commit"]
|
|
68
|
+
if not isinstance(commit, str) or not COMMIT.fullmatch(commit):
|
|
69
|
+
raise ValueError("catalogue commit must be 40 lowercase hex digits")
|
|
70
|
+
terms = raw["terms"]
|
|
71
|
+
specs: list[Spec] = []
|
|
72
|
+
for entry in raw["specs"]:
|
|
73
|
+
spec = Spec(entry["id"], entry["path"], entry["title"], int(entry["size"]), entry["sha256"])
|
|
74
|
+
if not DOC_ID.fullmatch(spec.id) or spec.path != f"{spec.id}.md":
|
|
75
|
+
raise ValueError(f"catalogue entry {spec.id!r}: id must be a slug and path <id>.md")
|
|
76
|
+
if not SHA256.fullmatch(spec.sha256):
|
|
77
|
+
raise ValueError(f"catalogue entry {spec.id!r}: sha256 must be 64 hex digits")
|
|
78
|
+
specs.append(spec)
|
|
79
|
+
if len({s.id for s in specs}) != len(specs):
|
|
80
|
+
raise ValueError("duplicate specification ids in the catalogue")
|
|
81
|
+
if terms["placement"] not in ("layer", "volume"):
|
|
82
|
+
raise ValueError("terms.placement must be 'layer' or 'volume'")
|
|
83
|
+
return Catalogue(
|
|
84
|
+
raw["repo"],
|
|
85
|
+
commit,
|
|
86
|
+
raw["raw_base"],
|
|
87
|
+
Terms(terms["licence"], terms["placement"], terms["terms_url"], terms["terms_read"], terms["summary"]),
|
|
88
|
+
tuple(specs),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def catalogue() -> Catalogue:
|
|
93
|
+
"""The packaged catalogue."""
|
|
94
|
+
text = resources.files("primarysource.c2sp").joinpath("catalogue.json").read_text(encoding="utf-8")
|
|
95
|
+
return parse_catalogue(json.loads(text))
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""`primarysource c2sp ...`: the collection's own commands.
|
|
2
|
+
|
|
3
|
+
primarysource c2sp import [--only ID ...]
|
|
4
|
+
primarysource c2sp docs
|
|
5
|
+
primarysource c2sp outline <doc>
|
|
6
|
+
primarysource c2sp read <ref> [--max-tokens N]
|
|
7
|
+
primarysource c2sp search <query> [--doc ID] [--limit N] [--start N]
|
|
8
|
+
primarysource c2sp cite <ref>
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import sys
|
|
15
|
+
from collections.abc import Sequence
|
|
16
|
+
|
|
17
|
+
from primarysource.c2sp import importer, library
|
|
18
|
+
from primarysource.errors import LibError
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _parser() -> argparse.ArgumentParser:
|
|
22
|
+
parser = argparse.ArgumentParser(prog="primarysource c2sp", description="C2SP specifications (CC BY 4.0)")
|
|
23
|
+
commands = parser.add_subparsers(dest="command", required=True)
|
|
24
|
+
imp = commands.add_parser("import", help="fetch and verify the pinned specifications (one request per second)")
|
|
25
|
+
imp.add_argument("--only", action="append", default=[], metavar="ID", help="import only this specification")
|
|
26
|
+
commands.add_parser("docs", help="the catalogue and what is imported")
|
|
27
|
+
commands.add_parser("outline", help="a specification's headings with refs").add_argument("doc")
|
|
28
|
+
read = commands.add_parser("read", help="read by ref")
|
|
29
|
+
read.add_argument("ref")
|
|
30
|
+
read.add_argument("--max-tokens", type=int, default=2000)
|
|
31
|
+
search = commands.add_parser("search", help="find blocks containing words")
|
|
32
|
+
search.add_argument("query")
|
|
33
|
+
search.add_argument("--doc")
|
|
34
|
+
search.add_argument("--limit", type=int, default=10)
|
|
35
|
+
search.add_argument("--start", type=int, default=0)
|
|
36
|
+
commands.add_parser("cite", help="the citation for a ref").add_argument("ref")
|
|
37
|
+
return parser
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _out(text: str) -> int:
|
|
41
|
+
sys.stdout.write(text if text.endswith("\n") else text + "\n")
|
|
42
|
+
return 0
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def main(argv: Sequence[str]) -> int:
|
|
46
|
+
args = _parser().parse_args(list(argv))
|
|
47
|
+
try:
|
|
48
|
+
if args.command == "import":
|
|
49
|
+
result = importer.import_specs(library.default_home(), args.only)
|
|
50
|
+
return _out(f"imported {len(result.files)} specifications at {result.commit[:7]} into {result.directory}")
|
|
51
|
+
if args.command == "docs":
|
|
52
|
+
return _out(library.docs())
|
|
53
|
+
if args.command == "outline":
|
|
54
|
+
return _out(library.outline(args.doc))
|
|
55
|
+
if args.command == "read":
|
|
56
|
+
return _out(library.read(args.ref, max_tokens=args.max_tokens))
|
|
57
|
+
if args.command == "search":
|
|
58
|
+
return _out(library.search(args.query, doc=args.doc, limit=args.limit, start=args.start))
|
|
59
|
+
return _out(library.cite(args.ref))
|
|
60
|
+
except LibError as exc:
|
|
61
|
+
print(exc.as_text(), file=sys.stderr)
|
|
62
|
+
return 1
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Import the pinned C2SP specifications into `$PRIMARYSOURCE_HOME/c2sp/<commit>/`.
|
|
2
|
+
|
|
3
|
+
Each specification is fetched from `raw.githubusercontent.com/C2SP/C2SP/<commit>/<id>.md`, at most one request per
|
|
4
|
+
second, and written only if its sha256 equals the catalogue's pin. `install.json` is written last, so a reader
|
|
5
|
+
never sees an install that is missing a file. C2SP specifications are licensed CC BY 4.0, so the installed files
|
|
6
|
+
may also be baked into an image layer; this code only ever writes to the home directory.
|
|
7
|
+
|
|
8
|
+
Run as `primarysource c2sp import [--only ID ...]`.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
from collections.abc import Sequence
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
from datetime import UTC, datetime
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
from primarysource.c2sp import markdown
|
|
20
|
+
from primarysource.c2sp._fetch import Fetcher, Transport, sha256, verify
|
|
21
|
+
from primarysource.c2sp.catalogue import DOC_ID, HOSTS, Catalogue, Spec
|
|
22
|
+
from primarysource.c2sp.catalogue import catalogue as packaged_catalogue
|
|
23
|
+
from primarysource.errors import LibError
|
|
24
|
+
from primarysource.home import atomic_write
|
|
25
|
+
|
|
26
|
+
INSTALL_RECORD = "install.json"
|
|
27
|
+
MAX_FILE = markdown.MAX_BYTES
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True, slots=True)
|
|
31
|
+
class ImportResult:
|
|
32
|
+
commit: str
|
|
33
|
+
directory: Path
|
|
34
|
+
files: dict[str, str] = field(default_factory=dict)
|
|
35
|
+
requests: int = 0
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def install_dir(home: Path, commit: str) -> Path:
|
|
39
|
+
return home / commit
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def import_specs(
|
|
43
|
+
home: Path,
|
|
44
|
+
only: Sequence[str] = (),
|
|
45
|
+
*,
|
|
46
|
+
cat: Catalogue | None = None,
|
|
47
|
+
transport: Transport | None = None,
|
|
48
|
+
fetcher: Fetcher | None = None,
|
|
49
|
+
) -> ImportResult:
|
|
50
|
+
"""Fetch, verify and install the catalogued specs (all, or those named in `only`) under `home`."""
|
|
51
|
+
cat = cat or packaged_catalogue()
|
|
52
|
+
chosen = [cat.spec(name) for name in only] if only else list(cat.specs)
|
|
53
|
+
fetcher = fetcher or Fetcher(HOSTS, transport)
|
|
54
|
+
target = install_dir(home, cat.commit)
|
|
55
|
+
record_path = target / INSTALL_RECORD
|
|
56
|
+
installed: dict[str, str] = {}
|
|
57
|
+
if record_path.is_file():
|
|
58
|
+
try:
|
|
59
|
+
installed = dict(json.loads(record_path.read_text(encoding="utf-8")).get("files", {}))
|
|
60
|
+
except (ValueError, OSError):
|
|
61
|
+
installed = {}
|
|
62
|
+
for spec in chosen:
|
|
63
|
+
data = fetcher.get(cat.url(spec), min(MAX_FILE, max(spec.size * 2, 1024)))
|
|
64
|
+
verify(data, spec.sha256, spec.id)
|
|
65
|
+
markdown.parse(data) # refuses non-UTF-8, NULs and over-bound text before anything is written
|
|
66
|
+
atomic_write(target / spec.path, data)
|
|
67
|
+
installed[spec.id] = spec.sha256
|
|
68
|
+
record = {
|
|
69
|
+
"collection": "c2sp",
|
|
70
|
+
"repo": cat.repo,
|
|
71
|
+
"commit": cat.commit,
|
|
72
|
+
"licence": cat.terms.licence,
|
|
73
|
+
"imported": datetime.now(UTC).isoformat(timespec="seconds"),
|
|
74
|
+
"files": dict(sorted(installed.items())),
|
|
75
|
+
}
|
|
76
|
+
atomic_write(record_path, (json.dumps(record, indent=2) + "\n").encode("utf-8"))
|
|
77
|
+
return ImportResult(cat.commit, target, dict(sorted(installed.items())), fetcher.requests)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def pin_specs(raw_base: str, commit: str, names: Sequence[str], fetcher: Fetcher) -> list[Spec]:
|
|
81
|
+
"""Maintainer step: fetch `names` (file ids) at `commit` and return catalogue entries with size and sha256.
|
|
82
|
+
The result is what goes in `catalogue.json`; it is a review step, never run by the importer."""
|
|
83
|
+
specs: list[Spec] = []
|
|
84
|
+
for name in names:
|
|
85
|
+
if not DOC_ID.fullmatch(name):
|
|
86
|
+
raise LibError(f"not a specification id: {name!r}", "")
|
|
87
|
+
data = fetcher.get(f"{raw_base}{commit}/{name}.md", MAX_FILE)
|
|
88
|
+
doc = markdown.parse(data)
|
|
89
|
+
specs.append(Spec(name, f"{name}.md", doc.title or name, len(data), sha256(data)))
|
|
90
|
+
return specs
|