Skip to content

Rework injection to shadow Discord's app.asar - #421

Merged
zerebos merged 20 commits into
wailsfrom
app-asar-injection
Jul 19, 2026
Merged

Rework injection to shadow Discord's app.asar#421
zerebos merged 20 commits into
wailsfrom
app-asar-injection

Conversation

@zerebos

@zerebos zerebos commented Jul 18, 2026

Copy link
Copy Markdown
Member

Human Sumary

Switches the injection to the Discord resources folder to match the intended injection swap by Doggybootsy. This will in theory allow the client code to hook the updater and survive versioned updates. Also added some additional logic and handling for uninstall and reinjection cases. Added a bunch more testcases.

Important Caveats

  • Global flatpak installs will require sudo
  • Not all macOS versions and security levels will be compatible with or without admin
  • Snap installs on linux are entirely unsupported

Clanker Summary

Migrates the installer's injection method from overwriting discord_desktop_core/index.js (a downloadable Discord module) to shadowing Discord's own app.asar in the install's resources/ directory.

For each install we now:

  1. Rename resources/app.asarresources/betterdiscord.app.asar (preserving the original),
  2. Create resources/app/ with a static package.json ({"main":"./index.js"}) and index.js that loads BetterDiscord and then chain-loads the preserved app.

Why

The point isn't just a different location — it's when BetterDiscord's code runs. Shadowing app.asar makes BD load as (part of) the app entry point, before Discord's updater runs. Under the old discord_desktop_core injection, Discord's updater ran first, so BD couldn't react to updates. Now the BD client can hook the updater and re-propagate its own injection across Discord updates — so the installer is generally run once rather than after every Discord update.

What changed

Discovery / validation (discord/paths_*.go, paths_common.go)

  • Validation now anchors on resources/app.asar via a shared resolveResources drill that accepts a Discord root, an app-{version} dir, a resources dir, or a macOS .app bundle.
  • Channel/version prefer resources/build_info.json (with path-parsing fallback) — needed on macOS/flatpak where the path carries no version segment. GetChannel also understands macOS bundle names (Discord Canary.app).
  • Search roots: macOS/Applications + ~/Applications bundles; flatpak/var/lib/flatpak + ~/.local/share/flatpak deployment files/…/resources; Windows/Linux-native/WSL roots unchanged (only the drill changed).
  • DefaultBrowseDir() added per-OS so the "browse for Discord" dialog opens where Discord actually lives now.

Injection (discord/injection.go, discord/assets/)

  • New inject/uninject/IsInjected operating on the app.asar shadow, with a writability probe before the destructive rename and rollback on any post-rename failure (transactional: fully injected or fully reverted).
  • A live app.asar is always treated as authoritative: re-injecting over a Discord repair/reinstall discards a stale preserved copy and re-preserves the current app, so injection can't silently no-op.
  • Assets replaced with static, channel-agnostic app_index.js + app_package.json (runtime resolution of the BetterDiscord folder — no templating).

Data folder (discord/install.go)

  • Flatpak's BetterDiscord folder is recomputed as ~/.var/app/{id}/config from the channel (the deployment tree has no config segment to derive from).

Types / frontend

  • DiscordInstall.CorePathResourcesPath; the frontend corePaths store → resourcePaths.

Docs

  • README compatibility matrix + Flatpak/Snap FAQ updated; new FAQ explaining the injection method.

Platform support

Target Status Notes
Windows (native + WSL) ✅ verified
macOS ✅ verified Modifies the .app bundle; no elevation/Gatekeeper issue seen (monitoring across versions).
Linux native ✅ verified ~/.config/{channel}/app-*/resources.
Flatpak (per-user) ✅ verified
Flatpak (system/global) ⚠️ not yet Deployment under /var/lib/flatpak is root-owned; needs elevation (not implemented — see below).
Snap ❌ dropped Read-only squashfs mount can't host the shadow.

