sci-protocol 0.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.
Files changed (80) hide show
  1. pysci/__init__.py +77 -0
  2. pysci/__main__.py +8 -0
  3. pysci/_cli/__init__.py +5 -0
  4. pysci/_cli/checks.py +173 -0
  5. pysci/_cli/clients/__init__.py +0 -0
  6. pysci/_cli/clients/archive.py +456 -0
  7. pysci/_cli/clients/credentials.py +54 -0
  8. pysci/_cli/clients/github.py +734 -0
  9. pysci/_cli/clients/hosting.py +238 -0
  10. pysci/_cli/clients/http.py +481 -0
  11. pysci/_cli/clients/huggingface.py +492 -0
  12. pysci/_cli/clients/index.py +435 -0
  13. pysci/_cli/clients/tsa.py +223 -0
  14. pysci/_cli/commands/__init__.py +44 -0
  15. pysci/_cli/commands/check.py +115 -0
  16. pysci/_cli/commands/claim.py +179 -0
  17. pysci/_cli/commands/common.py +91 -0
  18. pysci/_cli/commands/folders.py +83 -0
  19. pysci/_cli/commands/index.py +51 -0
  20. pysci/_cli/commands/keygen.py +58 -0
  21. pysci/_cli/commands/license.py +75 -0
  22. pysci/_cli/commands/protocol.py +72 -0
  23. pysci/_cli/commands/publish.py +282 -0
  24. pysci/_cli/commands/pull.py +337 -0
  25. pysci/_cli/commands/read.py +364 -0
  26. pysci/_cli/commands/revoke.py +207 -0
  27. pysci/_cli/commands/search.py +400 -0
  28. pysci/_cli/commands/sign.py +284 -0
  29. pysci/_cli/commands/submit.py +172 -0
  30. pysci/_cli/commands/timestamp.py +196 -0
  31. pysci/_cli/commands/verify.py +163 -0
  32. pysci/_cli/config.py +233 -0
  33. pysci/_cli/context.py +79 -0
  34. pysci/_cli/destinations.py +163 -0
  35. pysci/_cli/errors.py +168 -0
  36. pysci/_cli/main.py +229 -0
  37. pysci/_cli/output.py +80 -0
  38. pysci/_cli/retrieval.py +51 -0
  39. pysci/_cli/spec.py +113 -0
  40. pysci/_cli/storage/__init__.py +0 -0
  41. pysci/_cli/storage/catalog.py +371 -0
  42. pysci/_cli/storage/files.py +252 -0
  43. pysci/_cli/storage/inputs.py +205 -0
  44. pysci/_cli/storage/keys.py +132 -0
  45. pysci/_cli/storage/local_publish.py +88 -0
  46. pysci/accounts.py +135 -0
  47. pysci/baseline.py +142 -0
  48. pysci/bundle.py +878 -0
  49. pysci/citations.py +233 -0
  50. pysci/data/freetsa-cacert.pem +45 -0
  51. pysci/data/sci-baseline-LICENSE.txt +457 -0
  52. pysci/data/sci-protocol.md +613 -0
  53. pysci/der.py +146 -0
  54. pysci/discovery.py +461 -0
  55. pysci/draft.py +374 -0
  56. pysci/ed25519.py +153 -0
  57. pysci/errors.py +204 -0
  58. pysci/ids.py +61 -0
  59. pysci/jsonstrict.py +225 -0
  60. pysci/keys.py +247 -0
  61. pysci/manifest.py +302 -0
  62. pysci/metadata.py +406 -0
  63. pysci/paths.py +136 -0
  64. pysci/png.py +217 -0
  65. pysci/py.typed +0 -0
  66. pysci/records.py +384 -0
  67. pysci/rfc3339.py +66 -0
  68. pysci/schemas.py +75 -0
  69. pysci/source.py +166 -0
  70. pysci/specification.py +58 -0
  71. pysci/submission.py +225 -0
  72. pysci/templates.py +129 -0
  73. pysci/text.py +226 -0
  74. pysci/timestamps.py +867 -0
  75. pysci/zipformat.py +400 -0
  76. sci_protocol-0.2.0.dist-info/METADATA +112 -0
  77. sci_protocol-0.2.0.dist-info/RECORD +80 -0
  78. sci_protocol-0.2.0.dist-info/WHEEL +4 -0
  79. sci_protocol-0.2.0.dist-info/entry_points.txt +2 -0
  80. sci_protocol-0.2.0.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,613 @@
