docs: design the update manager + manifest generator #3

Merged
jakub merged 7 commits from jann/m2dev-client:claude/update-manager into main 2026-04-14 11:54:43 +02:00
Member

Closes #2.

Summary

Design for the Metin2 update manager, plus a working prototype manifest generator. No launcher in this PR — that comes in the next one once the design is reviewed.

  • docs/update-manager.md — full architecture: single-entry launcher, content-addressed Caddy-served blobs, Ed25519-signed JSON manifest, rename-before-replace self-update, offline fallback, failure-mode table, ~10-day MVP plan.
  • docs/update-manifest.md — formal JSON schema with canonical ordering rules so signatures stay stable.
  • docs/examples/manifest-example.json — small synthetic example so reviewers can see the shape without wading through a real 54k-file manifest.
  • scripts/make-manifest.py — sha256-hashes a client directory, emits canonical JSON. Excludes runtime artifacts (.log, .dxvk-cache, .pdb, .old, etc.). Launcher is broken out as a top-level field, not a file entry.

Verification so far

  • Ran make-manifest.py against our wine-client folder: 54443 files, 3.3 GiB hashed end-to-end in a single pass.
  • Canonical JSON output matches the rules in update-manifest.md (2-space indent, LF endings, sorted files array, stable key order).
  • Excludes pulled out .log and .dxvk-cache noise after first run.
  • End-to-end signing + verify (separate signer script is not in this PR)
  • Launcher consuming the manifest (next PR)

Review focus

  • Are the four baked decisions (A/B/C/D in the issue) the right ones?
  • Is the content-addressed server layout OK, or would you rather have path-based storage?
  • Implementation plan: does the ~10-day MVP scope feel realistic?
  • Any failure modes I missed in the table at the bottom of update-manager.md?

Notes

This is doc-heavy on purpose. The launcher code is the next PR and I want alignment on the design before writing it, so we don't throw away C# that doesn't fit the flow.

Closes #2. ## Summary Design for the Metin2 update manager, plus a working prototype manifest generator. No launcher in this PR — that comes in the next one once the design is reviewed. - `docs/update-manager.md` — full architecture: single-entry launcher, content-addressed Caddy-served blobs, Ed25519-signed JSON manifest, rename-before-replace self-update, offline fallback, failure-mode table, ~10-day MVP plan. - `docs/update-manifest.md` — formal JSON schema with canonical ordering rules so signatures stay stable. - `docs/examples/manifest-example.json` — small synthetic example so reviewers can see the shape without wading through a real 54k-file manifest. - `scripts/make-manifest.py` — sha256-hashes a client directory, emits canonical JSON. Excludes runtime artifacts (`.log`, `.dxvk-cache`, `.pdb`, `.old`, etc.). Launcher is broken out as a top-level field, not a file entry. ## Verification so far - [x] Ran `make-manifest.py` against our wine-client folder: 54443 files, 3.3 GiB hashed end-to-end in a single pass. - [x] Canonical JSON output matches the rules in `update-manifest.md` (2-space indent, LF endings, sorted files array, stable key order). - [x] Excludes pulled out `.log` and `.dxvk-cache` noise after first run. - [ ] End-to-end signing + verify (separate signer script is not in this PR) - [ ] Launcher consuming the manifest (next PR) ## Review focus - Are the four baked decisions (A/B/C/D in the issue) the right ones? - Is the content-addressed server layout OK, or would you rather have path-based storage? - Implementation plan: does the ~10-day MVP scope feel realistic? - Any failure modes I missed in the table at the bottom of `update-manager.md`? ## Notes This is doc-heavy on purpose. The launcher code is the next PR and I want alignment on the design before writing it, so we don't throw away C# that doesn't fit the flow.
jann added 3 commits 2026-04-14 10:37:14 +02:00
Design for a content-addressed, signed manifest-based update system for
the Metin2 client. Launcher is a single entry point; server is static
files behind Caddy at updates.jakubkadlec.dev; manifests are signed with
Ed25519. Publishing starts manual in v1 and moves to Gitea Actions in v2.
Formal JSON schema for the release manifest, with canonical ordering
rules so signatures stay stable. Includes a small synthetic example
under docs/examples/.
Walks a client directory, sha256-hashes every file, emits a canonical
JSON manifest matching docs/update-manifest.md. Excludes runtime
artifacts (.log, .dxvk-cache, .pdb, .old) and the launcher is broken
out as a top-level field rather than an entry in files[]. Does not
sign; pair with a separate signer step.
jann added 1 commit 2026-04-14 10:44:56 +02:00
After a survey of existing Metin2 launchers, general-purpose
auto-updaters, and adjacent open-source game launchers, update the
design to:

- drop the hand-rolled rename-before-replace self-update path
- use Velopack for launcher self-update (MIT, modern successor to
  Squirrel.Windows, handles atomic replace, delta, Authenticode, AV
  friendliness out of the box)
- keep the custom asset patcher for the 4 GB game payload, which
  Velopack is not designed for
- reference runelite/launcher as the architectural template
- name Sparkle 2 and wowemulation-dev/wow-patcher as Ed25519 prior art

No Metin2 community launcher is worth forking; the ceiling of
published prior art is 'file list + sha256 + HTTP GET' and this design
is already above it. Greenfield confirmed.
jann added 2 commits 2026-04-14 11:22:37 +02:00
Companion to make-manifest.py that signs the output with an Ed25519
private key. Signs the literal manifest bytes — never re-serializes —
because the launcher verifies against exactly what the server delivers.
Warns if the private key file is readable beyond owner. Verified
end-to-end against the launcher's real public key and a tamper test.
Caddy site block for the update CDN. Serves the signed manifest with
short TTL, content-addressed blobs as immutable, historical manifests
as immutable, and the Velopack launcher feed alongside. Caching rules
are calibrated so a new release is visible within a minute without
hammering the origin on thundering herds.
jann added 1 commit 2026-04-14 11:35:43 +02:00
Step-by-step operator runbook for turning on updates.jakubkadlec.dev:
create the webroot, append the site block, validate the Caddyfile
before reload, watch for Let's Encrypt cert issuance, verify from an
external client, plus explicit rollback for every mutating step and a
catastrophic-recovery section in case Caddy drops all sites. Targeted
at Jakub (VPS operator) so Claude does not touch the running service.
jakub merged commit 1dba3e3c91 into main 2026-04-14 11:54:43 +02:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: metin-server/m2dev-client#3