breaking: implement the new sysand index read protocol#288
Merged
andrius-puksta-sensmetry merged 122 commits intosensmetry:mainfrom May 6, 2026
Merged
breaking: implement the new sysand index read protocol#288andrius-puksta-sensmetry merged 122 commits intosensmetry:mainfrom
andrius-puksta-sensmetry merged 122 commits intosensmetry:mainfrom
Conversation
4f04d45 to
4b14059
Compare
This was referenced Apr 16, 2026
ef6f5e0 to
3d43a8f
Compare
4 tasks
6ee8561 to
de55a9e
Compare
Collaborator
|
Impressive... |
This comment was marked as resolved.
This comment was marked as resolved.
Collaborator
andrius-puksta-sensmetry
left a comment
There was a problem hiding this comment.
Just a start...
Minimal kpar fixtures should put `.project.json` and `.meta.json` at the archive root. Using a synthetic `root/` directory makes these fixtures look like they are testing root guessing rather than the ordinary minimal archive shape. Update the remaining minimal kpar builders and comments to use root-level metadata files. Fixtures that intentionally exercise root guessing keep their own non-root directory names. Signed-off-by: Erik Sundell <erik.sundell+2025@sensmetry.com>
The index-backed lock tests registered mocks that were either unasserted or had implicit request counts. That made accidental index enumeration and skipped network paths hard to spot. Give those mocks exact expectations and assert them. In particular, pin direct IRI locking to avoid `index.json` enumeration and pin lock-time archive handling so `lock` records advertised kpar metadata without downloading the kpar. Signed-off-by: Erik Sundell <erik.sundell+2025@sensmetry.com>
Several tests kept mockito mocks alive without declaring the expected request count. That made accidental extra or missing requests harder to spot in review. Add explicit expectations to the straightforward single-call mocks, assert the previously underscore-bound mocks, and keep negative mocks in the index cache test to pin the lazy get_project behavior. Signed-off-by: Erik Sundell <erik.sundell+2025@sensmetry.com>
The mock_project helper used lower-bound expectations for legacy HTTP-source fixtures. The observed request counts are deterministic, so make each caller pass the exact project and metadata probe counts instead. Signed-off-by: Erik Sundell <erik.sundell+2025@sensmetry.com>
Index entry project info and metadata are fetched with try_join!. When one leg returns a hard 404, the other future may be canceled before its HTTP request is dispatched. Keep exact assertions on the failing endpoint, but allow the sibling mock to be touched at most once and document why that count is not pinned exactly. Signed-off-by: Erik Sundell <erik.sundell+2025@sensmetry.com>
Co-authored-by: Andrius Pukšta <andrius.puksta@sensmetry.com> Signed-off-by: Erik Sundell <erik.i.sundell@gmail.com>
The PR added several Rust tests and helpers with test_ prefixes. Repository test naming avoids that prefix for newly added test cases. Rename the new cases and index-test helpers while leaving older pre-existing test_ names untouched. Signed-off-by: Erik Sundell <erik.sundell+2025@sensmetry.com>
Co-authored-by: Andrius Pukšta <andrius.puksta@sensmetry.com> Signed-off-by: Erik Sundell <erik.i.sundell@gmail.com>
Co-authored-by: Andrius Pukšta <andrius.puksta@sensmetry.com> Signed-off-by: Erik Sundell <erik.i.sundell@gmail.com>
Index `versions.json` entries are advertised in descending SemVer order. The solver still sorts candidates before choosing the highest allowed version, but the old pre-sort `.rev()` was inherited from ascending `versions.txt` ordering and now works against the advertised order. Signed-off-by: Erik Sundell <erik.sundell+2025@sensmetry.com>
Co-authored-by: Andrius Pukšta <andrius.puksta@sensmetry.com> Signed-off-by: Erik Sundell <erik.i.sundell@gmail.com>
Co-authored-by: Andrius Pukšta <andrius.puksta@sensmetry.com> Signed-off-by: Erik Sundell <erik.i.sundell@gmail.com>
andrius-puksta-sensmetry
approved these changes
May 6, 2026
Collaborator
andrius-puksta-sensmetry
left a comment
There was a problem hiding this comment.
Looking good.
consideRatio
added a commit
that referenced
this pull request
May 6, 2026
The client now reads remote indexes through the static-file protocol documented in `docs/src/index-protocol.md`, with the publish-side API scope split into `docs/src/index-api-protocol.md`. This breaks compatibility with the old HTTP registry layout and with `0.3` registry-shaped lockfiles. Users must regenerate lockfiles, and index servers must serve the new discovery, `index.json`, `versions.json`, and per-version file layout. Wire-format changes (reads): - `<discovery-root>/sysand-index-config.json` is fetched lazily on first index use to resolve `index_root` and `api_root`. A 404 defaults both roots to the discovery root; other non-2xx responses are hard errors. - `index_root` / `api_root` must be absolute HTTP(S) base URLs without userinfo. Relative URLs, credentials in URLs, and non-base paths are rejected. - `<index_root>/index.json` is now project enumeration. Each `projects` entry carries `iri` and optional `status`; omitted `status` is equivalent to `available`. - `index.json` project `status` supports `available` and `removed`. List-all operations filter removed projects, while direct resolution of a specific IRI still goes through that project's `versions.json`. - Per-project `versions.json` replaces the old `versions.txt` list. Each entry carries `version`, `usage`, `project_digest`, `kpar_size`, `kpar_digest`, and optional `status`. - `versions.json` entries are validated at ingest: SemVer only, no build metadata, descending precedence order, no duplicates, and lowercase `sha256:<64-hex>` digests. - `versions.json` status supports `available`, `yanked`, and `removed`. New resolution filters to available versions; direct reads of removed versions fail distinctly; yanked versions remain readable for pinned lockfiles. - Per-version layout becomes `<project>/<version>/.project.json`, `.meta.json`, and `project.kpar`. - IRI path mapping is `pkg:sysand/<publisher>/<name>` → `<publisher>/<name>/`; all other IRIs use `_iri/<sha256(normalized_iri)>/`. - Non-canonical `pkg:sysand/...` IRIs are hard-rejected through the new `purl` validation path, including `InterchangeProjectUsageRaw::validate`. - `project_digest` is verified before `.project.json` / `.meta.json` are exposed. `kpar_digest` and `kpar_size` are enforced during streamed archive download. Wire-format changes (publish): - `sysand publish --index` now treats the configured URL as the discovery root, performs mandatory discovery with the configured `HTTPAuthentication` policy, and posts to `<api_root>/v1/upload`. - Publish still supports direct API-root URLs because a missing discovery document defaults `api_root` to the configured URL. - Publish credentials are matched after discovery against the actual upload URL. Discovery can therefore be auth-gated while upload remains bearer-only. - Publish payload preparation now runs before network access, rejects SemVer build metadata, enforces the KPAR size limit, and sends archive-integrity metadata including the KPAR SHA-256 digest. Lock/sync format changes: - Lockfile format bumps from `0.3` to `0.4`; old registry-shaped lockfiles are rejected with a regenerate message. - `Source::Registry` is renamed to `Source::Index`. - New `Source::IndexKpar` records `index_kpar`, `index_kpar_size`, and `index_kpar_digest`, preserving the raw-archive integrity tripwire from `lock` to `sync`. - `sync` can now install index-backed KPARs directly from the lockfile and verifies the recorded size/digest without re-reading `versions.json`. - Lockfile validation now distinguishes canonical project digest format from index KPAR digest format and rejects uppercase index KPAR digests before canonicalization. Public Rust API: - New modules: `env::discovery`, `env::index`, `project::index_entry`, and `purl`. Removed: `env::reqwest_http::HTTPEnvironmentAsync`. - `IndexEnvironmentAsync` replaces the old HTTP registry environment and lazily resolves discovery endpoints. - `CombinedResolver` / `CombinedProjectStorage` terminology changes from `Registry` to `Index`. - `standard_index_resolver` and `standard_resolver` now return `Result` because discovery-root shape validation can fail. - `ProjectRead` / `ProjectReadAsync` wrappers must explicitly forward `get_info`, `get_meta`, `version`, `usage`, and `checksum_canonical_hex`; the derive macro now forwards these methods. - `CanonicalizationError::map_project_read` is added for wrapper error forwarding. - `ReqwestKparDownloadedProject` now accepts optional expected KPAR digest/size in `new` and `new_guess_root`, verifies them during download, and exposes `ensure_downloaded_verified` / `is_downloaded_and_verified`. Builder setters like `with_expected_sha256_hex` / `with_expected_size` were removed. - `ReqwestKparDownloadedError` gains `DigestMismatch` and `SizeMismatch`. - `InterchangeProjectValidationError::MalformedSysandPurl` is added. - `core/Cargo.toml` adds optional `idna` under networking and enables `tokio/sync`. Other updates: - The solver now uses `ProjectRead::version` and `ProjectRead::usage` summaries, allowing index candidates to be solved from `versions.json` without downloading archives. - The protocol docs now distinguish project persistence from project retirement, and version persistence from version retirement. - `.meta.json` `created` serialization is centralized and now emits second precision with a `Z` suffix. - Java/Python bindings handle the resolver's new fallible construction; Java enables Tokio drivers; Python test setup isolates `uv` from outer conda environments. - Tests cover discovery, IRI normalization, index read validation, digest/size drift, index project status filtering, yanked/removed behavior, publish discovery, and lock/sync round-trips for index-backed projects. Co-authored-by: Andrius Pukšta <andrius.puksta@sensmetry.com> Co-authored-by: Jonas Pukšta <146448971+Jonas-Puksta-Sensmetry@users.noreply.github.com> Signed-off-by: Erik Sundell <erik.sundell+2025@sensmetry.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The client now reads remote indexes through the static-file protocol
documented in
docs/src/index-protocol.md, with the publish-side APIscope split into
docs/src/index-api-protocol.md.This breaks compatibility with the old HTTP registry layout and with
0.3registry-shaped lockfiles. Users must regenerate lockfiles, andindex servers must serve the new discovery,
index.json,versions.json,and per-version file layout.
Wire-format changes (reads):
<discovery-root>/sysand-index-config.jsonis fetched lazily on firstindex use to resolve
index_rootandapi_root. A 404 defaults bothroots to the discovery root; other non-2xx responses are hard errors.
index_root/api_rootmust be absolute HTTP(S) base URLs withoutuserinfo. Relative URLs, credentials in URLs, and non-base paths are
rejected.
<index_root>/index.jsonis now project enumeration. Eachprojectsentry carries
iriand optionalstatus; omittedstatusisequivalent to
available.index.jsonprojectstatussupportsavailableandremoved.List-all operations filter removed projects, while direct resolution of
a specific IRI still goes through that project's
versions.json.versions.jsonreplaces the oldversions.txtlist. Eachentry carries
version,usage,project_digest,kpar_size,kpar_digest, and optionalstatus.versions.jsonentries are validated at ingest: SemVer only, no buildmetadata, descending precedence order, no duplicates, and lowercase
sha256:<64-hex>digests.versions.jsonstatus supportsavailable,yanked, andremoved.New resolution filters to available versions; direct reads of removed
versions fail distinctly; yanked versions remain readable for pinned
lockfiles.
<project>/<version>/.project.json,.meta.json, andproject.kpar.pkg:sysand/<publisher>/<name>→<publisher>/<name>/; all other IRIs use_iri/<sha256(normalized_iri)>/.pkg:sysand/...IRIs are hard-rejected through the newpurlvalidation path, includingInterchangeProjectUsageRaw::validate.project_digestis verified before.project.json/.meta.jsonareexposed.
kpar_digestandkpar_sizeare enforced during streamedarchive download.
Wire-format changes (publish):
sysand publish --indexnow treats the configured URL as the discoveryroot, performs mandatory discovery with the configured
HTTPAuthenticationpolicy, and posts to<api_root>/v1/upload.document defaults
api_rootto the configured URL.upload URL. Discovery can therefore be auth-gated while upload remains
bearer-only.
SemVer build metadata, enforces the KPAR size limit, and sends
archive-integrity metadata including the KPAR SHA-256 digest.
Lock/sync format changes:
0.3to0.4; old registry-shapedlockfiles are rejected with a regenerate message.
Source::Registryis renamed toSource::Index.Source::IndexKparrecordsindex_kpar,index_kpar_size, andindex_kpar_digest, preserving the raw-archive integrity tripwire fromlocktosync.synccan now install index-backed KPARs directly from the lockfile andverifies the recorded size/digest without re-reading
versions.json.from index KPAR digest format and rejects uppercase index KPAR digests
before canonicalization.
Public Rust API:
env::discovery,env::index,project::index_entry,and
purl. Removed:env::reqwest_http::HTTPEnvironmentAsync.IndexEnvironmentAsyncreplaces the old HTTP registry environment andlazily resolves discovery endpoints.
CombinedResolver/CombinedProjectStorageterminology changes fromRegistrytoIndex.standard_index_resolverandstandard_resolvernow returnResultbecause discovery-root shape validation can fail.
ProjectRead/ProjectReadAsyncwrappers must explicitly forwardget_info,get_meta,version,usage, andchecksum_canonical_hex; the derive macro now forwards these methods.CanonicalizationError::map_project_readis added for wrapper errorforwarding.
ReqwestKparDownloadedProjectnow accepts optional expected KPARdigest/size in
newandnew_guess_root, verifies them duringdownload, and exposes
ensure_downloaded_verified/is_downloaded_and_verified. Builder setters likewith_expected_sha256_hex/with_expected_sizewere removed.ReqwestKparDownloadedErrorgainsDigestMismatchandSizeMismatch.InterchangeProjectValidationError::MalformedSysandPurlis added.core/Cargo.tomladds optionalidnaunder networking and enablestokio/sync.Other updates:
ProjectRead::versionandProjectRead::usagesummaries, allowing index candidates to be solved from
versions.jsonwithout downloading archives.
retirement, and version persistence from version retirement.
.meta.jsoncreatedserialization is centralized and now emitssecond precision with a
Zsuffix.Java enables Tokio drivers; Python test setup isolates
uvfrom outerconda environments.
digest/size drift, index project status filtering, yanked/removed
behavior, publish discovery, and lock/sync round-trips for index-backed
projects.