# Manifestes de mise à jour du client BG LANDS

Ce sous-projet fabrique le petit fichier que consommera le launcher Java BG LANDS pour savoir
quelle version du mod installer. Il **ne télécharge, ne modifie et ne redistribue ni Minecraft ni
le launcher Mojang**. Il ne déploie rien sur le réseau Velocity/Paper/Geyser.

Le flux est volontairement séparé du serveur :

```text
build du mod Java -> release.config.json -> générateur -> release.json signé
                                                          |
launcher Java <- HTTPS + clé publique épinglée ------------+
```

Bedrock ne consomme pas ce manifeste. Les joueurs Bedrock restent servis par Geyser/Floodgate et
le `.mcpack` Bedrock du réseau.

## Contenu

- `schema/release-source.schema.json` : entrée CI versionnée ;
- `schema/release-manifest.schema.json` : contrat public du launcher ;
- `src/generate.ts` : empreintes SHA-256, taille, schémas hébergés et signature Ed25519 optionnelle ;
- `src/bundle.ts` : assemblage stable atomique, clé publique dérivée et validation de l'allowlist Velocity ;
- `src/verify.ts` : validation du manifeste, de sa signature et, en option, d'une instance locale ;
- `src/install.ts` : installation atomique depuis un cache déjà téléchargé et retour arrière ;
- `examples/release.config.example.json` : exemple à copier lorsque le vrai mod existe.

Les outils utilisent TypeScript strict et `tsx`. Installez les dépendances verrouillées avec
`npm ci` avant de lancer les commandes.

## Générer une release

1. Pour une release locale, copier l'exemple vers un fichier de travail ignoré par Git. La release
   stable du réseau utilise le contrat versionné `release.stable.json`.
2. Remplacer la version Minecraft, le loader, les identifiants CurseForge et `localFile`.
3. Construire le JAR du mod avant de générer le manifeste.
4. Épingler dans chaque `sourceIntegrity` le SHA-256 et la taille du fichier source attendu. Le
   générateur refuse de signer un build différent, même si son nom est identique.

```bash
cd /home/ubuntu/bglands-java-network/launcher-manifest
npm run generate -- \
  --config ./release.config.json \
  --out ./dist/stable/release.json
```

La commande écrit atomiquement :

- `dist/stable/release.json`, sans chemin local de build ;
- les deux schémas publics `release-manifest.schema.json` et `release-source.schema.json` ;
- `dist/stable/checksums.sha256`, qui couvre automatiquement les artefacts **et les deux schémas**.

`generate` prépare les métadonnées, mais la publication de production doit passer par `bundle` ou
`make client-release` : ce chemin copie les artefacts, interdit tout fichier parasite et vérifie que
l'allowlist correspond exactement au contenu qui sera servi par Velocity.

Une release stable de production doit être signée. Pour le réseau, la commande unique est :

```bash
cd /home/ubuntu/bglands-java-network
make client-release
```

Cette cible reconstruit le mod, exécute les tests TypeScript, assemble dans un dossier temporaire,
démarre depuis le JAR fraîchement produit sous `java-client-fabric/build/libs`, prépare Fabric API
depuis un cache validé (ou Maven Fabric officiel en HTTPS avec SHA-256/taille épinglés),
dérive `release-public.pem` depuis `.secrets/java-client-release-private.pem`, signe avec Ed25519,
fixe `$schema` à l'URL HTTPS publique, vérifie chaque SHA-256 puis remplace atomiquement
`dist/client-release`. La clé privée n'est jamais copiée dans le bundle, le contexte Docker ou les
logs. La clé dérivée doit correspondre à la clé de confiance versionnée
`trust/bglands-release-2026-01.pem`; une mauvaise clé privée est donc refusée avant signature.
`make release-verify` peut être relancé sans signer ni modifier le bundle et vérifie avec cette clé
de confiance, jamais avec la seule clé livrée par le bundle.

Pour une utilisation générique du sous-projet, l'équivalent détaillé reste :

```bash
openssl genpkey -algorithm Ed25519 -out release-private.pem
openssl pkey -in release-private.pem -pubout -out release-public.pem

npm run generate -- \
  --config ./release.config.json \
  --out ./dist/stable/release.json \
  --private-key ./release-private.pem \
  --key-id release-2026-01

npm run verify -- \
  --manifest ./dist/stable/release.json \
  --public-key ./release-public.pem \
  --require-signature
```

La clé privée ne doit jamais entrer dans le dépôt, l'image Docker, le launcher ou le serveur de
téléchargement. Elle doit vivre dans le coffre de secrets de la CI. La ou les clés publiques sont
épinglées dans le launcher. Une rotation ajoute une nouvelle clé publique au launcher avant de
signer avec elle.

Dans le dépôt réseau, le fichier doit rester en mode `0600` ou `0400`. Le bundler refuse une clé
accessible au groupe/autres, une clé non Ed25519, une release non `stable`, un symlink, un artefact
non conforme à `sourceIntegrity`, un schéma absent et tout fichier supplémentaire.

La rotation crée d'abord un nouveau fichier public sous `trust/` et le distribue dans le launcher,
puis change ensemble `CLIENT_RELEASE_KEY_ID` et `CLIENT_RELEASE_TRUSTED_PUBLIC_KEY`. Ne jamais
remplacer silencieusement la clé publique existante pour un `keyId` déjà publié.

