Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ dist/
# Editor
.vscode/
.idea/
.DS_Store
*.sw?
*.iml

Expand All @@ -31,6 +32,8 @@ debug/
target/
**/*.rs.bk
*.pdb
src-tauri/binaries/*
!src-tauri/binaries/.gitkeep

# ts-rs default output (real bindings go to src/lib/bindings/)
src-tauri/bindings/
Expand All @@ -39,6 +42,7 @@ src-tauri/src/lib/bindings/
# AI local settings
.claude/
.mcp.json
plans/

# Windows
nul
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[workspace]
members = ["src-tauri"]
members = ["src-tauri", "macos-patcher"]
resolver = "2"
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ The next-generation mod manager for League of Legends, built by the [League Tool
[![Releases](https://img.shields.io/github/v/release/LeagueToolkit/ltk-manager?style=for-the-badge)](https://github.com/LeagueToolkit/ltk-manager/releases)
[![License: MIT/Apache-2.0](https://img.shields.io/badge/License-MIT%2FApache--2.0-blue?style=for-the-badge)](https://github.com/LeagueToolkit/ltk-manager)
[![Windows 10+](https://img.shields.io/badge/Windows-10+-0078D4?style=for-the-badge&logo=windows)](https://www.microsoft.com/windows)
[![macOS 13+ ARM64](https://img.shields.io/badge/macOS-13%2B_ARM64-000000?style=for-the-badge&logo=apple)](docs/DEVELOPMENT.md#apple-silicon-development)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FLeagueToolkit%2Fltk-manager.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FLeagueToolkit%2Fltk-manager?ref=badge_shield)

---
Expand Down Expand Up @@ -40,7 +41,9 @@ The next-generation mod manager for League of Legends, built by the [League Tool

### Prerequisites

- **Windows 10 or 11** (64-bit). macOS and Linux support is planned.
- **Windows 10 or 11** (64-bit) for released installers.
- **macOS 13 or newer on Apple Silicon** for local source builds. Public macOS packages, Intel
support, notarization, and auto-updates are not currently provided.
- **League of Legends** — a valid game installation.

### Installation
Expand All @@ -56,6 +59,10 @@ The next-generation mod manager for League of Legends, built by the [League Tool
2. Drag and drop the file onto the LTK Manager window, or use the install button.
3. Enable the mod in your library and click **Run** to start the patcher.

Apple Silicon developers should use `pnpm macos:dev`, which builds the native ARM64 helper before
starting Tauri. See the [Apple Silicon development guide](docs/DEVELOPMENT.md#apple-silicon-development)
for the required local approval and preflight workflow.

---

## ⚖️ License & Reuse
Expand All @@ -75,6 +82,13 @@ If you are a developer looking to reuse this DLL in your own launcher or tool, y

For full terms, see [LICENSE-CSLOL.md](LICENSE-CSLOL.md).

### macOS helper

The native macOS patcher is a separate MIT-licensed process under `macos-patcher/`. Its ARM64
Mach-O scanner and WAD redirection mechanism are adapted from the separately MIT-licensed
`cslol-tools` subtree of cslol-manager. See `macos-patcher/NOTICE.md` for the pinned source commit
and attribution.

---

## ⚠️ Disclaimer
Expand Down
131 changes: 127 additions & 4 deletions docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,19 @@ This guide covers how to build LTK Manager from source and contribute to the pro
sudo apt-get install -y libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev
```

### Windows / macOS
### Windows

No additional system dependencies required beyond Rust and Node.js.

### macOS

- macOS 13 or newer
- Apple Silicon (`arm64`)
- Xcode Command Line Tools (`xcode-select --install`)
- SIP enabled

The initial native patcher does not support Intel Macs or an x86_64 League process under Rosetta.

## Getting Started

```bash
Expand All @@ -37,6 +46,119 @@ pnpm tauri dev
pnpm dev
```

## Apple Silicon Development

The macOS patcher is a separate executable. LTK Manager stays unprivileged; starting a patcher
session launches only the small helper through a macOS administrator approval prompt. The helper
connects back to an owner-only Unix socket, authenticates with a random session token, validates
the configured League bundle and overlay paths, and exits when the patcher stops.

### Build and run

```bash
pnpm install
pnpm macos:dev
```

`pnpm macos:dev` builds and ad-hoc signs
`src-tauri/binaries/ltk-macos-patcher-aarch64-apple-darwin`, then starts
`pnpm tauri dev --target aarch64-apple-darwin`.

For a local ARM64 application bundle:

```bash
pnpm macos:build
```

This produces `target/aarch64-apple-darwin/release/bundle/macos/LTK Manager.app`.
The build command ad-hoc signs the helper and final app bundle, then runs strict local signature
verification.
Developer ID signing, notarization, DMG packaging, updater artifacts, Intel builds, and universal
binaries are intentionally outside the local workflow.

### Configure League

LTK Manager accepts any of these selections and resolves them to one canonical installation:

- `/Applications/League of Legends.app`
- `League of Legends.app/Contents/LoL`
- `League of Legends.app/Contents/LoL/Game`
- A path inside `Game/LeagueofLegends.app`

Auto-detection checks common application locations and running `LeagueClient` or
`LeagueofLegends` process paths. `LTK_LEAGUE_PATH` can be set to override detection during local
development.

### Verify compatibility

Run a read-only dry scan before starting the patcher:

```bash
pnpm macos:preflight -- "/Applications/League of Legends.app"
```

A successful response reports `compatible`, architecture `arm64`, and the current signature ID.
The helper requires exactly one validated patch signature and fails before opening or writing
target process memory when the League build is unknown.

To inspect the installed executable directly:

```bash
file "/Applications/League of Legends.app/Contents/LoL/Game/LeagueofLegends.app/Contents/MacOS/LeagueofLegends"
```

The selected slice must be ARM64. Rosetta/x86_64 execution is not supported.

### End-to-end run

1. Keep SIP enabled.
2. Start LTK Manager with `pnpm macos:dev`.
3. Select or auto-detect the League application.
4. Install and enable a harmless, visually obvious test mod.
5. Run Diagnostics and confirm the native helper and ARM64 signature checks pass.
6. Click Run and approve the macOS administrator prompt for the helper.
7. Launch Practice Tool and verify the mod loads from the generated overlay.
8. Stop the patcher, launch another Practice Tool session, and verify unmodified assets are used.

Repeat the patched and unpatched cycle after every League update. A signature mismatch is a hard
compatibility failure and must not be bypassed.

### Stop, repair, and remove

The privilege mechanism is one-shot: no launch daemon or persistent root helper is installed.
Stopping the patcher sends a protocol stop request and waits for the elevated helper to exit.

Rebuild a missing or version-mismatched helper:

```bash
pnpm macos:helper
```

Remove local helper artifacts:

```bash
rm -f src-tauri/binaries/ltk-macos-patcher-aarch64-apple-darwin
cargo clean -p ltk-macos-patcher
```

If macOS App Translocation or quarantine prevents helper startup, move the locally built app to
`/Applications`, verify its source, and use the Diagnostics report before changing any extended
attributes. Do not disable SIP and do not run the full Tauri application with `sudo`.

### Patch maintenance

The patch-day smoke sequence is:

1. Rebuild the helper.
2. Run `pnpm macos:preflight`.
3. Build an overlay from the fixed harmless test mod.
4. Verify one patched Practice Tool launch.
5. Stop patching and verify one unmodified launch.
6. Repeat after restarting LTK Manager.

Update the versioned native signature only with an ARM64 executable fixture and a unique validated
match. The helper must continue to reject zero or multiple matches.

### Verbose Backend Logging

```bash
Expand All @@ -52,8 +174,8 @@ pnpm format # Prettier (auto-fix)
pnpm check # All three (typecheck + lint + format:check)

# Rust
cargo clippy -p ltk-manager
cargo fmt -p ltk-manager
cargo clippy --workspace --all-targets
cargo fmt --all
```

## Production Build
Expand Down Expand Up @@ -138,4 +260,5 @@ ltk-manager/
Logs are written to disk automatically and are useful for debugging:

- **Windows:** `%APPDATA%\dev.leaguetoolkit.manager\logs\ltk-manager.log`
- **Linux / macOS:** `~/.local/share/dev.leaguetoolkit.manager/logs/ltk-manager.log`
- **Linux:** `~/.local/share/dev.leaguetoolkit.manager/logs/ltk-manager.log`
- **macOS:** `~/Library/Logs/dev.leaguetoolkit.manager/ltk-manager.*.log`
17 changes: 17 additions & 0 deletions macos-patcher/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "ltk-macos-patcher"
version = "1.9.0"
edition = "2021"
license = "MIT"
publish = false

[dependencies]
libc = "0.2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"

[build-dependencies]
cc = "1"

[dev-dependencies]
tempfile = "3"
19 changes: 19 additions & 0 deletions macos-patcher/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright 2026 League Toolkit contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
10 changes: 10 additions & 0 deletions macos-patcher/NOTICE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Native patcher notice

The ARM64 Mach-O scanner and in-process WAD redirection mechanism are adapted
from the `cslol-tools` subtree of LeagueToolkit/cslol-manager, commit
`23f230858bc2359ce279e07ed129d482fe3b00bf`. That subtree carries an MIT
license. The protocol, validation, lifecycle, and privilege boundary in this
directory are specific to LTK Manager.

This helper is intentionally a separate process. It must not be linked into or
loaded by the Tauri WebView process.
15 changes: 15 additions & 0 deletions macos-patcher/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
fn main() {
println!("cargo:rerun-if-changed=native/patcher.cpp");
println!("cargo:rerun-if-changed=native/macho.hpp");

let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
if target_os == "macos" && target_arch == "aarch64" {
cc::Build::new()
.cpp(true)
.std("c++20")
.warnings(true)
.file("native/patcher.cpp")
.compile("ltk_macos_patch_engine");
}
}
Loading