The repositories moved from ludovic111 to the horizon-rust organization.
GitHub redirects repository URLs but not GitHub Pages, so the mod index
moved to https://horizon-rust.github.io/horizon-mods/: the launcher and
the game read it there. The launcher still accepts icons and files from
the old addresses, which a mirror keeps serving for launchers released
before the move.
Release notes thanked ludovic111 once the repository's owner became the
organization (verify failed on main): the maintainer is now named.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The registry's publish job (ludovic111/horizon-mods#7) writes each
project's `downloads` into index.json, summed from GitHub's counts of its
release assets every hour. `index::Project` reads it (absent: 0, never
written when 0, so older readers and the schema are unchanged).
The launcher's Horizon results show "12.3k downloads" like Modrinth's, and
list the most downloaded first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every function the game offers mods has the API level it arrived at
(crates/horizon-mods/src/api_level.rs): level 0 is the contract of
Horizon 0.2.67 and 0.2.68, level 1 adds render.set-minimap. A mod needs
the level of the newest function it imports, read from its component.
- The game refuses a mod that calls a function it doesn't have with
"made for a newer Horizon (it uses ...): update Horizon to run it"
instead of wasmtime's linking error.
- The registry index lists each mod version's level (api_level, read
from the file by CI); check_file compares it when the entry has one,
and the moved extras' installer takes the newest version this game
can run.
- cargo horizon-mod check prints the level.
- The api_reference test fails until a new WIT function has a level.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Mods page gets a fourth kind, Shader packs, which searches Horizon's
registry and installs a `.hshader` into `shaderpacks/` once
`index::check_file` has run the game's own `horizon_shader_api::check`.
Modrinth and CurseForge answer that tab with a pointer to the registry:
their shader packs are the shaders/ format and are not offered.
The sample index lists a shader pack (`glow`, made with
`horizon-shader new` and `pack`), so the registry build, check_file and
the launcher's install are tested on a real file. A file of the same
name the registry didn't put in place is no longer called a mod.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The `shader` kind's check (index::inspect) reads the .hshader and runs
horizon_shader_api::check, the same check the game runs before it loads a
pack, so the registry's CI (`cargo horizon-mod check` / `registry build`)
refuses a pack the game would refuse, with each problem at its file and line.
Its facts are the manifest's id, version and shader API line. The kind's
files are .hshader, `cargo horizon-mod check` recognises them, and
registry::open takes shader submissions. Installing one from the launcher
stays refused (check_file) until the launcher installs shader packs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/mods.md gets "Publish your mod" (the form, the review, updates) and
its Distribution section points at the registry instead of the plan for
it. docs/api/mod-index.md says how horizon-mods runs: its checks, merging,
Pages, the weekly cleanup, and that removing an entry needs an admin merge
(takedowns go in revoked.toml). The service's README says where it runs on
Railway.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its own workspace, as the telemetry server's, for its own Railway service.
GitHub sign-in with no scope (the user's token reads the login once and is
dropped), signed-cookie sessions, an origin check and a CSRF token, six
submissions per account and sixty in all per hour. A submission runs the
file's own checks (horizon_mods::index::inspect, the signature, the entry's
fields through registry::project), uploads it to the registry's `staging`
release as <sha256>.<extension>, writes mods/<id>.toml on a new branch with
the submitter as co-author, and opens the pull request. Only an entry's
owners publish updates; a key change is a pull request by hand. Shader packs
are refused until their format exists.
Tested end to end against a mock GitHub: sign-in, the signed sample mod,
what lands on GitHub, and what is refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `horizon_mods::index::registry`: the registry repository's
`mods/<id>.toml` and `revoked.toml`, `build` (every entry and file
checked, every problem listed, the index written) and `review` (a
published file never changes or disappears, a kind never changes;
non-owner, owner and key changes pointed out).
- `cargo horizon-mod registry build|review|check|list` for the registry's CI.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
horizon_mods::index (feature index): index.json's types and rules (unique
ids, https URLs, plain file names, hex SHA-256, minisign keys and
signatures, a modpack's mods present in the index), open-ended kinds
(mod, modpack, resourcepack, shader; unknown kinds kept, not offered),
revocations, and check_file: size, hash, revocation, signature and the
file's own manifest against its entry. horizon_mods::modpack reads
.hpack files: horizon-modpack.toml and an allow-list of overrides.
docs/api/mod-index.md documents both, with a sample index the tests read.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>