## Contrat attendu du launcher

Au démarrage, l'updater Java doit :

1. télécharger `release.json` exclusivement en HTTPS ;
2. rejeter une `schemaVersion` inconnue ;
3. canonicaliser `payload` selon `bglands-canonical-json-v1` ;
4. vérifier `integrity.payloadSha256`, puis la signature Ed25519 avec la clé publique correspondant
   à `keyId` ;
5. vérifier la version du launcher, de Minecraft, de Java, du loader et `serverProtocol` ;
6. refuser tout `installPath` en dehors de `mods/`, `config/` ou `resourcepacks/` ;
7. télécharger chaque fichier dans un temporaire de l'instance dédiée ;
8. imposer la taille annoncée et le SHA-256 **avant** toute installation ;
9. sauvegarder l'ancienne version, faire un remplacement atomique, puis restaurer la sauvegarde si
   le lancement échoue ;
10. ne supprimer que les fichiers explicitement possédés par BG LANDS, jamais les mods personnels
    d'une autre instance.

Pour vérifier une instance déjà préparée :

```bash
npm run verify -- \
  --manifest ./dist/stable/release.json \
  --public-key ./release-public.pem \
  --require-signature \
  --artifact-root /chemin/vers/instance-bglands
```

`verify --artifact-root` reste strictement en lecture seule ; seule la commande `apply-release`
ci-dessous installe une release.

## Appliquer une release dans l'instance Java

Le launcher ou l'intégration CurseForge place d'abord chaque fichier annoncé dans un cache dédié,
sous son `fileName`. `apply-release` vérifie ensuite obligatoirement la signature, la combinaison
Minecraft/Java/loader/protocole et tous les SHA-256 avant la première écriture :

```bash
npm run apply-release -- \
  --manifest ./dist/stable/release.json \
  --public-key ./release-public.pem \
  --artifact-cache /chemin/vers/cache-bglands \
  --instance /chemin/vers/instance-bglands \
  --launcher-version 1.0.0 \
  --minecraft 26.2 \
  --java-major 25 \
  --loader fabric \
  --loader-version 0.19.3 \
  --server-protocol 1
```

L'installation ne touche qu'aux chemins déclarés sous `mods/`, `config/` et `resourcepacks/`.
Chaque fichier est copié dans un temporaire, revérifié, puis renommé atomiquement. L'ancienne
version est conservée dans une sauvegarde transactionnelle unique sous
`.bglands/backups/<nouvelle-release>/transaction-*/`; un échec restaure les fichiers précédents.
Ce nom unique permet aussi de réparer plusieurs fois une release corrompue sans écraser ses
sauvegardes antérieures. L'état final est écrit dans `.bglands/installed-release.json`. Une seconde
application de la même release valide ne modifie rien.

Lors d'un passage N → N+1, l'updater relit cet état avec un schéma strict. Un chemin auparavant
géré mais absent du nouveau manifeste n'est retiré que s'il reste un fichier régulier, sans lien
symbolique, et que sa taille et son SHA-256 correspondent encore à l'état enregistré. Il est alors
déplacé dans la même sauvegarde `transaction-*`; un fichier modifié ou non répertorié est laissé en
place et bloque le retrait automatique. Les mods et configurations personnels ne sont jamais
parcourus ni supprimés par motif générique.

Le téléchargement reste volontairement sous la responsabilité du launcher ou de CurseForge :
cela évite de contourner leur API et sépare l'accès réseau de la transaction d'installation.

## CurseForge

Deux modes sont prévus par le schéma :

- `distribution.type = "curseforge"` : le launcher/pack autorisé résout `projectId` et `fileId`
  via le mécanisme CurseForge officiel. Le générateur a tout de même besoin du JAR local pour
  enregistrer son empreinte exacte ;
- `distribution.type = "direct"` : le JAR est publié par BG LANDS sur une URL HTTPS immuable.

Pour CurseForge, publier de préférence un modpack/profil qui installe le loader, le mod BG LANDS
et ses dépendances. Un launcher personnalisé peut ensuite sélectionner ce profil et vérifier le
mod via ce manifeste. Il ne doit pas contourner l'API, les licences ou les règles de distribution
des dépendances CurseForge.

Ne réutilisez jamais le même `fileId` ou la même URL pour un contenu différent. Chaque release doit
être immuable ; une correction produit un nouveau `releaseId` et une nouvelle version.

## Canaux et compatibilité

- `stable` : joueurs ; signature obligatoire ;
- `beta` : testeurs volontaires ; signature obligatoire ;
- `canary` : CI/test interne ; signature fortement recommandée.

`compatibility.serverProtocol` est le contrat entre le mod client et le plugin Paper. Le serveur
peut accepter plusieurs protocoles pendant une migration, mais le launcher ne doit jamais lancer
une combinaison déclarée incompatible.

## Tests

```bash
cd /home/ubuntu/bglands-java-network/launcher-manifest
npm ci
npm test
npm run typecheck
```

Les tests couvrent la canonicalisation, les hashes d'artefacts, l'intégrité source épinglée, la
signature Ed25519, les deux schémas obligatoires, la reproductibilité du bundle, la détection des
fichiers parasites, l'installation idempotente avec sauvegarde et le refus des URL HTTP/chemins
traversants ou versions incompatibles.
