Skip to content

Index bundles: licence texts inside, and an uploader for the contributor bucket - #194

Open
Deicyde wants to merge 2 commits into
mainfrom
claude/index-bundle-tooling
Open

Deicyde wants to merge 2 commits into
mainfrom
claude/index-bundle-tooling

Conversation

@Deicyde

@Deicyde Deicyde commented Nov 13, 2026 •

Copy link
Copy Markdown
Owner

The first half of the token-gated index download (Jack, 2026-11-11: "Token-gated download (Recommended)", and "Include ATLAS"): the bundle carries its libraries' licence texts, and a script puts it in the contributor bucket. The Worker routes that serve it come in a separate, release-affecting PR; this one is release-neutral.

The bundle (scripts/pipeline/package_index.py, schema index-bundle/2).

  • --licence-texts DIR takes DIR/<library id>/ per library and copies its LICENSE, LICENCE or COPYING file and any NOTICE into licences/<library>/. They are listed in the manifest (licences.texts) and linked from LICENCES.md. A library with no licence file is refused before anything is written, and so is a folder that names no library (a typo would otherwise drop one library's texts unnoticed). A copy handed to a contributor cannot be recalled, which is why the texts go inside it.
  • The converter's provenance keeps its shas and counts, but every absolute path or dir is cut to its name: a published manifest had the operator's home directory in it.
  • An index whose header line names another universe than the universe document is refused.
  • The command prints the sha256 of MANIFEST.json, which is the bundle's identity in the bucket and, later, in the Worker.

The upload (scripts/articles/upload_contrib.py, new, operator only, a dry run by default). It puts a bundle under index/v1/<manifest sha256>/ in the contributor bucket (wikilean-contrib). Every key is derived from content and never replaced: an object with the same size and MD5 is skipped, one with other bytes is refused, and so is an object under the prefix the bundle does not list. MANIFEST.json goes last, each upload is checked by its ETag, and the prefix is listed again at the end. Before any request it hashes every file against the manifest and refuses an unlisted file, a library that records no licence or has no licence texts, and use restrictions not named exactly with --acknowledge-restricted (ATLAS is one). It reuses upload_store.py's SigV4 client, whose credential helpers now take the variable names, and reads its own token, WIKILEAN_CONTRIB_R2_*: R2 scopes a token to buckets, and the articles upload must not be able to overwrite a bundle. docs/SECRETS.md lists it (not created yet) and scripts/ci-python.sh unsets it.

Work packages. upload_contrib.py package <envelope> puts one work package for the claims API (#196) at packages/v1/<envelope sha256>.json, the key POST /api/packages checks. It refuses an envelope that is not in canonical form (sorted keys, no spaces, ASCII) or whose files are not exactly the ones its package.json names, so the published sha256 is a function of the package. Same token, same never-replace rule, a dry run unless --execute.

Release. scripts/ and docs/ only, so release-neutral. Nothing is uploaded until the operator runs the script with the new token.

Checks. CI on 4c7d13e: worker, python (the uploader's 19 tests, 4 of them for packages, and the packager's), browser and attribution passed.
https://github.com/Deicyde/WikiLean/actions/runs/43525480984

…tor bucket

Outside contributors are to download the declaration index from a private R2
bucket through the Worker (Jack, 2026-11-11: "Token-gated download"; ATLAS
included). A copy handed out cannot be recalled, so the licences travel with it.

package_index.py (bundle schema /2):
- --licence-texts DIR copies each library's LICENSE, LICENCE or COPYING file
  and any NOTICE into licences/<library>/, lists them in the manifest
  (licences.texts) and LICENCES.md, and refuses a library with none, or a
  folder that names no library;
- the converter's provenance keeps its shas and counts but no absolute path
  of the operator's machine;
- an index whose header names another universe than the universe document
  is refused;
- the command prints the MANIFEST.json sha256, the bundle's identity.

upload_contrib.py (new, operator only, a dry run by default) puts a bundle in
the contributor bucket under index/v1/<manifest sha256>/: every key derived
from content, never replaced (same bytes skipped, other bytes refused),
MANIFEST.json last, each upload checked by its ETag and the prefix listed
again at the end. It refuses a bundle with a library that records no licence
or has no licence texts, and use restrictions not named exactly with
--acknowledge-restricted. It reuses upload_store.py's SigV4 client, whose
credential helpers now take the variable names, and reads its own token
(WIKILEAN_CONTRIB_R2_*), which docs/SECRETS.md now lists and CI unsets.
@Deicyde
Deicyde marked this pull request as ready for review November 13, 2026 14:43
…ork packages

upload_contrib.py package <envelope> puts a package's transport envelope at
packages/v1/<its sha256>.json, a dry run unless --execute, never replacing a
key. It refuses an envelope that is not in canonical form, or whose files
are not exactly the ones its package.json names, so the sha256 the Worker
publishes it under is a function of the package.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant