• v2.5.1 31da869800

    WarpEngine Client 2.5.1
    ci/woodpecker/push/woodpecker Pipeline was successful
    ci/woodpecker/tag/woodpecker Pipeline was successful
    Stable

    mr.zero released this 2026-08-19 16:14:28 +00:00 | 0 commits to master since this release

    WarpEngine Client 2.5.1

    The app has an icon. Until now every build shipped the default Electron one —
    electron-builder said so on every run, in a line easy to read past: "default Electron
    icon is used, reason=application icon is not set"
    . A store you install games with
    should not look like a framework demo in the Dock.

    The mark is a W with three lines running into it: the product's initial, and what it
    is doing. It uses the window's own palette, so the icon and the application it opens are
    the same object. It was drawn for the smallest size first — at 32px the W still reads
    and the lines survive as motion rather than as noise. A portal, a play triangle and a
    send arrow were each tried and each discarded: they already mean a loading spinner, a
    media player and a submit button.

    resources/icon.svg is the source and the only file to edit. make icons renders the
    .png, the .ico, the .icns and the Linux size directory from it — three committed
    binaries with no way to regenerate them is how an icon becomes something nobody dares
    touch.

    The window also picks it up when run from source, where there is otherwise no icon to
    carry and a dev run looks like a different application from the one being built.

    Nothing else changed: same store handling, same catalog, same sign-in.

    Downloads
  • v2.5.0 6285d93790

    WarpEngine Client 2.5.0
    ci/woodpecker/push/woodpecker Pipeline was successful
    ci/woodpecker/tag/woodpecker Pipeline was successful
    Stable

    mr.zero released this 2026-08-19 12:54:34 +00:00 | 1 commits to master since this release

    WarpEngine Client 2.5.0

    A store can be taken off the machine again. Hovering a store in the side menu shows
    a bin. It asks first, and says how many titles will go with it — because removing a
    store uninstalls what it installed. That is not a convenience: a store's state.json
    is the only record of which payloads, icons and menu entries belong to it, so leaving
    the games behind would leave orphans nothing could ever identify, least of all a later
    install of the same store into the same folder. The catalog cache, the settings and any
    sign-in token go too.

    Adding one moved to a + beside Refresh, and it now takes a catalog address of your
    own as well as the ones the registry lists. A bare host is enough — https is assumed —
    and the name is taken from the address. Both are actions on the whole store rather than
    on one of them, which is why they sit together; the full-width "Add a store…" button
    under the list read as a third store.

    The picker has a way out. Opening it with a store already installed used to be a
    trap: the grid was replaced and nothing short of installing something brought it back.

    Two things found by the new test. make storetest adds and removes a store in a
    sandbox, because removal is the only code here that deletes a directory tree and the
    path it deletes is named by the window. It immediately caught that http:// was
    accepted and became a store called http — the trailing slashes were being stripped
    before the scheme was checked — and that STORE_ROOT only prepended to the search
    path, so a "sandboxed" run still listed the real stores. With a delete button in the
    window, a sandbox that can reach a working installation is not a sandbox; it replaces
    the search path now.

    btn-secondary had no styling at all. It was introduced in 2.4.0 on the card's
    sign-in button and on the sign-in panel, and rendered as a plain button in both places.

    Downloads
  • v2.4.0 3d42355189

    WarpEngine Client 2.4.0
    ci/woodpecker/push/woodpecker Pipeline was successful
    ci/woodpecker/tag/woodpecker Pipeline was successful
    Stable

    mr.zero released this 2026-08-19 09:58:21 +00:00 | 3 commits to master since this release

    WarpEngine Client 2.4.0

    The catalog can now say a title is not yours, and the client can do something about
    it.
    Where a store sells things, a card shows the price and a Buy button that
    opens the store's own page; where you own it, an Install as before. Two new
    categories go with it — Owned and To buy — because owning something is not the
    same as having installed it.

    Signing in, without a password ever reaching this window. The side menu grows an
    Account block, and signing in shows a short code: your browser opens on the store's
    own page and you type it there. That detour is the point — a desktop application asking
    for a password is a desktop application people should not be giving one to. The token
    lives in the OS keychain (Keychain, libsecret, DPAPI), one per store, and Sign out
    revokes it at the server as well as forgetting it here.

    None of this is knowledge about any particular store. It all arrives from the
    catalog's own server: WarpEngine 0.5 answers GET /api/service with what it offers, and
    puts an access block on every entry. A client that carried those facts would work for
    exactly one shop — this one asks, which is why the same build serves any of them.

    A store with no sign-in shows none. Every WarpEngine before 0.5 has no descriptor at
    all, and a 0.5 store that sells nothing reports none either. Both read as "a plain
    catalog", which is what this client assumed for its whole life until now, and the window
    behaves accordingly: no Account block, no prices, no new categories.

    A title nobody has bought is not dimmed. The dimming and the dashed badge belong to
    what this machine cannot do — an unsupported platform, no build for this architecture
    — and there is nothing wrong with the machine when a title simply costs money.

    A bearer token is never sent to a host that did not issue it. A gated download
    answers with a redirect to signed storage, often somebody else's server, and some object
    stores refuse a request outright when an Authorization header rides along with the
    signature. The credential stops at the origin it belongs to; a redirect back to the
    catalog keeps it.

    Downloads
  • v2.3.0 e35a72336a

    WarpEngine Client 2.3.0
    ci/woodpecker/push/woodpecker Pipeline was successful
    ci/woodpecker/tag/woodpecker Pipeline was successful
    Stable

    mr.zero released this 2026-08-19 06:17:43 +00:00 | 5 commits to master since this release

    WarpEngine Client 2.3.0

    A card says which version you have, and offers the newer one. Where the catalog has
    moved on, an installed title's version line reads 0.1 → 0.3 instead of just the number
    on your disk, so the card answers both questions a person came to it with: what is
    installed, and is there anything better.

    Which version is installed was already recorded — that is what state.json has always
    been for. What was missing was somewhere to act on it.

    The actions moved into a three-dot menu. Installed cards now lead with Play (or
    Open for a hosted title) and put the rest behind the ⋮ button beside it:

    Upgrade fetches whatever the catalog now has, replacing the old payload and menu entry. Greyed out when there is nothing newer
    Uninstall as before — the payload, the icon and the menu entry, and nothing else

    Upgrade stays visible while disabled rather than appearing and disappearing: a menu whose
    items come and go makes a person hunt for the one they used last time, and greyed out
    already says "not now".

    Playing stays the headline action even when an upgrade is waiting. The version on the disk
    still runs, and wanting to play it is not the same as wanting to wait for a download.

    Package names have no spaces. WarpEngineClient-2.3.0-arm64.dmg rather than
    WarpEngine Client-2.3.0-arm64.dmg, and the same for the AppImage and both Windows
    builds — a space in a release asset is a space in every curl, script and shell command
    that ever touches it. The app is still called WarpEngine Client, which is what appears
    in the Dock and in /Applications; only the file names changed.

    Also

    The catalog refresh control's tooltip now says Refresh the catalog, because in Hungarian
    it and the new Upgrade both wanted the word "Frissítés" and only one of them reloads a
    list.

    Opening it on macOS

    Ad-hoc signed, not notarised, so macOS asks first:

    xattr -dr com.apple.quarantine "/Applications/WarpEngine Client.app"
    

    What is attached

    The macOS package, built and verified on a Mac, plus the Linux (AppImage, deb) and
    Windows (installer, portable) packages the pipeline builds when the tag is pushed.

    Verified

    make check is clean: typecheck, lint, the headless smoke test and the window self-test.

    The upgrade path was tested on a sandbox store rather than reasoned about. Two titles were
    installed, then one of their state records was rewritten to claim an older build — which is
    exactly what the engine compares — and the window was asked what it would offer:

    BombExpert    [newer]   Upgrade:on  Uninstall:on
    Rabbit Roller [current] Upgrade:off Uninstall:on
    

    Pressing Upgrade runs the same call the window makes, and the record went from 0.1 to
    0.2 with the old payload removed first. The self-test now asserts that pairing on every
    installed card, because a closed menu photographs identically whether or not its items are
    right.

    The new package names were read off a real build (WarpEngineClient-2.3.0-arm64.dmg,
    …-arm64-mac.zip) and checked against the release script's own file filter, which finds
    both. The Windows names are -Setup- and -Portable- so the two .exe targets cannot
    resolve to one name and overwrite each other.

    Downloads
  • v2.2.0 82590d3ec4

    WarpEngine Client 2.2.0
    ci/woodpecker/push/woodpecker Pipeline was successful
    ci/woodpecker/tag/woodpecker Pipeline was successful
    Stable

    mr.zero released this 2026-08-19 05:27:52 +00:00 | 6 commits to master since this release

    WarpEngine Client 2.2.0

    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.

    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.

    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 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

    Ad-hoc signed, not notarised, so macOS asks first:

    xattr -dr com.apple.quarantine "/Applications/WarpEngine Client.app"
    

    What is attached

    The macOS package, built and verified on a Mac, plus the Linux (AppImage, deb) and
    Windows (installer, portable) packages the pipeline builds when the tag is pushed.

    Verified

    make check is clean: typecheck, lint, the headless smoke test and the window self-test.

    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.

    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 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.

    Downloads
  • v2.1.0 045c7bf5b7

    WarpEngine Client 2.1.0
    ci/woodpecker/push/woodpecker Pipeline was successful
    ci/woodpecker/tag/woodpecker Pipeline was successful
    Stable

    mr.zero released this 2026-08-19 04:43:39 +00:00 | 7 commits to master since this release

    WarpEngine Client 2.1.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.

    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.

    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.

    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.

    Opening it on macOS

    Ad-hoc signed, not notarised, so macOS asks first:

    xattr -dr com.apple.quarantine "/Applications/WarpEngine Client.app"
    

    What is attached

    The macOS package, built and verified on a Mac, plus the Linux (AppImage, deb) and
    Windows (installer, portable) packages the pipeline builds when the tag is pushed.

    Verified

    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:

    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

    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.

    Downloads
  • v2.0.0 06f3f2a3b1

    WarpEngine Client 2.0.0
    ci/woodpecker/push/woodpecker Pipeline was successful
    ci/woodpecker/tag/woodpecker Pipeline was successful
    Stable

    mr.zero released this 2026-08-18 21:36:35 +00:00 | 8 commits to master since this release

    WarpEngine Client 2.0.0

    The store engine is part of the app. Nothing has to be installed on the machine any
    more.
    Reading the catalog, choosing which release fits your computer, downloading and
    unpacking it, writing the menu entry and remembering what went where all happen inside
    the application now. There is no Python to find, no child process, and no JSON protocol
    between the two halves — which is why this is a major version rather than a feature.

    What that changes for a person: on Windows and on a fresh Mac the app simply works.
    Before it looked for python3, python and py -3, and where none answered it drew a
    screen with a link to python.org instead of a catalog. That screen is gone, along with
    the one that offered to refresh a store engine too old to drive.

    Your existing library is kept. config.json and state.json on disk are unchanged —
    the same field names, the same <scope>:<name> keys, the same file modes — so a machine
    whose games were installed by the shell store keeps them. Opening this version against
    such a store lists them as installed, offers no needless update, and a sync reports
    already up to date. A version: 1 state file is still migrated on first read.

    The two Python files an earlier install left in the store folder are removed the next
    time that store is set up. Nothing reads them, and a folder that still looks like it
    holds the engine invites someone to run it against a state file this app is also writing.

    The client knows which WarpEngine served a catalog. Every WarpEngine API response
    carries a WarpEngine-Version header, and the client now reads it, names the version in
    the log, and picks the catalog dialect for it. SUPPORTED_WARP_ENGINE_VERSIONS lists what
    this build was written against — 0.2, 0.3 and 0.4 — and the four cases are all handled:

    The header says What the client does
    a supported version reads the catalog with that version's dialect
    nothing at all reads it as the oldest supported version — an engine before 0.4.0 sent no header
    something older the same, and says so in the log
    something newer tries the newest dialect anyway, warning that titles may be missed

    Adding a version to that list fails the build until somebody says what it reads like, in
    the type checker and in the linter both. A new engine version cannot arrive unnoticed.

    Refresh and the language picker are icons. They sit together at the foot of the side
    menu, and the Actions heading that used to head a section of one button is gone. Both
    carry their name as a tooltip and to a screen reader, and the language picker is still a
    real <select> underneath — the native dropdown, keyboard and all, with only the glyph
    showing.

    Also

    No runtime dependencies, still: the zip reader the installer needs is about 150 lines over
    node:zlib rather than a package. It restores the executable bit from each entry's
    external attributes, which is what makes an unpacked game able to start at all, and it
    refuses a zip64 archive, an unknown compression method or an entry that would be written
    outside its destination rather than guessing.

    The repository itself is free of Python too — the Makefile, the CI check and the release
    script read package.json and the forge's JSON with Node now.

    Opening it on macOS

    Ad-hoc signed, not notarised, so macOS asks first:

    xattr -dr com.apple.quarantine "/Applications/WarpEngine Client.app"
    

    What is attached

    The macOS package, built and verified on a Mac, plus the Linux (AppImage, deb) and
    Windows (installer, portable) packages the pipeline builds when the tag is pushed.

    Verified

    make check is clean: typecheck, lint, the headless smoke test and the window self-test.

    The new engine was measured against the old one rather than trusted. On the same catalog
    and the same config, the Python engine and this one produce the same 13-title listing
    with zero field differences
    and the same resolved paths. Installing three titles — a
    bare TIC-80 binary wrapped in a bundle, a Godot .app symlinked, and a hosted web entry —
    gives byte-identical payloads, identical file modes and an identical Info.plist; the
    only difference in the two trees is the sandbox path inside the generated launcher script.
    state.json matches record for record.

    The upgrade path was tested directly: pointed at a store home installed by the Python
    engine, this one reports all three titles installed with no update available, and a
    re-sync writes nothing. Remove, prune, prune-suppression on a named sync, and the v1→v2
    state migration were each exercised. The zip reader was checked against Python's
    extractall on an archive holding stored, deflated, directory and symlink entries —
    identical bytes and identical modes — and its zip-slip and not-a-zip guards both fire.

    Downloads
  • v1.5.0 8511ccbef8

    WarpEngine Client 1.5.0
    ci/woodpecker/push/woodpecker Pipeline was successful
    ci/woodpecker/tag/woodpecker Pipeline was successful
    Stable

    mr.zero released this 2026-08-18 17:45:54 +00:00 | 9 commits to master since this release

    WarpEngine Client 1.5.0

    The app is called WarpEngine Client. It was WarpEngine Store, which named the thing
    it opens rather than the thing you run — and the store is a catalog on a site, not a
    window on your machine. The window title, the packages, the bundle and the menu entry all
    say Client now; the repository already did. The store the window is driving is named in
    the side menu, so the bar no longer repeats it as a badge.

    Every title in the catalog is listed, including the ones this machine cannot install.
    A C64 cartridge on a desktop, or a game with no build for your operating system, used to
    be silently absent — leaving you to wonder whether the catalog is small or your machine
    is unusual. Those cards are now there, dimmed, with an unsupported platform or no
    build for this machine
    badge and the engine's own sentence underneath, and with nothing
    to press. They have a category of their own, Not for this machine, and they stay out of
    the native/hosted counts: a title with no build has no mode to be counted under.

    This needs store engine 1.2.0 (desktop) on warpstore 1.4.0, which report what
    they had to leave out and why. An older engine still works — its listing is simply all
    installable, as it was before.

    A build can be pointed at another site's registry.

    make dist STORES_API=https://games.example.org/api/stores
    

    The address is written into the packaged app's own package.json, so a client built for
    somebody else's catalog needs no source change and nothing set on the user's machine. A
    runtime STORES_API still wins, which is for trying something out rather than shipping.

    Also

    The scrollbars are the window's own now: the platform's light track down the side menu of
    a dark window looked like a mistake.

    Opening it on macOS

    Ad-hoc signed, not notarised, so macOS asks first:

    xattr -dr com.apple.quarantine "/Applications/WarpEngine Client.app"
    

    What is attached

    The macOS package, built and verified on a Mac, plus the Linux (AppImage, deb) and
    Windows (installer, portable) packages the pipeline builds when the tag is pushed.

    Verified

    make check is clean. The store on this machine lists 13 titles — ten installable, three
    C64 cartridges this store does not carry — and the window was photographed with all
    thirteen on screen, the three dimmed and labelled. The engine change was measured through
    the CLI as well, in both its human and its --json listing, and the other two store
    engines still get the two-value answer they ask for.

    Downloads
  • v1.4.0 a364a5ce5f

    WarpEngine Store 1.4.0
    ci/woodpecker/push/woodpecker Pipeline was successful
    ci/woodpecker/tag/woodpecker Pipeline was successful
    Stable

    mr.zero released this 2026-08-18 15:24:42 +00:00 | 10 commits to master since this release

    WarpEngine Store 1.4.0

    A store no longer needs a repository of its own. Until now every store in the
    registry pointed at a repository holding its config.json, and the client read that
    file to know what to install. It turns out almost nothing in there was necessary: the
    store engine's built-in defaults already cover the host-to-asset mapping, the install
    modes, the platforms and the behaviour. What defaults cannot know is identity — a
    slug, a name and a catalog URL — and that is exactly what a registry record carries.

    So storeRepositoryUrl is now optional. A record with a name and a catalog URL is a
    complete store: the client derives the slug from the catalog host
    (teletypegames.orgteletypegames), writes a small config and installs. Given a
    repository it still reads it, and that file remains the authority on how the store
    behaves — which platforms it offers, which statuses it shows, where things land. A
    repository without a config.json is treated as no repository at all.

    With the defaults, games land in a folder named after the store id and released,
    archived and demo titles are listed: a catalog that publishes a demo means it to
    be played.

    The site's registry endpoint changed to match — storeRepositoryUrl answers null
    when there is none — and adding a store is now genuinely one database row with two
    fields filled in.

    The "Install all" button is gone. Titles are installed one at a time from their
    own cards.

    No footer. The window carried a bar at the bottom at all times — a toggle and a
    line of absolute paths — for something most sessions never need. The log is still
    there, with the two folder buttons in it, but it lives behind a quiet switch at the
    bottom of the side menu and takes no room until it is opened. The grid gets the height
    back.

    The repository is now warp-engine-client. The app has always been called
    WarpEngine Store; warp-engine-desktop-gui described the role rather than the
    product, and the host-specific engines keep their own shape
    (warp-engine-desktop-store, -retroarch-store, -batocera-store). Gitea keeps a
    redirect from the old path, and the releases and tags moved with the repository, so
    existing links and clones still resolve.

    Three things a screenshot found

    Photographing the setup screen — which no automated count had ever looked at — turned
    up three faults that every check had passed:

    • the store badge in the bar rendered as an empty pill when no store was open;
    • the gate's store picker showed as an empty dropdown stub, because an explicit
      display in the stylesheet beats the browser's own [hidden] rule;
    • the gate went up while the "No installable titles in the catalog" line stayed on
      screen underneath it.

    The last one was a design fault, not a typo: whether the gate is up was an imperative
    call on a view rather than state, so the gate and the grid could disagree. The setup
    screen is now a field in the state store, and that one field decides which of the two
    is drawn. The window test's gate assertion was wrong too — it demanded a store picker,
    which only appears when the registry offers more than one store, so a perfectly good
    window failed it.

    Linux and Windows packages now come from CI

    A vX.Y.Z tag now starts the pipeline, which builds the AppImage, the deb, the NSIS
    installer and the portable exe — Windows through Wine — creates this release and
    attaches all four. macOS stays a local build, because Apple's toolchain and its signing
    exist only on a Mac, so make release from a Mac pushes that package onto the same
    release afterwards. Publishing uses a gitea_token repository secret in Woodpecker.

    The Windows installer is not signed: Windows will warn about an unknown publisher until
    there is a certificate.

    Opening it on macOS

    Ad-hoc signed, not notarised, so macOS asks first:

    xattr -dr com.apple.quarantine "/Applications/WarpEngine Store.app"
    

    What is attached

    The macOS arm64 package, built and verified here, plus whatever the pipeline attaches
    for Linux (AppImage, deb) and Windows (installer, portable).

    Verified

    A repository-less store was installed end to end against a local registry serving one
    record with storeRepositoryUrl: null: the id came out as teletypegames, the engine
    and the shared core downloaded, the written config had three sections, engine 1.1.0
    accepted it, it listed the same ten titles the configured store does, and a hosted
    title synced into a sandbox with its menu entry written. make check is clean, and
    the setup gate was photographed on a machine with no store at all.

    Downloads
  • v1.3.0 3d63c8a0b0

    mr.zero released this 2026-08-18 13:30:23 +00:00 | 14 commits to master since this release

    WarpEngine Store 1.3.0

    TypeScript, in layers. The client was one main.js, one preload.js, three files
    in lib/ and one renderer script. It is now a typed application with the dependency
    rule pointing inward: domain (models, ports, errors) knows nothing about Electron,
    Node or Python; application orchestrates it through those ports; infrastructure
    holds the adapters — the Python CLI, HTTP, the filesystem, Electron itself — and the
    hosts (main, preload, renderer) sit on top. STRUCTURE.md is
    the map: every layer, every pattern in use, and the naming rules, written to be read
    before adding anything.

    Nothing about the window changed. Same side menu, same categories, same switcher, same
    two languages — this release is the inside of the app.

    Two properties came out of the move, and both are worth having:

    • The catalog can be driven with no window and no Electron at all. make smoke
      assembles the same services against the same ports in a plain Node process. It was a
      script that reimplemented the bridge before; now it is a second composition root.
    • The window never receives a filesystem path. A title crosses the bridge without
      one, and launching is asked for by name — the main process resolves what that means
      from the store's own state. Nothing in the renderer can be talked into opening a path.

    A strict linter, and types everywhere. strict plus
    noUncheckedIndexedAccess, exactOptionalPropertyTypes, noImplicitOverride,
    noPropertyAccessFromIndexSignature and friends; typescript-eslint's
    strictTypeChecked and stylisticTypeChecked sets; explicit return types, parameter
    types and property types required even where inference would manage; exhaustive
    switches; no any, no !, no casts on foreign data — engine output and the registry
    go through readers that turn unknown into typed values. The naming patterns are
    enforced by naming-convention rather than trusted.

    Two things the types now catch that a person used to: a translation with a missing key
    does not compile, and a channel the preload does not implement does not compile.

    New make targets: make build, make typecheck, make lint, make lint-fix and
    make check — the gate, which runs the type-check, the linter and both test suites in
    that order, cheapest failure first. Every script that runs the app builds first, so a
    stale bundle cannot be tested.

    Opening it on macOS

    Ad-hoc signed, not notarised, so macOS asks first:

    xattr -dr com.apple.quarantine "/Applications/WarpEngine Store.app"
    

    Open Anyway under System Settings ▸ Privacy & Security works as well.

    What is attached

    macOS arm64 only, the machine this was built and verified on. Windows and Linux
    packages need a build on those platforms (make dist-win / make dist-linux).

    Verified

    make check is clean: no type errors, no lint findings, the smoke test green against
    the real store and against a sandbox one, and the window test green with one store and
    with two — where it clicks the store that is not open and checks that the bar, the grid
    and the categories follow. The window was photographed before and after the refactor
    and the two are the same picture. The packaged app was run from the built bundle, not
    from a dev launch.

    Downloads