A registry record is a name and a catalog
`config` — added this morning in 2.1.0 — is gone, and `storeRepositoryUrl` with it, along with the two store repositories they pointed at. 2.1.0 had the registry say how each store behaves. Wrong shape: how a store behaves is fixed per installed client, and this application is the only thing that can see the machine it runs on. A copy of that on a server was a second authority over decisions this side had already made correctly — including which directories the store may delete from — and two authorities are a way to disagree. Keeping two stores on one machine apart needs none of it. It is a subfolder, derived here: the store id is a slug of the catalog host, the home is `<id>-desktop`, the games folder is `<id>`, and that folder is the only subtree the store will ever delete from. Derived from the *catalog* on purpose — the catalog is what a store is, so two records naming the same one are the same store and land in the same place, which makes installing twice idempotent instead of a way to orphan what is already there. Existing installations keep their identity: a store home is recognised by its own `config.json`, so one installed as `ttg` stays `ttg` in `ttg-desktop` with its games where they are. Only a new install derives its id. `StoreProvisioningService` no longer re-reads the registry before installing. That existed to keep the renderer from supplying a config, and with no config in the record there is nothing left to protect: a name and a catalog have no paths in them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+46
-33
@@ -1,29 +1,41 @@
|
||||
# WarpEngine Client 2.1.0
|
||||
# WarpEngine Client 2.2.0
|
||||
|
||||
**A store's configuration now comes from the registry itself.** `GET /api/stores`
|
||||
records carry a `config` field — the same thing a store's `config.json` held, moved into
|
||||
the record that already says what the store is. The client applies it directly, so
|
||||
installing a store no longer depends on a second repository existing and staying
|
||||
reachable, and a store can be configured entirely from the site's admin.
|
||||
**A registry record is a name and a catalog.** Nothing else. The `config` field added in
|
||||
2.1.0 is gone, and so is `storeRepositoryUrl` — along with the two store repositories they
|
||||
pointed at, which have been deleted.
|
||||
|
||||
The old path still works. Where a record has no `config` but names a repository, the
|
||||
client reads `…/raw/branch/master/config.json` from it exactly as before, so a registry
|
||||
whose stores have not moved over is unaffected. With neither, the engine's defaults
|
||||
carry the store, as they always have. The order is: registry config, then repository
|
||||
file, then defaults.
|
||||
2.1.0 had the registry say how each store behaves. That was the wrong shape. How a store
|
||||
behaves is fixed per installed client: this application carries its own store engine and
|
||||
is the only thing that can see the machine it is running on. A copy of that on a server
|
||||
was a second authority over decisions this side had already made correctly — including
|
||||
which directories the store is allowed to delete from — and a second authority is a way
|
||||
for the two to disagree.
|
||||
|
||||
**The window cannot supply a store config.** It is handed stores to show and hands one
|
||||
back to install, but only as an identity — the config is dropped on the way in, and the
|
||||
main process reads the record again from the registry before installing anything. This
|
||||
is not tidiness: a config decides where files are written and, through
|
||||
`paths.subfolder`, which subtree the store may later delete from. That is not a decision
|
||||
the renderer gets to make, for the same reason a `GameDto` carries no paths.
|
||||
Keeping two stores on one machine out of each other's files needs none of it. It is a
|
||||
subfolder, and the client derives it:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **store id** | a slug of the catalog host — `teletypegames.org` becomes `teletypegames` |
|
||||
| **store home** | `<id>-desktop`, under the usual store root |
|
||||
| **games folder** | `<id>`, inside the OS's usual place for programs |
|
||||
| **delete boundary** | that same folder, and nothing outside it |
|
||||
|
||||
Derived from the *catalog* on purpose: the catalog is what a store is, so two records
|
||||
naming the same one are the same store and land in the same place. Installing the same
|
||||
store twice is idempotent rather than a way to orphan what is already there.
|
||||
|
||||
**Existing installations are untouched.** A store already on the machine is recognised by
|
||||
the `config.json` in its own home and keeps the identity written there — a store installed
|
||||
as `ttg` stays `ttg`, in `ttg-desktop`, with its games where they are. Only a *new* install
|
||||
derives its id from the catalog.
|
||||
|
||||
### Also
|
||||
|
||||
The headless check now reports *which* source configured a store — registry, repository
|
||||
or defaults — and names the resulting prune boundary, because "it installed" and "it
|
||||
installed where I meant" are different claims.
|
||||
The public `/stores` page loses its desktop card, which advertised a `curl … | sh` for a
|
||||
repository that no longer exists; an ordinary computer is served by the app. The `/desktop`
|
||||
URL now lands on the app rather than on a device tab, so what someone typing it wants is
|
||||
what they get.
|
||||
|
||||
### Opening it on macOS
|
||||
|
||||
@@ -40,19 +52,20 @@ Windows (installer, portable) packages the pipeline builds when the tag is pushe
|
||||
|
||||
### Verified
|
||||
|
||||
`make check` is clean: typecheck, lint, the headless smoke test and the window
|
||||
self-test.
|
||||
`make check` is clean: typecheck, lint, the headless smoke test and the window self-test.
|
||||
|
||||
All three configuration sources were exercised end to end against the real serializer
|
||||
output, with the client's own classes and a sandbox store root:
|
||||
The install path was measured end to end against a local registry serving exactly the new
|
||||
record shape. The slug came out `teletypegames`, the home `teletypegames-desktop`, the
|
||||
games subfolder `teletypegames`, and installing the same record twice landed in the same
|
||||
home. A record carrying `config` and `storeRepositoryUrl` — what a stale registry or a
|
||||
tampering renderer might still send — changed nothing on disk, because neither field exists
|
||||
in the model any more.
|
||||
|
||||
| Registry record | What configured the store | Prune boundary written |
|
||||
|---|---|---|
|
||||
| `config` present | the registry, with no extra request | `teletypegames` |
|
||||
| `config: null`, repository given | the repository's `config.json` | `teletypegames` |
|
||||
| neither | the engine's defaults | `teletypegames` |
|
||||
On this machine the check also shows both halves of the compatibility claim at once: the
|
||||
registry's new slug reads `teletypegames`, while the store that was installed before today
|
||||
is still found as `ttg` in `ttg-desktop`, listing its ten installed titles from
|
||||
`Application Support/teletypegames`.
|
||||
|
||||
The renderer barrier was tested adversarially rather than assumed: an install started
|
||||
from a record carrying `paths.subfolder: "ATTACKER"` and `paths.install_root:
|
||||
"/tmp/pwned"` wrote `subfolder: teletypegames` and `install_root: null` to disk, because
|
||||
the service re-read the registry and the mapper had already dropped the config.
|
||||
The site side was migrated and its specs re-run, and the frontend was built — which first
|
||||
required removing a dead `engines` list that had been failing `vue-tsc` on master, so that
|
||||
page could not be built to check at all.
|
||||
|
||||
Reference in New Issue
Block a user