Known limitations / behavior changes

  • Global (system-wide) Flatpak is a regression. The old method wrote the user-writable sandbox config dir; the new one targets the root-owned deployment, so global-flatpak injection now fails the writability probe with a clear error. Per-user flatpak is unaffected. Restoring global flatpak needs the elevation work (a follow-up: pkexec for flatpak, osascript for non-admin macOS), which is intentionally deferred as reactive.
  • Snap is no longer supported at all (previously worked via the module method). Its read-only mount makes the app.asar shadow impossible.
  • macOS bundle signature: the asar rename invalidates the bundle's resource seal. In testing this caused no Gatekeeper prompt or launch failure (the executable's own signature is untouched), but behavior may vary across macOS versions/settings — worth watching. No speculative re-signing is done.

Testing

  • go test ./..., go vet ./..., task check, frontend bun run test, and GOOS=darwin/GOOS=windows cross-compile — all green.
  • New tests cover: clean/idempotent/re-over-repair injection, probe-abort, rollback, exact uninject, build_info.json sourcing, macOS bundle resolution, flatpak data-root recompute, and — importantly — that an injected install stays resolvable (so repair/uninstall keep working after injection).
  • SME manually verified the full flow on native Windows, macOS, Linux native, and per-user Flatpak.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the BetterDiscord installer to use a new injection method that shadows Discord's app.asar with a custom app/ directory in the resources folder, rather than modifying index.js inside discord_desktop_core. This change updates path resolution, validation, and installation logic across Windows, macOS, and Linux (including Flatpak, while explicitly dropping Snap support due to its read-only nature). The review feedback suggests improving error handling by logging failures during rollback renames and cleaning up stale betterdiscord.app.asar files during uninjection if a fresh app.asar has already been restored.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread discord/injection.go Outdated
Comment thread discord/injection.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR reworks BetterDiscord injection to shadow Discord’s resources/app.asar (preserving it as betterdiscord.app.asar and inserting a resources/app/ loader), aiming to load BetterDiscord before Discord’s updater and improve survivability across versioned updates. It updates discovery/validation to anchor on resources/, renames CorePathResourcesPath throughout, refreshes frontend state accordingly, and expands test coverage for the new behaviors.

Changes:

  • Replace the prior discord_desktop_core-based injection with a transactional app.asar shadow (inject/uninject/IsInjected) plus static loader assets.
  • Rework install discovery/validation to resolve the resources directory (including injected state) and prefer resources/build_info.json for channel/version, with per-OS default browse directories.
  • Update frontend + API surfaces to use resourcePaths/ResourcesPath, and add/extend unit tests to cover reinjection/rollback and path resolution.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
README.md Updates Linux/Flatpak/Snap support notes and documents the new injection approach.
frontend/src/routes/actions/setup/[action]/+page.svelte Renames state usage from corePaths to resourcePaths in setup flow.
frontend/src/routes/actions/perform/[action]/+page.svelte Builds install path map from resourcePaths instead of corePaths.
frontend/src/lib/types.ts Renames AppState.corePathsresourcePaths.
frontend/src/lib/stores/state.svelte.ts Initializes and populates resourcePaths from GetDiscordPath.
api/controller.go Renames action params to resourcePaths, uses DefaultBrowseDir(), returns ResourcesPath.
discord/paths.go Updates suggested/resolve logic to use ResourcesPath and improves channel parsing for macOS bundle names.
discord/paths_windows.go Adds DefaultBrowseDir() and keeps Windows path discovery aligned with resources/.
discord/paths_linux.go Updates Linux discovery roots for resources-based validation and adds DefaultBrowseDir() (incl. WSL).
discord/paths_darwin.go Changes macOS discovery to search .app bundles and adds DefaultBrowseDir().
discord/paths_common.go Introduces resolveResources, build_info parsing, injected-state recognition, and resources-anchored validators.
discord/paths_test.go Adds GetChannel tests for macOS bundle naming.
discord/paths_common_test.go Replaces core.asar tests with app.asar/resources tests; adds coverage for injected resolvability and build_info preference.
discord/install.go Renames CorePathResourcesPath and recomputes Flatpak BD data root from channel.
discord/install_test.go Updates uninstall and BD-path tests for resources-based injection + Flatpak recompute logic.
discord/injection.go Implements transactional app.asar shadow injection + uninject and updated IsInjected.
discord/injection_test.go Adds tests for clean/idempotent injection, rollback, probe abort, repair/reinject, and post-injection resolvability.
discord/assets/app_index.js New loader that loads BetterDiscord then chain-loads the preserved asar.
discord/assets/app_package.json Minimal package.json for the injected resources/app/ entrypoint.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread discord/injection.go
Comment thread discord/injection.go Outdated
@zerebos

zerebos commented Jul 18, 2026

Copy link
Copy Markdown
Member Author

/gemini review for the last time

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the BetterDiscord installer to inject into Discord by shadowing app.asar with a custom app/ directory rather than modifying index.js inside discord_desktop_core. This changes path tracking from CorePath to ResourcesPath across the Go backend and Svelte frontend, updates Flatpak path resolution, and drops Snap support due to read-only mounts. Feedback on these changes highlights critical robustness issues: empty ResourcesPath or input paths can cause operations (like write probing, uninjecting, and resource resolution) to run relative to the current working directory, risking accidental data loss. Additionally, the feedback points out a regression in DefaultBrowseDir where the fallback to HOME was lost, and a potential nil pointer dereference if discord.Channel is nil.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread discord/injection.go
Comment thread discord/injection.go
Comment thread discord/injection.go
Comment thread discord/paths_common.go
Comment thread discord/paths_linux.go
Comment thread discord/install.go

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 1 comment.

Comment thread discord/injection.go
@zerebos

zerebos commented Jul 18, 2026

Copy link
Copy Markdown
Member Author

/gemini review the fixes

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the installer to target Discord's resources directory instead of the discord_desktop_core module, implementing a new injection method that renames the original app.asar and uses a shadow app/ directory loader. It also updates path resolution, validation, and Flatpak configuration across platforms while dropping Snap support due to read-only mounts. The review feedback suggests wrapping the BetterDiscord require statement in a try-catch block to prevent Discord from failing to launch if the asar is corrupted, and refining the Snap path detection heuristic to avoid false positives.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread discord/assets/app_index.js Outdated
Comment thread discord/paths_common.go

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 1 comment.

Comment thread discord/injection.go Outdated
@zerebos

zerebos commented Jul 18, 2026

Copy link
Copy Markdown
Member Author

/gemini review final

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the BetterDiscord installer to use a new injection method that shadows Discord's app.asar with a custom app/ directory containing a loader script, rather than modifying index.js inside discord_desktop_core. This change involves renaming CorePath to ResourcesPath across the codebase, updating path resolution logic to target the resources directory directly (including reading build_info.json for channel and version details), and stopping the Discord process before performing injection or uninjection to avoid file lock issues. Additionally, macOS bundle search paths and Flatpak path resolution have been updated, while Snap support has been removed due to its read-only filesystem. One review comment was evaluated and kept as it correctly identifies a potential runtime error in the injection script if process.env.HOME is undefined, offering a robust fallback using require("os").homedir().

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread discord/assets/app_index.js

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.

Comment thread discord/process.go Outdated
Comment thread discord/install_test.go Outdated
Comment thread discord/injection_test.go
@zerebos

zerebos commented Jul 18, 2026

Copy link
Copy Markdown
Member Author