1
+ # SCI
2
+
3
+ SCI is an open, decentralized research publication system. Anyone can publish signed research, host copies, or build tools to find and verify publications. A shared protocol lets these independent services work together.
4
+
5
+ # SCI Protocol Specification
6
+
7
+ Protocol version: **0.1**. This draft may change before release.
8
+
9
+ `MUST` states a requirement. `MUST NOT` states a prohibition.
10
+ `SHOULD` states a recommendation. `MAY` states an option.
11
+ These words have the meanings in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119.html).
12
+
13
+ ## 1. Roles
14
+
15
+ | Role | Meaning |
16
+ | --- | --- |
17
+ | Publisher | The party that signs a publication. |
18
+ | Reader | A program that retrieves and verifies publication files. |
19
+ | Archiver | A service that stores and serves verified publication objects. |
20
+ | Indexer | A service that derives a searchable catalog from verified records. |
21
+
22
+ One program can perform more than one role.
23
+ A reader MUST be able to verify local files without an indexer or archiver.
24
+
25
+ ## 2. Publication bundle
26
+
27
+ A publication is identified by the SHA-256 hash of its exact manifest bytes.
28
+ The identifier is `sha256:` followed by 64 lowercase hexadecimal characters.
29
+
30
+ Corrections MUST be new publications with new manifest hashes.
31
+ A correction MAY cite the original with `supersedes` in its `relations` array.
32
+
33
+ Paths are relative to the bundle root.
34
+
35
+ | Path | Required? | Purpose |
36
+ | --- | --- | --- |
37
+ | `manifest.json` | Yes | Publisher’s public key and file list. |
38
+ | `manifest.json.sig` | Yes | Publisher’s signature, defined in [section 3](#3-manifest). |
39
+ | `sci.json` | Yes | Publication metadata defined in section 4, including any display items. |
40
+ | `README.md` | Yes | Main publication text as UTF-8 Markdown, following the text character rules in section 4. |
41
+ | `LICENSE` | For baseline | License terms supplied by the publisher as UTF-8 text, following the text character rules in section 4. |
42
+ | `files/<path>` | No | Supporting files, such as `files/results.png` or `files/data.csv`. |
43
+
44
+ `README.md` MUST be nonblank under [section 4.2](#42-text-characters).
45
+
46
+ Publishing tools MUST NOT include the publication's signing private key in the bundle.
47
+ Publishing tools SHOULD offer timestamping by default and allow the publisher to skip it or choose a timestamp authority.
48
+
49
+ A publication may be distributed as individual files at the paths above or as a single uncompressed ZIP. A publisher or archiver may assemble the ZIP from the signed manifest, its signature, and the listed files.
50
+
51
+ An optional `manifest.json.sig.tsr` sidecar contains timestamp proofs under [section 6.8](#68-timestamp-verification) for the SHA-256 of the exact bytes of `manifest.json.sig`. It MAY accompany the loose files but is not part of the bundle and MUST NOT be included as a ZIP entry. When distributed alongside a named ZIP, the sidecar SHOULD use `<digest>.tsr`, with the same digest as `<digest>.zip`. Archives serve the sidecar through `entry_url` as specified in [section 8.2](#82-archive-descriptor).
52
+
53
+ ### 2.1 Baseline publications
54
+
55
+ The baseline defines a shared compatibility guarantee. SCI indexers and archivers MUST NOT reject publications meeting its requirements because of per-publication resource limits or the approved license terms, while other admission policies remain applicable under [section 6.9](#69-service-policy). Publications outside the baseline remain valid if they satisfy the other format rules; services MAY decline them under their published policy.
56
+
57
+ A valid publication is baseline if it satisfies every limit below and includes `LICENSE` with the exact bytes of one of the approved licenses in this table. Hashes are SHA-256 over the exact license file bytes.
58
+
59
+ | Approved license | Short description | SHA-256 |
60
+ | --- | --- | --- |
61
+ | [SCI baseline license 0.1](sci-baseline-LICENSE.txt) | CC BY 4.0 for text, figures, and data; MIT for code. | `8ccfd5fdaa93e5e71dc8812bf11e0a7198bd0a534fa64b8970fd2de58e6c2478` |
62
+
63
+ | Quantity | Maximum |
64
+ | --- | --- |
65
+ | Complete ZIP under section 2.2 | 64 MiB (67,108,864 bytes) |
66
+ | Entries in the manifest's `files` object | 1,000 |
67
+ | Items in `sci.json`'s `display` array, if present | 3 |
68
+ | `manifest.json` | 256 KiB (262,144 bytes) |
69
+ | `sci.json` | 64 KiB (65,536 bytes) |
70
+ | `README.md` | 1 MiB (1,048,576 bytes) |
71
+
72
+ Display files in baseline publications MUST meet these requirements:
73
+
74
+ | Extension | Maximum file size | Requirements |
75
+ | --- | --- | --- |
76
+ | `.png` | 2 MiB (2,097,152 bytes) | At most 8,000,000 pixels; width and height each from 1 to 8,192 pixels.<br><br>For baseline checks, use the signature, chunk layout, and `IHDR` dimensions defined in [PNG, Third Edition](https://www.w3.org/TR/2025/REC-png-3-20250624/). The file MUST start with the PNG signature, followed by one `IHDR` chunk with 13 data bytes. Complete chunks MUST account for all bytes after the signature and end with one empty `IEND` chunk. No other `IHDR` or `IEND` chunk may occur. The chunks `acTL`, `fcTL`, and `fdAT` MUST NOT occur. Baseline checks do not require CRC verification, image decoding, or other PNG conformance checks. |
77
+
78
+ Compare the extension without regard to ASCII case.
79
+
80
+ For a publication distributed as loose files, use the size of the exact ZIP defined in section 2.2. Tools MAY calculate this size without constructing the ZIP:
81
+ ```text
82
+ zip_bytes = 22 + sum(file_bytes + 76 + 2 * path_bytes)
83
+ ```
84
+ The sum covers every ZIP entry, including `manifest.json` and `manifest.json.sig`. `file_bytes` is the entry's content length in bytes; `path_bytes` is its full relative ASCII path length in bytes.
85
+ Indexers and archivers MUST be able to complete the bundle checks in section 6.4 for baseline publications. They MUST NOT impose smaller limits that exclude otherwise valid baseline publications.
86
+
87
+ Publishing tools MUST check the baseline requirements before signing. They SHOULD require an explicit publisher override before signing a publication that does not meet them.
88
+
89
+ ### 2.2 Single-file bundle (ZIP)
90
+
91
+ When distributing a ZIP as a named file, use `<digest>.zip`, where `<digest>` MUST be the 64 lowercase hexadecimal characters of the manifest’s SHA-256 hash.
92
+ SCI readers MUST support this ZIP representation and be able to verify its contents even if the file has been renamed.
93
+
94
+ To save an individual file, a reader MUST verify the manifest and its signature, then check the file’s byte count and SHA-256 hash against the requested path in the manifest.
95
+ Before unpacking the whole bundle, a reader MUST verify the complete bundle as defined in [section 6.4](#64-complete-bundle-checks), including all of the ZIP requirements below.
96
+ The record layouts are defined in the [ZIP specification, sections 4.3–4.4](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT).
97
+
98
+ - Starting at byte zero, store consecutive local file headers and their data, then the corresponding central directory file headers in the same entry order, then one end-of-central-directory record ending at the last byte. These records and file data MUST account for every byte.
99
+ - The first two entries MUST be `manifest.json` and `manifest.json.sig`, in that order. Next, include each file listed in the manifest exactly once, in ascending lexicographic order of its full ASCII path bytes. Omit enclosing directories, explicit directory entries, and other files.
100
+ - For each file listed in the manifest, the filename bytes in both ZIP headers MUST exactly match the ASCII bytes of its decoded `files` key.
101
+ - Use classic ZIP in a single part. All disk-number fields MUST be zero. Use method 0 (stored) and zero general-purpose flags. Extra fields and comments MUST be empty. ZIP64 records, ZIP64 sentinel values, and data descriptors are prohibited in this version of the schema.
102
+ - Set the complete version-made-by field to `0x000A` (MS-DOS host, version 1.0), the version needed to extract to 1.0 (10), and both internal and external file attributes to zero. Every entry represents an ordinary file.
103
+ - In both headers, set the last-modification date to `0x0021` and time to `0x0000` (1980-01-01 00:00:00 in DOS format).
104
+ - Each central entry MUST point to the start of its corresponding local header. All fields shared by the two headers MUST agree byte for byte. Both sizes MUST equal the actual data length, and the CRC-32 MUST match the data. The end-of-central-directory record MUST give the actual entry count in both count fields, the byte offset where the central directory starts, and its length in bytes.
105
+
106
+ These rules determine the exact ZIP bytes from the exact manifest bytes, signature bytes, and listed file contents. The publication identifier remains the hash of the manifest, not the ZIP.
107
+
108
+ ## 3. Manifest
109
+
110
+ The publisher signs `manifest.json`, a [JSON](#7-json-encoding-rules) object with exactly these required fields:
111
+
112
+ | Field | Type | Rule |
113
+ | --- | --- | --- |
114
+ | `schema` | String | Exactly `sci-publication/0.1`. |
115
+ | `public_key` | Object | Ed25519 public JWK as defined in [section 5.1](#51-keys-and-signatures). |
116
+ | `files` | Object | Maps each listed path to its SHA-256 hash and byte count. |
117
+
118
+ `manifest.json.sig` contains the signature over the exact bytes of `manifest.json`, using the key in `public_key`.
119
+ `manifest.json.sig` MUST contain exactly the raw 64 signature bytes, without text encoding or surrounding bytes.
120
+ The signature follows [section 5.1](#51-keys-and-signatures).
121
+
122
+ Publishers SHOULD write the members of `files` in ascending lexicographic order by the ASCII bytes of the full path.
123
+ Each path in `files` maps to a JSON object with exactly two fields:
124
+
125
+ - `sha256` is the SHA-256 hash of the file’s exact bytes, written as a string of 64 lowercase hexadecimal characters.
126
+ - `bytes` is the file’s length in bytes, written as a decimal integer from 0 through `9007199254740991`, using digits only and no leading zeros.
127
+
128
+ The manifest’s `files` field MUST list `sci.json` and `README.md`. It MUST also list `LICENSE` if supplied. All other entries MUST be paths under `files/`.
129
+ A complete bundle MUST contain exactly the listed files plus the manifest and its signature.
130
+
131
+ ### 3.1 File paths
132
+
133
+ Paths MUST be relative to the bundle root. Use `/` to separate components.
134
+ The full path relative to the bundle root MUST be at most 255 ASCII bytes, including `/` separators.
135
+ Each component MUST contain one or more characters from the POSIX portable filename character set: ASCII letters, digits, period (`.`), underscore (`_`), and hyphen (`-`).
136
+
137
+ Reject `.`, `..`, and components ending in a period.
138
+ Reject [Windows device names](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file) in any component: `CON`, `PRN`, `AUX`, `NUL`, `COM1`–`COM9`, and `LPT1`–`LPT9`, including names followed by an extension. Compare these names without regard to ASCII case.
139
+
140
+ Compare paths without regard to ASCII case when checking for conflicts. Two file paths MUST NOT be equal, and a file path MUST NOT also name a directory used by another entry. For example, `files/a` conflicts with both `files/A` and `files/A/b.txt`.
141
+ Every occurrence of the same directory path MUST use the same capitalization. For example, `files/Data/a.csv` and `files/data/b.csv` conflict.
142
+
143
+ ## 4. Publication metadata
144
+
145
+ `sci.json` is a JSON object that permits only these fields:
146
+
147
+ | Field | Required | Type and rule |
148
+ | --- | --- | --- |
149
+ | `title` | Yes | Nonblank string, at most 200 Unicode code points. |
150
+ | `abstract` | Yes | Nonblank string, at most 2,500 code points. |
151
+ | `categories` | No | An array of at most three category objects, defined below. |
152
+ | `keywords` | No | An array of at most 20 nonblank strings, each at most 64 code points. |
153
+ | `display` | No | Ordered array of files the publisher wants readers to show first. |
154
+ | `citations` | No | An array of at most 1,000 citation objects, defined below. |
155
+
156
+ ### 4.1 Categories
157
+
158
+ Each category object contains exactly two required string fields:
159
+
160
+ | Field | Rule |
161
+ | --- | --- |
162
+ | `scheme` | Classification scheme identifier matching `[a-z][a-z0-9.-]{0,63}` in its entirety. |
163
+ | `code` | Nonblank category code, at most 128 Unicode code points. |
164
+
165
+ Scheme identifiers and category codes are compared exactly. Duplicate `(scheme, code)` pairs are prohibited.
166
+ Within each scheme, entries are ordered by publisher-assigned relevance.
167
+
168
+ Publishers SHOULD use the following identifiers when referring to these classification schemes:
169
+
170
+ | Identifier | Classification scheme |
171
+ | --- | --- |
172
+ | `arxiv` | [arXiv Category Taxonomy](https://arxiv.org/category_taxonomy) |
173
+ | `anzsrc-for-2020` | [ANZSRC 2020 Fields of Research](https://www.abs.gov.au/statistics/classifications/australian-and-new-zealand-standard-research-classification-anzsrc/2020) |
174
+
175
+ ### 4.2 Text characters
176
+
177
+ All decoded strings in `sci.json`, including object keys, and the decoded contents of `README.md` and `LICENSE` MUST contain only Unicode Assignables as defined in [RFC 9839 §4.3](https://www.rfc-editor.org/rfc/rfc9839.html#section-4.3).
178
+
179
+ TAB (U+0009), LF (U+000A), and CR (U+000D) are permitted only in `abstract`, citation `reason`, `README.md`, and `LICENSE`.
180
+
181
+ Implementations MUST reject text that violates these restrictions.
182
+
183
+ A nonblank string MUST contain at least one permitted character other than SPACE, TAB, LF, or CR.
184
+
185
+ ### 4.3 Display items
186
+
187
+ Each `display` item is an object with a required `path` and an optional `caption`. Only these fields are permitted.
188
+ Each path MUST exactly match a distinct entry under `files/` in the signed manifest.
189
+ A caption MUST be a nonblank string of at most 2,000 Unicode code points.
190
+
191
+ ### 4.4 Citations
192
+
193
+ A publication can cite other SCI publications by their publication identifiers or external work by DOI or URL.
194
+ Publishers SHOULD prefer SCI identifiers or DOIs over URLs when available, to keep citations independent of hosting locations.
195
+
196
+ A citation is a JSON object with required `target` and optional `relations` and `reason` fields. No other fields are permitted.
197
+ `relations`, when present, MUST be an array of distinct strings from the table below. Multiple relations may apply; their order has no meaning.
198
+ Each relation is a publisher claim about the citing publication's relationship to the cited work.
199
+
200
+ | Relation | Meaning |
201
+ | --- | --- |
202
+ | `uses` | Uses material, methods, or results from the cited work. |
203
+ | `reviews` | Substantively evaluates the cited work, without implying agreement or disagreement. |
204
+ | `supports` | Provides evidence or reasoning supporting a claim in the cited work. |
205
+ | `challenges` | Provides evidence or reasoning questioning a claim in the cited work. |
206
+ | `supersedes` | Is intended to replace the cited publication. |
207
+
208
+ An absent or empty `relations` array indicates an ordinary citation without a more specific relationship claim.
209
+ `reason`, when present, is nonblank text of at most 1,000 code points.
210
+
211
+ `target` MUST use one of these exact forms:
212
+
213
+ - `sci:sha256:<hash>`: the publication identifier from [section 2](#2-publication-bundle), prefixed with `sci:`.
214
+ - `doi:<name>`: a DOI URI conforming to the [DOI URI Scheme specification](https://doi.org/10.1000/292).
215
+ - An HTTP or HTTPS URI under [RFC 9110 section 4.2](https://www.rfc-editor.org/rfc/rfc9110.html#section-4.2), with an optional fragment under [RFC 3986 section 3.5](https://www.rfc-editor.org/rfc/rfc3986.html#section-3.5).
216
+
217
+ DOI targets MUST use that specification's encoding and equivalence rules.
218
+
219
+ ## 5. Publisher identity
220
+
221
+ A publisher is identified by its Ed25519 public key.
222
+ A signature establishes control of a key, subject to the security of that key and algorithm.
223
+ It does not establish correctness of the published claims, content safety, or license ownership.
224
+
225
+ **Warning:** Publishers MUST protect their private keys from unauthorized access. Anyone with a private key can sign publications and revoke that key.
226
+
227
+ ### 5.1 Keys and signatures
228
+
229
+ Public keys MUST use the JWK representation defined in [RFC 8037](https://www.rfc-editor.org/rfc/rfc8037.html).
230
+ Only `kty`, `crv`, and `x` are permitted in a public key object.
231
+ Verifiers MUST reject noncanonical base64url encodings of `x`.
232
+
233
+ Signatures MUST use the 64-byte pure Ed25519 format defined in [RFC 8032](https://www.rfc-editor.org/rfc/rfc8032.html).
234
+ Signers MUST use the deterministic signing procedure in [RFC 8032 §5.1.6](https://www.rfc-editor.org/rfc/rfc8032.html#section-5.1.6).
235
+ Verifiers MUST check signatures according to [RFC 8032 §5.1.7](https://www.rfc-editor.org/rfc/rfc8032.html#section-5.1.7).
236
+ The public key `A` and signature point `R` MUST also be nonidentity members of the prime-order subgroup.
237
+
238
+ Each signed record type MUST use distinct `schema` values.
239
+ Before accepting a record, verifiers MUST check that its `schema` identifies the expected record type and a supported version.
240
+
241
+ ### 5.2 External identity claims
242
+
243
+ A public key can claim affiliation with a domain or an external account.
244
+ Claims are optional. Each account can list many keys, and each key can name many accounts.
245
+ To verify the affiliation, the domain or account hosts a claim signed by the key that names that domain or account.
246
+ The key’s signature and the account’s hosted file show agreement from both sides.
247
+
248
+ Publishers and indexers can supply account hints for lookup. Hints do not prove an association.
249
+ Readers and indexers MUST complete the checks in section 6.6 before displaying an association as verified.
250
+ **Warning:** If a domain or account changes owners, the new owner can reuse the previous owner’s hosted claim until it expires.
251
+
252
+ #### 5.2.1 Signed claim
253
+
254
+ The account hosts the claim in a `.json` file and its signature in a matching `.json.sig` file.
255
+ The claim is a JSON object with only these fields:
256
+
257
+ | Field | Required? | Type and rule |
258
+ | --- | --- | --- |
259
+ | `schema` | Yes | String, exactly `sci-key-claim/0.1`. |
260
+ | `public_key` | Yes | Public JWK under section 5.1. |
261
+ | `account` | Yes | Object with exactly two string fields: `type` and `id`. The account method defines their values. |
262
+ | `name` | No | Publisher display name. Nonblank string, at most 80 Unicode code points. Uses the `title` character rules in section 4.2. |
263
+ | `note` | No | Nonblank string, at most 2,500 Unicode code points. Uses the `abstract` character rules in section 4.2. |
264
+ | `expires` | Yes | String, an expiry time in [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339.html#section-5.6) format. |
265
+
266
+ Publishers SHOULD set `expires` no more than 90 days after signing.
267
+ Expiry ends a claim's current status. A hosted claim still covers publications timestamped before `expires`, under section 6.6, so a retired key needs no renewal.
268
+ Readers and indexers MAY impose a shorter remaining validity period as local policy.
269
+ Readers and indexers MAY reuse a successful association check until the claim expires.
270
+
271
+ The claim JSON MUST follow section 7.
272
+ The `.json.sig` file contains the raw 64-byte signature defined in section 5.1.
273
+ The claim's key signs the exact `.json` bytes.
274
+ Readers MAY use the account's avatar or domain icon as an unsigned display image.
275
+
276
+ #### 5.2.2 Domain method
277
+
278
+ `account.type` MUST be `domain`.
279
+ `account.id` MUST contain only a lowercase ASCII DNS host name, without a final dot.
280
+ Internationalized labels MUST use valid [IDNA2008 A-labels](https://www.rfc-editor.org/rfc/rfc5890.html#section-2.3.2.1). IP addresses are not permitted.
281
+
282
+ The domain MUST host the claim and signature for each key at:
283
+
284
+ ```text
285
+ https://<account.id>/.well-known/sci/keys/<public_key.x>.json
286
+ https://<account.id>/.well-known/sci/keys/<public_key.x>.json.sig
287
+ ```
288
+
289
+ #### 5.2.3 GitHub method
290
+
291
+ `account.type` MUST be `github`.
292
+ `account.id` MUST be the GitHub account's login in lowercase, for a user or organization.
293
+ The account MUST host the files in either its public `<account.id>/<account.id>` or `<account.id>/.github` repository, on the `main` branch.
294
+ Both locations are permitted for users and organizations:
295
+ ```text
296
+ https://raw.githubusercontent.com/<account.id>/<account.id>/main/sci/keys/<public_key.x>.json
297
+ https://raw.githubusercontent.com/<account.id>/<account.id>/main/sci/keys/<public_key.x>.json.sig
298
+ ```
299
+ or:
300
+ ```text
301
+ https://raw.githubusercontent.com/<account.id>/.github/main/sci/keys/<public_key.x>.json
302
+ https://raw.githubusercontent.com/<account.id>/.github/main/sci/keys/<public_key.x>.json.sig
303
+ ```
304
+ Readers and indexers MAY also use the [GitHub API](https://docs.github.com/en/rest/repos/repos#get-a-repository) to check that the account still owns the repository.
305
+
306
+ #### 5.2.4 Hugging Face method
307
+
308
+ `account.type` MUST be `huggingface`.
309
+ `account.id` MUST be the user or organization name, with the spelling returned by the Hub API.
310
+ The account MUST host the files in its public `<account.id>/README` Space, on the `main` branch:
311
+
312
+ ```text
313
+ https://huggingface.co/spaces/<account.id>/README/resolve/main/sci/keys/<public_key.x>.json
314
+ https://huggingface.co/spaces/<account.id>/README/resolve/main/sci/keys/<public_key.x>.json.sig
315
+ ```
316
+
317
+ Readers and indexers MAY also use the Hub API to check that the account still owns the Space.
318
+
319
+ ### 5.3 Key revocation
320
+
321
+ A key holder can revoke a key with a signed record.
322
+ Revocation is permanent.
323
+ A revocation consists of two required files and one optional file:
324
+
325
+ | File | Required? | Contents |
326
+ | --- | --- | --- |
327
+ | `revocation.json` | Yes | Revocation record. |
328
+ | `revocation.json.sig` | Yes | Signature of the record. |
329
+ | `revocation.json.sig.tsr` | No | Timestamp proofs for the signature under section 6.8. |
330
+
331
+ `revocation.json` MUST follow section 7 and contain exactly these fields:
332
+
333
+ | Field | Type and rule |
334
+ | --- | --- |
335
+ | `schema` | String, exactly `sci-key-revocation/0.1`. |
336
+ | `public_key` | The public JWK of the key to revoke, under section 5.1. |
337
+
338
+ `revocation.json.sig` MUST contain that key's signature over the exact bytes of `revocation.json`, under section 5.1.
339
+ An optional `revocation.json.sig.tsr` file contains timestamp proofs under section 6.8 for the SHA-256 of the signature file.
340
+
341
+ A verified key revocation MUST end the verified status of all domain and account associations for that key.
342
+ Readers MUST mark a publication's signing key as revoked when they have a verified revocation for that key.
343
+ A missing revocation record is not proof that a key has never been revoked.
344
+
345
+ Once an indexer verifies a key revocation, it MUST reject new catalog admissions for that key.
346
+ Publication dates and timestamp proofs MUST NOT override this rule.
347
+ Revocation alone MUST NOT remove publications already admitted to its searchable catalog. The indexer MUST mark their signing key as revoked.
348
+ An operator block MUST NOT be presented as a publisher's key revocation.
349
+
350
+ **Warning:** A key holder can create the signed record in advance. Keep it private until revocation is intended. Anyone with the record and signature can submit them.
351
+
352
+ ## 6. Verification
353
+
354
+ Verification lets readers and services check files and signed records without trusting the party that supplies them.
355
+
356
+ Implementations MUST distinguish failed checks from checks they cannot complete.
357
+ They MUST report an unknown `schema` value as unsupported.
358
+ They MUST NOT report a publication as invalid only because data is unavailable or a local limit prevents a check.
359
+
360
+ Assume that a publisher can deliberately construct and sign a malicious bundle.
361
+ Verification MUST NOT execute content, open external applications, or expand nested archives. Read ZIP entries as bytes without extracting them. Use the bytes that passed verification for later rendering or extraction.
362
+ Implementations MUST check input bounds and arithmetic for overflow before reading, seeking, or allocating memory. They MUST limit input sizes and verification work. Stop if a declared size or local limit is exceeded.
363
+
364
+ ### 6.1 Manifest checks
365
+
366
+ Before using a manifest to verify publication files, readers, archivers, and indexers MUST:
367
+
368
+ - Check the JSON encoding against [section 7](#7-json-encoding-rules).
369
+ - Check the fields, types, and values against [section 3](#3-manifest).
370
+ - Check the file paths and path conflicts against [section 3.1](#31-file-paths).
371
+ - Calculate the publication identifier from the original manifest bytes, as specified in [section 2](#2-publication-bundle).
372
+ - If an expected publication identifier was supplied, check that the calculated identifier matches it.
373
+ - Check the public key and manifest signature against [section 5.1](#51-keys-and-signatures), using the key and original manifest bytes specified in [section 3](#3-manifest).
374
+
375
+ ### 6.2 File checks
376
+
377
+ Before using a file as part of a publication, readers, archivers, and indexers MUST:
378
+
379
+ - Complete the [manifest checks](#61-manifest-checks).
380
+ - Check that the requested path is a key in the manifest's `files` object.
381
+ - Check that the retrieved byte count equals that entry's `bytes` value.
382
+ - Check that the SHA-256 of the retrieved bytes equals that entry's `sha256` value.
383
+
384
+ Complete these checks before decoding or rendering the file.
385
+ Publishing tools SHOULD check that display images decode. Readers SHOULD limit memory and processing time when decoding display images.
386
+
387
+ ### 6.3 Metadata checks
388
+
389
+ Before presenting publication metadata as verified, readers, archivers, and indexers MUST:
390
+
391
+ - Complete the [file checks](#62-file-checks) for `sci.json`, `README.md`, and any declared `LICENSE`.
392
+ - Check the JSON encoding of `sci.json` against [section 7](#7-json-encoding-rules).
393
+ - Check its fields, types, and values against [section 4](#4-publication-metadata), including its subsections.
394
+ - Check `README.md` and any declared `LICENSE` against the text requirements in [section 2](#2-publication-bundle) and [section 4.2](#42-text-characters).
395
+
396
+ ### 6.4 Complete bundle checks
397
+
398
+ Before unpacking or accepting a complete bundle, readers MUST:
399
+
400
+ - For a ZIP, check the entire archive against [section 2.2](#22-single-file-bundle-zip).
401
+ - Complete the [metadata checks](#63-metadata-checks).
402
+ - Complete the [file checks](#62-file-checks) for every path in the manifest's `files` object.
403
+ - Check that the bundle contains exactly the files permitted by [section 3](#3-manifest). For loose files, exclude the optional `manifest.json.sig.tsr` sidecar from this inventory; timestamp verification is separate under section 6.8.
404
+
405
+ Indexers and archivers SHOULD complete these checks before accepting or serving a complete bundle.
406
+
407
+ When extracting files, a reader MUST write only within the selected destination.
408
+
409
+ ### 6.5 Catalog admission
410
+
411
+ Before adding a publication to its searchable catalog, an indexer MUST:
412
+
413
+ - Complete the [metadata checks](#63-metadata-checks).
414
+ - Apply the revocation rule in [section 5.3](#53-key-revocation).
415
+ - Apply its published admission policy, including its limits, under [section 6.9](#69-service-policy).
416
+
417
+ An indexer SHOULD complete the [complete bundle checks](#64-complete-bundle-checks) before adding a publication to its searchable catalog.
418
+
419
+ After catalog admission, indexers SHOULD asynchronously obtain and retain a timestamp proof verified under their trust policy if no such proof is retained. Failure MUST NOT block or reverse catalog admission.
420
+
421
+ ### 6.6 Key association checks
422
+
423
+ Before reporting a key's external association as verified, readers, archivers, and indexers MUST:
424
+
425
+ - Retrieve the claim and signature using the account method in [section 5.2](#52-external-identity-claims).
426
+ - Check the claim against section 5.2.1 and section 7.
427
+ - Check that the claim's decoded public key bytes equal the expected public key bytes.
428
+ - Check that `account.type` and `account.id` equal the requested account type and identifier.
429
+ - Check the claim signature under section 5.1.
430
+ - Check that the current time is before `expires`. Alternatively, to report that a publication falls within a hosted claim's validity, check that a timestamp verified under section 6.8 for that publication has a `genTime` before `expires`.
431
+ - Check that the key has no known verified revocation.
432
+
433
+ A successful check for the same key and account MAY be reused under section 5.2.1.
434
+ Expiry and known revocations MUST still be checked.
435
+
436
+ ### 6.7 Revocation checks
437
+
438
+ Before recording a key as revoked, readers, archivers, and indexers MUST:
439
+
440
+ - Check the record against section 5.3 and section 7.
441
+ - Check the public key and signature under section 5.1, using the key and original record bytes specified in section 5.3.
442
+
443
+ Retain the verified record and signature. Keep the revoked status even if the source removes the files.
444
+
445
+ ### 6.8 Timestamp verification
446
+
447
+ SCI timestamp files MUST contain one or more concatenated DER-encoded `TimeStampResp` values, with no other bytes. Each value MUST follow [RFC 3161](https://www.rfc-editor.org/rfc/rfc3161.html), as updated by [RFC 5816](https://www.rfc-editor.org/rfc/rfc5816.html), and MUST be checked independently when verifying the file.
448
+
449
+ Readers MUST delimit values by their DER tag and length, within the bounds in section 6. If the remaining bytes do not begin with a complete DER value, readers MUST stop parsing and MUST NOT scan ahead; earlier individually verified proofs remain usable even though the file is malformed. A complete value that fails verification does not prevent checking later values.
450
+
451
+ Readers MAY verify timestamp proofs.
452
+ Before reporting a verified timestamp, readers MUST:
453
+
454
+ - Complete the [manifest checks](#61-manifest-checks) or [revocation checks](#67-revocation-checks), as applicable.
455
+ - Check that the response status is `granted` or `grantedWithMods` and a `TimeStampToken` is present.
456
+ - Check that the token's `messageImprint` uses SHA-256 and matches the SHA-256 of the verified signature bytes.
457
+ - Verify the token under RFC 3161 and RFC 5816, including its signature, signing certificate, certificate chain, and revocation status, using local trust policy.
458
+ - Report the token's `genTime`, any stated accuracy, and the timestamp authority.
459
+
460
+ Readers and services choose which timestamp authorities they trust.
461
+ A failed timestamp check MUST NOT invalidate otherwise valid publication signatures or file hashes.
462
+ Anyone MAY distribute timestamp proofs as a separate sidecar. Adding, replacing, or removing the sidecar changes neither the publication identifier nor the ZIP bytes.
463
+
464
+ ### 6.9 Service policy
465
+
466
+ Services MAY set admission requirements stricter than the format rules, subject to the baseline requirements in [section 2.1](#21-baseline-publications).
467
+ They MUST publish their admission limits and license requirements.
468
+ The baseline does not require a service to accept every publication or provide unlimited storage or throughput.
469
+
470
+ Any additional policies MUST NOT be used to impose smaller per-publication size, file-count, or structural limits on baseline publications. Indexers and archivers MUST NOT reject baseline publications because of their approved license terms or require additional licensing grants as a condition of admission. Services MAY accept other licenses under their published policy.
471
+
472
+ A service MUST distinguish an admission-policy refusal or a temporary capacity failure from a failed verification check.
473
+
474
+ ## 7. JSON encoding rules
475
+
476
+ JSON documents MUST follow [RFC 8259](https://www.rfc-editor.org/rfc/rfc8259.html), with these additional restrictions:
477
+
478
+ - Use UTF-8 without a byte-order mark.
479
+ - Reject duplicate object keys after escape decoding, at every depth.
480
+ - Reject unpaired UTF-16 surrogate escapes.
481
+
482
+ Field types and permitted values are defined by each record's schema.
483
+ Authors can choose whitespace, field order, and escape spelling within these rules.
484
+ Hashes and signatures MUST be checked against the original JSON bytes.
485
+
486
+ ## 8. Discovery and retrieval
487
+
488
+ These interfaces let readers find and retrieve publications without custom code for each service.
489
+ A reader can ask an index for publication sources or use an archive descriptor supplied directly.
490
+ Services that implement these interfaces MUST follow this section.
491
+
492
+ Descriptors and publication lookup responses under section 8.1.1 MUST be JSON objects under section 7.
493
+ Descriptor URLs and expanded templates MUST be absolute URLs.
494
+ Templates use [RFC 6570](https://www.rfc-editor.org/rfc/rfc6570.html) with only the expressions listed for each field.
495
+
496
+ Responses intended for browser readers SHOULD send `Access-Control-Allow-Origin: *`.
497
+ For bundle range responses, they SHOULD also send `Access-Control-Expose-Headers: Content-Range`.
498
+
499
+ ### 8.1 Index descriptor
500
+
501
+ An index that supports this interface MUST publish its descriptor at:
502
+
503
+ ```text
504
+ https://<index>/.well-known/sci/index.json
505
+ ```
506
+
507
+ The following fields are strings:
508
+
509
+ | Field | Required? | Purpose | Required expressions |
510
+ | --- | --- | --- | --- |
511
+ | `schema` | Yes | Exactly `sci-index/0.1`. | None |
512
+ | `publication_lookup_url` | Yes | Find archive descriptors for a publication under section 8.1.1. | `{id}` |
513
+ | `revocation_lookup_url` | Yes | Retrieve revocation files under section 8.1.2. | `{x}`, `{file}` |
514
+ | `timestamp_lookup_url` | Yes | Retrieve retained publication timestamp proofs under section 8.1.3. | `{id}` |
515
+ | `submit_url` | No | Submit publication locations under section 8.1.4. | None |
516
+ | `policy_url` | No | Service policy, including admission limits and license requirements under section 6.9. | None |
517
+ | `docs_url` | No | Documentation for the index's API and services. | None |
518
+
519
+ Additional fields MAY describe other services, such as revocation or timestamp submission.
520
+
521
+ #### 8.1.1 Publication lookup
522
+
523
+ `id` is the manifest's SHA-256 hash in 64 lowercase hexadecimal characters, without the `sha256:` prefix.
524
+ A successful GET to the expanded `publication_lookup_url` returns `200` with a `descriptors` array of archive descriptor URL strings:
525
+
526
+ ```json
527
+ {"descriptors": ["https://archive.example/.well-known/sci/archive.json"]}
528
+ ```
529
+
530
+ Each descriptor identifies a source that may provide the requested publication.
531
+ If the index has no sources to return, it MAY return `{"descriptors":[]}` or `404`.
532
+ Readers MUST verify retrieved files under section 6, using the requested publication ID.
533
+
534
+ #### 8.1.2 Revocation lookup
535
+
536
+ For each key whose revocation it verifies, an index MUST retain at least one verified record and its matching signature.
537
+ It MUST serve that pair through its revocation lookup interface.
538
+
539
+ `x` is the key's canonical JWK `x` value. `file` is one of the three filenames in section 5.3.
540
+ A GET to the expanded `revocation_lookup_url` returns the original file bytes with `200`, or `404` if the index has no such file.
541
+ Readers MUST check that the record's `public_key.x` equals the requested key and verify the record and signature under section 6.7.
542
+ A missing record does not prove that the key has never been revoked.
543
+
544
+ #### 8.1.3 Timestamp lookup
545
+
546
+ `id` uses section 8.1.1. A GET to the expanded `timestamp_lookup_url` returns `200` with the retained proofs for that publication's manifest signature, encoded as a timestamp file under section 6.8, or `404` if none are retained. Servers SHOULD use content type `application/octet-stream` for timestamp files. Readers MUST verify proofs under section 6.8 using the requested publication ID before reporting a verified timestamp.
547
+
548
+ Indexers MAY retain a bounded selection of proofs under their published trust and resource policies. A `404` response does not prove that no timestamp exists.
549
+
550
+ #### 8.1.4 Publication submission
551
+
552
+ An index MAY accept submissions through `submit_url`. If advertised, the endpoint MUST implement this section. Publishers, archivers, and other callers use the same interface; the caller need not control the signing key.
553
+
554
+ Send a POST with `Content-Type: application/json` and an object under section 7 containing only these fields:
555
+
556
+ | Field | Required? | Rule |
557
+ | --- | --- | --- |
558
+ | `id` | Yes | Publication digest under section 8.1.1. |
559
+ | `descriptors` | Yes | Nonempty array of distinct absolute archive descriptor URLs under section 8.2. |
560
+ | `account` | No | Account object under section 5.2.1, supplied as an unsigned lookup hint. |
561
+
562
+ Apply section 6 and the service policy under section 6.9. Success confirms registration of at least one supplied location for publication lookup under section 8.1.1. Repeat submissions MUST NOT create duplicate registrations.
563
+
564
+ Return `200` or `201` on success. The response MUST use `application/json` and be an object under section 7 containing `id`, the requested publication digest. Services MAY return additional fields.
565
+
566
+ Errors MUST use a `4xx` or `5xx` response with `application/json` and an `error` object containing string fields `code` and `message`, and Boolean `retryable`. Codes MUST be stable.
567
+
568
+ ### 8.2 Archive descriptor
569
+
570
+ An archive descriptor gives retrieval routes for publication files.
571
+ The following fields are strings:
572
+
573
+ | Field | Required? | Meaning | Permitted expressions |
574
+ | --- | --- | --- | --- |
575
+ | `schema` | Yes | Exactly `sci-archive/0.1`. | None |
576
+ | `bundle_url` | No | Complete publication ZIP under section 2.2. | `{id}`, `{id2}` |
577
+ | `entry_url` | No | Individual file, including the manifest, signature, and optional timestamp sidecar. | `{id}`, `{id2}`, `{+path}` |
578
+ | `blob_url` | No | Individual file addressed by its SHA-256 hash. | `{sha256}` |
579
+ | `list_url` | No | Current list of advertised publication IDs. | None |
580
+
581
+ `id` uses section 8.1.1. `id2` is its first two characters.
582
+ `path` is the exact entry path relative to the bundle root. `sha256` is the file's SHA-256 hash in 64 lowercase hexadecimal characters.
583
+ The archiver MUST serve the manifest and signature through `bundle_url` or `entry_url` for each advertised publication.
584
+ Listed files MAY be available through any combination of routes.
585
+ An archiver serving publication timestamp proofs MUST provide `entry_url` and serve them at `path = manifest.json.sig.tsr`. Readers MAY retrieve this sidecar separately and verify it under section 6.8.
586
+ `bundle_url` SHOULD support single-range requests with status `206` when the range is satisfiable.
587
+
588
+ `list_url` returns UTF-8 text with one publication ID per LF-terminated line, using the form in section 8.1.1.
589
+
590
+ #### 8.2.1 Descriptor discovery
591
+
592
+ For discovery from a domain or repository, an archiver MUST publish the descriptor at the corresponding location:
593
+
594
+ | Source | Descriptor URL |
595
+ | --- | --- |
596
+ | Domain | `https://<domain>/.well-known/sci/archive.json` |
597
+ | GitHub repository | `https://raw.githubusercontent.com/<owner>/<repo>/main/sci/archive.json` |
598
+ | Hugging Face repository | `<repository-url>/resolve/main/sci/archive.json` |
599
+
600
+ For Hugging Face, `repository-url` is the full model, dataset, or Space repository URL, without a trailing slash.
601
+
602
+ A descriptor MAY also be supplied directly or hosted at another URL.
603
+
604
+ ## Acknowledgements
605
+
606
+ SCI draws on ideas explored by [Nanopublications](https://nanopub.net/) for decentralized scientific publishing, [Nostr](https://github.com/nostr-protocol/nips/blob/master/01.md) for signed records and public-key identity, and [IPFS](https://docs.ipfs.tech/concepts/immutability/) for content addressing.
607
+ Related precedents include [Keybase’s signed identity proofs](https://book.keybase.io/docs/server), [OpenPGP revocation certificates](https://www.gnupg.org/faq/gnupg-faq.html), and [OpenID Connect Discovery’s service discovery documents](https://openid.net/specs/openid-connect-discovery-1_0.html).
608
+
609
+ ## Changelog
610
+
611
+ ### 0.1 (draft) — 2026-11-06
612
+
613
+ Initial protocol draft. All schema versions start at `0.1`.