This repository was archived by the owner on Sep 14, 2026. It is now read-only.
Repository navigation
Conversation
The front is the part this repository is evaluating, so it stays exactly as it is; everything that existed only to host it under Electron goes. Gone: src/main, src/preload, src/ipc, src/config and src/utils, with the tests that covered them, plus electron-vite, electron-builder, the release and Windows conformance workflows, and the electron, electron-updater and electron-log dependencies. Nothing in src/renderer or src/domain imported any of it: the renderer only ever reached the host through window.api, and src/domain was already free of Node and Electron, which is what made the cut a deletion rather than a rewrite. Two pieces were host-shaped by accident rather than by nature and moved into the domain instead of being deleted: - normalizeConfig, now src/domain/config/normalize.ts. Every rule in it is a decision about what a config field may hold, and the host only ever supplied the three folder paths a fresh config falls back to. Those are a parameter now, so the function is pure and both sides can share it. - the two archive size ceilings a backup is measured against, now in src/domain/installations/backupCapacity.ts next to the arithmetic that reads them. The renderer builds through vite.config.ts with the same two aliases it had and the Tailwind v4 plugin, rooted at src/renderer so index.html keeps loading /src/main.tsx as before. The document's CSP meta tag moves to the Tauri configuration, which serves the policy as a header and rewrites it to admit its own IPC; two policies would intersect and cut that off. Vitest keeps both projects. Coverage thresholds are dropped rather than carried over: they were measured on a tree with the Electron host in it.
src-tauri is a Rust binary with four commands in three jobs: read_config and write_config for the config file, list_installations for what it lists, and launch_game for starting a version. The window is 1280x720 with a 1024x600 floor, the identifier is dev.stratumvs.riftlauncher.tauri, the bundle targets are AppImage, deb and rpm, and the icons come from the set the launcher already ships. The config is read and written here and validated there. Rust does file IO and nothing else with the document: it hands over the raw JSON plus the three fallback folders, and normalizeConfig, the same function the Electron host called, decides what the fields may hold. A second normalizer in Rust would be the one that drifts, and the one that drifts is the one that stops agreeing with the file on disk. Writes go through a temporary file in the destination's own directory, flushed before the rename, so a machine that loses power leaves the previous config rather than an empty one. launch_game is the exception, and it is an honest one. The renderer names a version and an installation by id; every path, argument, environment variable and wrapper is read out of the saved config by the host itself, and both folders have to sit inside a root that config names before anything is spawned, lexically and again after symbolic links are resolved. That check is the reason the plan is restated in Rust rather than computed on the front: picking the executable means listing the version folder, which the front cannot do. The three details a player's game depends on, the single start-parameters argument, the executable leading the arguments under mono, and the Mesa variable on the native Linux launcher only, each have a test. The capability grants the core event, window and webview surface. The commands this host defines are application commands, which the access control list does not gate; the tray, the menu, image and resource handles and the path API are left out because nothing asks for them. On the front, src/renderer/src/host/tauriApi.ts implements the whole window.api shape and main.tsx installs it before the first render, so no page, hook or adapter changed and the renderer-dom tests keep installing their own mock over the same property. Three members are real calls. The rest refuse in the vocabulary their own return type already has and say so once under a fixed token. None of them invents a success: a checkPathExists that answered true would have the renderer build a flow on a folder that is not there.
One job per platform runs the front and the host together: npm ci, typecheck, lint, format, the whole vitest run, then cargo fmt, clippy with warnings denied, the Rust tests and a build. A Rust change that breaks the front should turn one leg red rather than two unrelated ones. A second job bundles on Linux and uploads the AppImage and the deb. It builds through the @tauri-apps/cli devDependency, so the bundle comes from the version the lockfile pins. The Windows bundle needs its own installer decisions and waits for a pass of its own. The Sonar project key and exclusions follow the tree: no out/ or resources/ any more, and the Rust target and icon directories stay out of the scan.
The README opened on a launcher people download. This one is not that: it is the Tauri host behind RiftLauncher issue 18, and the first thing a reader needs is that the downloads are somewhere else. It also says the part that is easy to get wrong: the front is shared but not symmetrically. A renderer or domain fix that lands here first is a fix that gets lost the next time the two repositories are reconciled, so it belongs in StratumServer/RiftLauncher and gets cherry-picked across. The sections about what the launcher is, privacy, translation and support are kept. The feature list went, since the paragraph above it already said the same thing, and two links into a docs tree this repository does not have went with it. docs/architecture.md keeps a note saying it still describes the Electron host.
build/icons was electron-builder's icon set; the Tauri host generates its own from the same source into src-tauri/icons, so nothing reads these any more. The VS Code launch configuration started electron-vite, which is not installed; a debug entry that runs a missing binary is worse than none, and the dev loop is npm run tauri:dev. The extension recommendations pick up Tauri and rust-analyzer.
Every local bundle run died the same way: linuxdeploy carries an old binutils, and that strip cannot read the .relr.dyn section modern linkers put in the system libraries it copies into the AppDir. It does not skip the library, it fails the whole AppImage, and the deb and rpm never get built because the AppImage runs first. NO_STRIP skips that pass. The AppImage then carries unstripped copies of libraries it was going to carry either way, which is a few megabytes against a bundle that does not build at all.
tauri-build rewrites src-tauri/gen/schemas on every run, and it writes them minified, while the copies in the tree were formatted by prettier. So any local cargo build left four tracked files dirty and turned format:check red, which is a confusing way to learn that you built the host before running the checks. They are build output. The build script recreates all four from tauri.conf.json and the capability, verified by deleting the folder and building again, so nothing needs the committed copies. CI never did either: it generates them during the cargo steps, which run after format:check. Prettier 3 reads .gitignore as well as .prettierignore, so ignoring the folder settles both halves at once.
Four of the launch plan tests built their expectation as a string with forward slashes, while build_plan reaches the game file through Path::join. On Linux the two happen to agree, so the tests passed here and on the Ubuntu leg, and the Windows leg read /v\Vintagestory.exe against /v/Vintagestory.exe and failed all four. The rules under test are the argument shape, the mono fallback and the wrapper, none of which is about separators, so the expectations now join the same way the code does and the tests go back to being about the rules. No production code changed: it was already right on both platforms, and only the assertions were Unix shaped.
Contributor
Author
|
Closing without merging: the team decided to stay on Electron (RiftLauncher issue 18). This branch and docs/notes/tauri-spike.md stay as the reference for the spike; the repository is archived. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
The Tauri v2 skeleton for RiftLauncher issue 18. The front is untouched: the same React under
src/renderer, the same pure logic undersrc/domain, callingwindow.apiexactly as it always has. What changed is everything behind that property.The point of a skeleton is to measure the gap, not to hide it. So the Rust host does three jobs, and every other member of
window.apiis a stub that refuses in the vocabulary its own return type already has and says so once in the console under a fixed token. None of them invents a success. AcheckPathExiststhat answeredtruebecause answering something felt more useful would have the renderer build a flow on a folder that is not there, and this PR would be showing a bug rather than a gap.What the host does today: read
config.json, write it back atomically, list the installations that config names, and start the game. The launch honours the installation's data folder, start parameters, environment variables, Mesa GL thread setting and Linux launch wrapper, and refuses any path outside the folders the config manages.What it cannot do: install a version. Which means it cannot yet start a game a fresh user does not already have.
What moved and what stayed
Stayed, untouched.
src/renderer/**(171 files) andsrc/domain/**. Nothing in either imported@src/, so the cut was a deletion rather than a rewrite: the renderer only ever reached the host throughwindow.api, andsrc/domainwas already free of Node and Electron.Deleted.
src/main/,src/preload/,src/ipc/,src/config/,src/utils/(56 files), pluselectron.vite.config.ts,electron-builder.yml,dev-app-update.yml,postcss.config.js,resources/,build/icons/,scripts/,tsconfig.node.json, the release and Windows-conformance workflows, and theelectron,electron-vite,electron-builder,electron-updaterandelectron-logdependencies.Moved into the domain, not deleted. Two pieces were host-shaped by accident:
normalizeConfigand the config defaults are nowsrc/domain/config/normalize.ts. Every rule in that function is a decision about what a config field may hold; the host only ever supplied the three folder paths a fresh config falls back to, which are a parameter now. Both sides share it, and the Rust side does no validation of its own. Two normalizers would be one too many: the one that drifts is the one that stops agreeing with the file on disk.MAX_BACKUP_TOTAL_BYTESandMAX_ARCHIVE_TOTAL_BYTESnow sit insrc/domain/installations/backupCapacity.ts, next to the arithmetic that reads them.src/preload/preload.d.tsbecamesrc/api.d.ts. It is a type declaration, not Electron code, and it is still the contract the bridge is held to.New.
src-tauri/(Cargo manifest,tauri.conf.json, one capability, icons, four commands acrossconfig.rs,launch.rs,paths.rs),vite.config.ts,src/renderer/src/host/tauriApi.ts,.github/workflows/ci.yml.Honest about the stubs. Everything in this list answers a refusal and logs
[host] [tauriApi] not-implemented-on-tauri <member>once:utilsgetAppVersion(answers0.0.0),setPreventAppClose,openOnBrowser,selectFolderDialog,onPreventedAppCloseappUpdatermodsManagerpathsManagergameManagerlookForAGameVersionnetManagerbackgroundsManageraccountManagerclientsettings.jsonTwo members are answered on the front rather than stubbed, and are marked as such in the file.
getOsreads the user agent, because the renderer only asks it to pick a download and to show Linux-only settings, and a stub answeringlinuxon a Windows machine would be a wrong answer rather than a missing one.logMessagewrites to the console, which is a real destination, just not a file.Two things in the host worth a reviewer's eye.
launch_gamerestates the launch plan fromsrc/domain/versions/launch.tsin Rust instead of calling it. That is not laziness about reuse: picking the executable means listing the version folder, which the front cannot do, and adding a directory-listing command to let it would hand the renderer a capability the skeleton has no other use for. The three details a player's game actually depends on (start parameters as one argument, the executable leading the argument list undermono, the Mesa variable on the native Linux launcher only) each have a Rust test pinning them against the TypeScript. If the two ever need to be one thing, the fix is alist_version_foldercommand, not a copy that drifts.saveConfighas nounauthorized-pathrefusal. The Electron host compared the incoming config against the previous one before accepting a folder outside the managed roots; that comparison is not ported, so the reason exists in the type and never fires. Worth closing before anyone runs this on real data.list_installationshas no caller yet.getConfigcarries the installations already, so the command is there for the slice that stops reading the config wholesale, exposed as a named export ontauriApirather than onwindow.api.Testing
Front, both vitest projects (
node, which runstests/domainand the top-level suites, andrenderer-dom), on this machine:That is a green run, and three of five full runs on this machine were green. The other two each
failed two renderer-dom tests, a different pair each time, and both runs were started while a
cargo build was still competing for the machine. Every one of those tests passes when its file is
run on its own. The failures are testing-library queries giving up after their one second default
while the CPU is busy, not assertions disagreeing, and none of the four files involved is touched
by this branch: the renderer-dom half of
vitest.config.tsis byte for byte the one ondev. Sothis is the front's existing load sensitivity showing up on a loaded laptop rather than something
the port introduced. Worth a fix upstream, where the front lives, not here.
npm run typecheck,npm run lint:ci(0 errors, 14 pre-existingexhaustive-depswarnings),npm run format:checkandnpm run buildall pass.Rust:
cargo fmt --check,cargo clippy --all-targets -- -D warningsandcargo test(21 tests: the launch plan, the environment allow list, the wrapper resolution, the path containment rules, and the managed roots a config names).87 test files were deleted, every one of them a test of a module that no longer exists:
tests/ipc/**(52),tests/main/**(6),tests/utils/**(4, all electron-updater and electron-log),tests/config/**(3, electron-builder configuration),tests/e2e/**(3, the Windows installer), the zip and child-process fixtures (15), plussecurity-boundaries.test.ts,ipc-validation.test.ts,rendererErrorLog.test.tsandsetup-node.ts. Nothing undertests/domain,tests/renderer,tests/renderer-domortests/i18nwas deleted or skipped, andtests/renderer-dom/tauriApi.test.tsxis new (10 tests on the bridge).The vitest coverage thresholds are dropped rather than carried over. They were measured against a tree with the Electron host in it, so every one of those numbers was about code that is gone. New floors get measured once the host stops being a skeleton.
npm run tauri -- buildproduces:src-tauri/target/release/bundle/appimage/RiftLauncher_1.7.0-beta.10_amd64.AppImagesrc-tauri/target/release/bundle/deb/RiftLauncher_1.7.0-beta.10_amd64.debsrc-tauri/target/release/bundle/rpm/RiftLauncher-1.7.0-beta.10-1.x86_64.rpmThe deb and the rpm move by a few bytes between builds, so treat those two numbers as approximate rather than a checksum. The deb and the rpm are 3.7 MB because they declare WebKitGTK as a dependency and let the system provide it. The AppImage carries it, which is what AppImages do, and it is 96 MB here rather than the usual 80-ish because of the
NO_STRIPbelow.The first bundle run failed, and it took the deb and rpm down with it, since the AppImage is built first. linuxdeploy carries its own old binutils, and that
stripcannot read the.relr.dynsection this host's linker puts in the system libraries it copies into the AppDir; it fails the whole AppImage rather than skipping the library.NO_STRIP=trueskips that pass, and the run above is with it. The CI bundle job sets the same variable, with the reason written next to it.Not tested: the app actually opening. There is no virtual display on the machine this was built on, and opening a window on a real desktop was not an option, so the binary has never been run. Everything above is static: it compiles, it bundles, the tests pass. Which means the one thing nobody has confirmed is whether the capability in
src-tauri/capabilities/default.jsongrants enough for the webview to come up and carry aninvoke. It listscore:event,core:windowandcore:webviewand leaves out the tray, the menu, image and resource handles and the path API, on the reasoning that application commands are not gated by the access control list and nothing on the front asks for the rest. First person to run it should say whether that holds; widening it is one line.Windows bundling is deliberately not in this PR. It needs its own installer decisions, and proving the host bundles at all is the thing worth doing first.
One follow-up commit sits on top of the ones above.
src-tauri/gen/schemaswas committed, andtauri-build rewrites all four of those files, minified, on every run. So any local
cargo buildleft the tree dirty and turned
format:checkred, which is a confusing way to learn you built thehost before running the checks. They are build output, recreated from
tauri.conf.jsonand thecapability, verified by deleting the folder and building again, so the folder is ignored now
rather than tracked. CI never read the committed copies either: it generates them during the
cargo steps, which run after
format:check.CI is green on both legs and on the bundle job. Getting there took one fix worth naming, because
it is exactly the class of thing this repository exists to find. Four launch plan tests wrote their
expectation as a forward slash string while
build_planreaches the game file throughPath::join. Linux could not tell the difference, so they passed here and on the Ubuntu leg, andthe Windows leg compared
/v\Vintagestory.exeagainst/v/Vintagestory.exeand failed all four.The assertions join the same way the code does now. No production code moved: it was already
right on both platforms, and only the tests were Unix shaped.
The
sonarcloudjob is red and stays red until someone with repository settings adds aSONAR_TOKENand creates the project. It iscontinue-on-error: truefor exactly this reason, soit does not gate the merge, but it will keep showing a red tick on the checks list until the new
repository is wired up the way the front's is.