/gemini review the fixes

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the BetterDiscord installer to target Discord's resources directory directly, shadowing app.asar with a custom loader folder (app/) and renaming the original to betterdiscord.app.asar. It updates path resolution across Windows, macOS, and Linux to support this new injection method, removes Snap support due to read-only constraints, and updates Flatpak path handling. The frontend is updated to use resourcePaths instead of corePaths, and extensive unit tests are added. Review feedback highlights the need to parse channel names from right to left to avoid false positives from parent directories, and suggests adding error checks for os.UserHomeDir() and os.UserConfigDir() to prevent relative path fallbacks on macOS and Linux.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread discord/paths.go Outdated
Comment on lines 40 to 52
for folder := range strings.SplitSeq(proposed, string(filepath.Separator)) {
// Normalize the segment so macOS bundle names ("Discord Canary.app") and
// flatpak channel dirs ("discord-canary") both match the channel names
// ("discordcanary").
normalized := strings.ToLower(folder)
normalized = strings.TrimSuffix(normalized, ".app")
normalized = strings.ReplaceAll(normalized, " ", "")
normalized = strings.ReplaceAll(normalized, "-", "")
for _, channel := range types.Channels {
if strings.ToLower(folder) == strings.ReplaceAll(strings.ToLower(channel.Name()), " ", "") {
if normalized == strings.ReplaceAll(strings.ToLower(channel.Name()), " ", "") {
return channel
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Parsing the channel by iterating through path segments from left to right can lead to false positives if any parent directory contains a channel name (for example, if the user's home directory is /home/discord or if they have a folder named discord in their path). Iterating from right to left (backwards) is much more robust because the channel identifier is always closest to the leaf of the path.

	segments := strings.Split(proposed, string(filepath.Separator))
	for i := len(segments) - 1; i >= 0; i-- {
		folder := segments[i]
		// Normalize the segment so macOS bundle names ("Discord Canary.app") and
		// flatpak channel dirs ("discord-canary") both match the channel names
		// ("discordcanary").
		normalized := strings.ToLower(folder)
		normalized = strings.TrimSuffix(normalized, ".app")
		normalized = strings.ReplaceAll(normalized, " ", "")
		normalized = strings.ReplaceAll(normalized, "-", "")
		for _, channel := range types.Channels {
			if normalized == strings.ReplaceAll(strings.ToLower(channel.Name()), " ", "") {
				return channel
			}
		}
	}

Comment thread discord/paths_darwin.go Outdated
Comment thread discord/paths_linux.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.

Comment thread discord/injection.go
Comment thread discord/injection.go
@zerebos

zerebos commented Jul 18, 2026

Copy link
Copy Markdown
Member Author

/gemini review pls

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the BetterDiscord installer to target the Discord resources directory (containing app.asar) instead of the nested discord_desktop_core directory, updating both the Go backend and Svelte frontend. It introduces a new injection method that renames the original app.asar to betterdiscord.app.asar and shadows it with a custom app/ loader directory. The changes also include transactional rollbacks on failure, writability probes, process lock management, and improved path resolution across Windows, macOS, and Linux (including Flatpak). One high-severity issue was identified in the BrowseForDiscord controller, where manually browsed custom installations are ignored because ResolvePath is used instead of AddCustomPath.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread api/controller.go

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.

Comment thread discord/install.go
Comment thread discord/install.go
Comment thread discord/install.go
@zerebos

zerebos commented Jul 18, 2026

Copy link
Copy Markdown
Member Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the BetterDiscord installer to use a new injection method that shadows Discord's app.asar with a custom app/ loader directory, transitioning from tracking CorePath to ResourcesPath. It also adds explicit rejections for Snap installs, improves Flatpak path resolution, and enhances process management by waiting for Discord to fully exit before modifying files. The review feedback highlights three key areas for improvement: resolving a compilation failure on Windows caused by using os.Geteuid() in tests, mitigating a potential bricking risk in uninject() by restoring app.asar before deleting the shadow directory, and improving cross-platform path splitting robustness by utilizing filepath.ToSlash.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread discord/injection_test.go
Comment thread discord/injection.go Outdated
Comment thread discord/paths.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.

Comment thread discord/process.go Outdated
Comment thread discord/injection.go Outdated
@zerebos
zerebos requested a review from Copilot July 18, 2026 23:11
@zerebos

zerebos commented Jul 18, 2026

Copy link
Copy Markdown
Member Author

/gemini review more

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request transitions the BetterDiscord installer from injecting into discord_desktop_core to shadowing Discord's app.asar via a shadow app/ directory. It updates path resolution across all platforms to target the resources directory, introduces robust process management to stop Discord and release file locks before modification, and explicitly rejects unsupported Snap installs. The review feedback correctly identifies a compilation failure on Windows in the test suite due to the use of os.Geteuid(), which is not supported on Windows, and suggests a cross-platform alternative using os/user.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread discord/injection_test.go

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 1 comment.

Comment thread discord/process.go
@zerebos
zerebos merged commit c97d7ce into wails Jul 19, 2026
4 checks passed
@zerebos
zerebos deleted the app-asar-injection branch July 19, 2026 04:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants