Files
installer/bundles/README.md
wtclaude fd59a74912
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 5s
ci(bundle): recognize a linux-aarch64 link asset
Step 1 of PLAN.md §5.2's four, and it has to be first. Two rules in this
job are strict in opposite directions: an unrecognized link asset name
fails the run, and a missing REQUIRED platform key fails it too. So the
name must be taught before the release that carries it, and the key can
only be required after one exists -- requiring it first would fail every
bundle for as long as the gap lasts.

This is therefore the mapping only. linux-aarch64 is not in REQUIRED
yet; step 3 promotes it once a link release actually ships the binary,
after which a dropped target reddens CI instead of vanishing silently
from every bundle.

The compose step needed no change: it builds the asset map from the
platform TSV, so a third key costs it nothing.

Edited on `main` and deliberately not on `edge`. The compose job runs
from `main`, and leaving `edge`'s copy untouched means the eventual
cutover merge has nothing to conflict over.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-05 05:22:59 -05:00

95 lines
4.2 KiB
Markdown

# Bundles
**These files are generated. Do not edit them by hand.**
A *bundle* names one exact, protocol-checked combination of the two components the installer
deploys — a `uo-link` sidecar release and a `servuo-plugins` overlay release. The installer does not
hardcode versions and does not resolve "latest" at run time; it fetches one of these documents and
installs what it names. **The bundle is the compat matrix.**
They are written by [`.gitea/workflows/bundle.yml`](../.gitea/workflows/bundle.yml), which composes
one whenever a component publishes a release (dispatched by that release's own workflow) and
nightly, so a missed dispatch self-heals. A run that finds nothing changed writes nothing.
See `docs/installer/PLAN.md` §7 for the design.
## Layout
| File | What it is |
|---|---|
| `current.json` | The bundle the installer uses by default. Always a copy of the newest `bundle-*.json`. |
| `bundle-<tag>.json` | Every bundle ever published, kept forever so `--bundle <tag>` stays reproducible. |
Tags are UTC dates — `2026.08.04`. A second bundle on the same day (a sidecar release in the
morning, an overlay release in the afternoon) becomes `2026.08.04.2`, so one tag always names
exactly one matrix.
## How the installer fetches these
Plain anonymous `GET`s against a public repo. The shard host gets no git and no Gitea credentials
(`PLAN.md` §1), so nothing here may require auth:
```
https://gitea.whitlocktech.com/RunicGateway/installer/raw/branch/main/bundles/current.json
https://gitea.whitlocktech.com/RunicGateway/installer/raw/branch/main/bundles/bundle-2026.08.04.json
```
Bundles are committed rather than published as Gitea releases because this repo's *own* releases are
the installer binaries, and `/releases/latest` returns whichever release is newest regardless of
kind — interleaving the two would make "latest" intermittently resolve to a release containing no
installer binary.
## Schema
`schema` is the version of *this document's* shape, and is unrelated to `protocol` (the uo-link wire
protocol) or to either component's release version. All three move independently.
```jsonc
{
"schema": 1,
"bundle": "2026.08.04", // this bundle's tag; what --bundle takes
"generated": "2026-08-04T16:07:13Z",
"protocol": 3, // the wire protocol both halves speak (gate 1 proved it)
"link": {
"repo": "RunicGateway/link",
"tag": "v1.1.0",
"version": "1.1.0",
"protocol": 3,
"assets": { // per-platform: the installer runs on each
"linux-x86_64": { "name": "…", "url": "…", "sha256": "…" },
"windows-x86_64": { "name": "…", "url": "…", "sha256": "…" }
// "linux-aarch64" joins these from link's first release built for it;
// bundle.yml already knows the name (PLAN.md §5.2)
}
},
"overlay": {
"repo": "RunicGateway/servuo-plugins",
"tag": "v0.1.1",
"version": "0.1.1",
"commit": "3a52abb…", // recorded into install.json at deploy time
"protocol": 3,
"servuo": {
"min_version": "57.4", // base overlay: only adds files
"patches_verified_against": "57.4" // patch tier: skipped with a warning elsewhere
},
"asset": { "name": "runicgateway-overlay-0.1.1.tar.gz", "url": "…", "sha256": "…" }
}
}
```
### The two things worth knowing
**`sha256` is load-bearing, not decorative.** Every artifact Runic Gateway publishes is deliberately
**unsigned** (`PLAN.md` §3) — the checksum is the entire trust anchor. Each hash here was computed by
CI from the asset it actually downloaded, *after* verifying it against the `SHA256SUMS` the
publishing repo shipped beside it. The installer must verify every download against these values and
refuse on a mismatch. A bundle whose hashes are trusted but never checked buys nothing.
**`link.protocol` and `overlay.protocol` are always equal, and that is the point.** The sidecar
rejects a protocol mismatch with `409` rather than mis-parsing, so a mismatched pair is a shard
emitting into a void. CI refuses to publish one: it reads `PROTOCOL_VERSION` from the sidecar's
source at its release tag and the declared `protocol` from the overlay tarball's `manifest.json`, and
fails if they differ. The top-level `protocol` is that agreed value.