This guide is the deep reference for engineers, packagers, and contributors who need to understand how Pake works internally — how the CLI compiles apps, how the Rust container behaves at runtime, the configuration surface, the security model, and how to extend or contribute to the project. If you only want to package a website, the User Guide is the right document; this one assumes you want to see the machinery.
Pake is a two-part system: a TypeScript CLI (bin/) that orchestrates builds, and a Rust/Tauri container (src-tauri/) that becomes the desktop app. Both live in one repository and ship together as the pake-cli npm package.
1. Executive Summary
| Attribute | Value |
|---|---|
| Project | Pake — turn any webpage into a desktop app |
| License | MIT |
| CLI package | pake-cli v3.11.7 (binary: pake) |
| Container crate | pake v3.11.7 (lib name: app_lib) |
| Frameworks | Tauri 2.x (Rust), Commander 14 (CLI), Rollup (CLI bundling) |
| Languages | TypeScript (CLI), Rust 2021 edition (container) |
| Min toolchain | Node ≥ 18 (22 recommended), Rust ≥ 1.85.0 |
| Platforms | macOS, Windows, Linux (incl. ARM64) |
| Webview | WebKit (macOS/Linux), WebView2 (Windows) |
What Pake actually is, architecturally: it is a code generator and compiler driver, not a runtime. The CLI takes a URL plus options, synthesizes a Tauri configuration, compiles a purpose-built Rust binary that wraps the URL in a system webview, and emits a platform installer. The compiled container is deliberately small and uniform — every Pake app shares the same shortcuts, menu, tray, download handling, and JavaScript-injection behavior.
Important
The Pake container does not bundle a browser engine. The wrapped site runs in the OS-provided webview. This is the source of Pake's headline advantage (≈5 MB vs ≈100 MB for Electron) and also the source of its main caveat: site behavior is subject to the host webview's capabilities and quirks.
2. Architecture
2.1 Two-stage pipeline
┌─────────────────────────── Build time (your machine / CI) ───────────────────────────┐
│ │
│ pake [url] [options] │
│ │ │
│ ▼ │
│ bin/cli.ts ──► getCliProgram() (Commander parses flags) │
│ │ │
│ ▼ │
│ options/index.ts ──► resolve name, identifier, icon (handleIcon, resolveIdentifier)│
│ │ │
│ ▼ │
│ BuilderProvider.create() ──► MacBuilder | WinBuilder | LinuxBuilder │
│ │ │
│ ▼ │
│ BaseBuilder.prepare() ──► ensure Rust, install deps (pnpm/npm), CN-mirror toggle │
│ │ │
│ ▼ │
│ BaseBuilder.buildAndCopy() ──► mergeConfig() writes src-tauri/.pake/tauri.conf.json │
│ │ then `tauri build -c .pake/tauri.conf.json` │
│ ▼ │
│ Rust/Tauri compiles src-tauri/ ──► installer copied to CWD as <name>.<type> │
│ │
└────────────────────────────────────────────────────────────────────────────────────────┘
│ produces
▼
┌─────────────────────────── Run time (end user's machine) ────────────────────────────┐
│ │
│ app_lib::run() (src-tauri/src/lib.rs) │
│ │ reads embedded pake.json + tauri.conf.json (include_str!) │
│ ▼ │
│ tauri::Builder ──► plugins ──► invoke_handler (8 IPC commands) ──► setup(): │
│ ├─ set_window() build the "pake" webview, inject 8 JS scripts │
│ ├─ set_system_tray() optional tray icon + menu │
│ ├─ set_global_shortcut() optional activation hotkey │
│ └─ set_app_menu() native menu (macOS only) │
│ │
└────────────────────────────────────────────────────────────────────────────────────────┘
2.2 Component inventory
| Layer | Path | Responsibility |
|---|---|---|
| CLI entry | bin/cli.ts |
Parse args, run the builder, handle errors |
| CLI program | bin/helpers/cli-program.ts |
Commander option definitions (authoritative flag list) |
| Option processing | bin/options/index.ts, bin/options/icon.ts |
Resolve name/identifier/icon |
| Builders | bin/builders/* |
Platform-specific build orchestration |
| Build environment | bin/builders/env.ts |
Package-manager detection, timeouts, CN-mirror, NO_STRIP logic |
| Config merge | bin/helpers/merge.ts, bin/helpers/tauriConfig.ts |
Translate CLI options → Tauri config |
| Rust toolchain | bin/helpers/rust.ts |
Detect/install Rust, set up environment |
| Utilities | bin/utils/* |
URL parsing, name normalization, icon conversion, shell exec |
| Container entry | src-tauri/src/main.rs, lib.rs |
Tauri app bootstrap |
| Window | src-tauri/src/app/window.rs |
Build the webview window, proxy, downloads, multi-window |
| Setup | src-tauri/src/app/setup.rs |
Tray, global shortcut |
| Menu | src-tauri/src/app/menu.rs |
Native menu + handlers (macOS) |
| IPC commands | src-tauri/src/app/invoke.rs |
The 8 backend commands |
| Config types | src-tauri/src/app/config.rs |
PakeConfig / WindowConfig structs |
| Utilities | src-tauri/src/util.rs |
Config loading, data dir, toast, download-message i18n, filename collision |
| Injected scripts | src-tauri/src/inject/*.js |
Behavior injected into the wrapped page |
| Capabilities | src-tauri/capabilities/default.json |
Tauri permission grants |
2.3 CLI build flow in detail
BaseBuilder.prepare() (bin/builders/BaseBuilder.ts) runs before any compilation:
ensureRustEnv()/checkRustInstalled()— if Rust is missing, prompt andinstallRust(). Non-interactive CI failures exit with code 1.configureCargoRegistry()— whenPAKE_USE_CN_MIRROR=1, copysrc-tauri/rust_proxy.tomltosrc-tauri/.cargo/config.toml(rsproxy.cn); otherwise remove the auto-generated mirror config.detectPackageManager()— preferpnpm; fall back tonpmif pnpm is absent or its major version doesn't match the pinnedpackageManagerfield inpackage.json.getInstallCommand()— install dependencies (--registry=https://registry.npmmirror.comunder CN mirror;--legacy-peer-depsfor npm).
BaseBuilder.buildAndCopy() then:
mergeConfig(url, options, tauriConfig)— writes the merged Tauri configuration intosrc-tauri/.pake/tauri.conf.json(the build never mutates the tracked source config).getBuildCommand()— runspnpm run build -c .pake/tauri.conf.json(orbuild:debugin debug mode) with thecli-buildCargo feature, addingmacos-proxyon macOS 14+ (Darwin ≥ 23).- Copies the resulting installer to
<name>.<type>in the working directory, optionally copies a standalone binary (--keep-binary), and on macOS optionally installs to/Applications(--install).
Note
The
cli-buildCargo feature changes which config the container embeds. Insrc-tauri/src/util.rs,get_pake_config()usesinclude_str!("../.pake/pake.json")undercli-build, versusinclude_str!("../pake.json")for a plain source build. This is why CLI builds read from.pake/and developer builds (pnpm run dev) read the tracked root config.
2.4 Architecture-to-target mapping
BaseBuilder.ARCH_MAPPINGS resolves logical targets to Rust triples:
| Platform | arm64 |
x64 |
universal |
|---|---|---|---|
macOS (darwin) |
aarch64-apple-darwin |
x86_64-apple-darwin |
universal-apple-darwin |
Windows (win32) |
aarch64-pc-windows-msvc |
x86_64-pc-windows-msvc |
— |
| Linux | aarch64-unknown-linux-gnu |
x86_64-unknown-linux-gnu |
— |
3. The Pake Container (Runtime Internals)
3.1 Bootstrap (lib.rs::run_app)
On launch the container:
- On Linux, sets
WEBKIT_DISABLE_DMABUF_RENDERER=1andWEBKIT_DISABLE_COMPOSITING_MODE=1(unless already set) to avoid common WebKitGTK rendering failures (white-screen bugs). - Loads the embedded
PakeConfigand TauriConfigviaget_pake_config(). - Registers plugins: window-state, oauth, http, shell, notification, opener. Adds
single-instanceonly whenmulti_instanceis false. - Registers the IPC
invoke_handler(see §3.2). - In
setup(): builds the main window, the tray, the global shortcut, and (macOS) the menu. - Handles window close: when
hide_on_closeis set,CloseRequestedis intercepted and the window is hidden instead of the app quitting.
3.2 IPC command surface
Tauri commands are neither CLI nor HTTP endpoints — they are IPC calls dispatched from page JavaScript (window.__TAURI__.invoke('name', args)) to Rust. The complete, authoritative surface is the tauri::generate_handler! macro in lib.rs. There are 8 commands, all defined in src-tauri/src/app/invoke.rs:
| Command | Signature | Purpose |
|---|---|---|
download_file |
(params: { url, filename, language? }) |
Download a URL to the OS Downloads folder, with localized toast notifications |
send_notification |
(params: { title, body, icon }) |
Show a native OS notification |
set_dock_badge |
(count: Option<i64>) |
Set the macOS dock badge to a count (1–99999), or clear |
increment_dock_badge |
() |
Increment the dock badge by 1 (clamped to 99999) |
set_dock_badge_label |
(label: Option<String>) |
Set a text badge (≤ 16 chars) |
clear_dock_badge |
() |
Clear the dock badge |
update_theme_mode |
(mode: String) |
Set window theme to dark/light (macOS) |
clear_cache_and_restart |
() |
Clear all browsing data and restart the app |
Note
Dock-badge commands are macOS-native (via
objc2/NSDockTile) on macOS and fall back toset_badge_counton the main window for other platforms.update_theme_modeis a no-op off macOS.
3.3 Window construction (window.rs::build_window)
This is the heart of the container. Each window is built from the first entry in PakeConfig.windows plus platform branches:
- URL type —
url_type: "web"parses the URL as a remote address;"local"treats it as a bundled file path. Any other value is a hard error. - Sizing — on Windows the configured pixel size is divided by the monitor scale factor (HiDPI correctness); other platforms use the raw size.
min_width/min_heightapply a minimum if set. - Title — macOS prefers an empty title; Windows/Linux fall back to the product name.
- Injection scripts — eight scripts are injected in a fixed order (see §3.5).
window.pakeConfigis injected first so later scripts can read it. - Browser args — Windows/Linux receive
--disable-blink-features=AutomationControlled(and Windows also disables several Edge OOUI features).--ignore-certificate-errorsand WASM flags (SharedArrayBuffer,--enable-unsafe-webgpu) are appended conditionally. - Proxy —
proxy_urlis parsed and applied viaproxy_url(); on Windows an explicit--proxy-server=arg is also emitted for http/socks5. - Downloads — an
on_downloadhandler redirects webview-initiated downloads (blob/data-URI/Content-Disposition) into the OS Downloads folder, independent of page CSP and the IPC channel. Filename collisions are resolved bycheck_file_or_append. - New windows — when
new_windowis enabled, anon_new_windowhandler opens popups as additional Pake windows (used for auth flows).
Caution
On macOS 26+, sharing the opener's
WKWebViewConfigurationtriggers duplicateWKScriptMessageHandlerregistrations and crashes the app (issue #1194).build_windowtherefore reuses only the opener's position/size on macOS, never the full feature set. Preserve this branch if you modify popup handling.
3.4 Tray, shortcut, and menu
set_system_tray(setup.rs) — builds a tray icon with a Show/Hide/Quit (and New Window in multi-window) menu. Left-click toggles window visibility. On Linux, fullscreen is re-applied on show to work around WM focus bugs.set_global_shortcut(setup.rs) — registers the activation hotkey viatauri-plugin-global-shortcut, with a 300 ms debounce. Invalid shortcuts log a warning and are skipped rather than failing the launch.set_app_menu(menu.rs, macOS only) — constructs the full native menu.handle_menu_clickdispatches menu items, most of which callwindow.eval(...)into the injected JavaScript (zoomIn(),zoomOut(),setZoom(),window.pakeFind?.open(),triggerPasteAsPlainText(), etc.).
3.5 The injection system
Eight scripts under src-tauri/src/inject/ are compiled into the binary with include_str! and injected into every wrapped page in this order (order matters):
| Order | Script | Role |
|---|---|---|
| 1 | (generated) window.pakeConfig |
Exposes the window config to the page |
| 2 | find.js |
In-page Find UI (window.pakeFind) |
| 3 | toast.js |
Toast notifications (window.pakeToast) — must register before Rust calls show_toast |
| 4 | fullscreen.js |
Fullscreen polyfill / opt-out handling |
| 5 | event.js |
Keyboard shortcuts and page events |
| 6 | style.js |
CSS customizations (ad removal, theming) |
| 7 | theme_refresh.js |
Dark/light theme synchronization |
| 8 | auth.js |
Authentication-flow helpers |
| 9 | custom.js |
User-supplied customizations |
User --inject files are layered on top of these built-in scripts. This is the supported mechanism for per-site customization (see §7.2).
4. Configuration Reference
4.1 Configuration model
Two JSON documents drive the container, both embedded at compile time:
pake.json— Pake-specific window and behavior config (deserialized intoPakeConfig).tauri.conf.json(+ platform variantstauri.{macos,linux,windows}.conf.json) — standard Tauri bundle/app config.
Important
The
pake.jsonandtauri.conf.jsontracked in the repo are the default "Weekly" template, not user configuration. When you run the CLI,mergeConfig()synthesizes a fresh config from your options intosrc-tauri/.pake/. Editing the root files only affectspnpm run devbuilds.
4.2 PakeConfig (src-tauri/src/app/config.rs)
| Field | Type | Meaning |
|---|---|---|
windows |
Vec<WindowConfig> |
Window definitions (first is the main window) |
user_agent |
PlatformSpecific<String> |
Per-platform UA string |
system_tray |
PlatformSpecific<bool> |
Per-platform tray default (macOS off, Win/Linux on) |
system_tray_path |
String |
Tray icon path (default icons/icon.png) |
proxy_url |
String |
Proxy for all requests |
multi_instance |
bool |
Allow multiple processes |
multi_window |
bool |
Allow multiple windows per process |
4.3 WindowConfig fields
| Field | Type | Default | CLI flag |
|---|---|---|---|
url / url_type |
String |
— | positional URL |
width / height |
f64 |
1200 / 780 | --width / --height |
min_width / min_height |
f64 |
0 | --min-width / --min-height |
zoom |
u32 |
100 | --zoom |
resizable |
bool |
true | — |
fullscreen |
bool |
false | --fullscreen |
maximize |
bool |
false | --maximize |
hide_title_bar |
bool |
false | --hide-title-bar |
always_on_top |
bool |
false | --always-on-top |
dark_mode |
bool |
false | --dark-mode |
disabled_web_shortcuts |
bool |
false | --disabled-web-shortcuts |
activation_shortcut |
String |
empty | --activation-shortcut |
hide_on_close |
bool |
macOS true | --hide-on-close |
incognito |
bool |
false | --incognito |
enable_wasm |
bool |
false | --wasm |
enable_drag_drop |
bool |
false | --enable-drag-drop |
new_window |
bool |
false | --new-window |
start_to_tray |
bool |
false | --start-to-tray |
force_internal_navigation |
bool |
false | --force-internal-navigation |
internal_url_regex |
String |
empty | --internal-url-regex |
enable_find |
bool |
false | --enable-find |
ignore_certificate_errors |
bool |
false | --ignore-certificate-errors |
title |
Option<String> |
— | --title |
4.4 Tauri config highlights (tauri.conf.json)
{
"productName": "Weekly",
"identifier": "com.pake.weekly",
"version": "3.11.7",
"app": {
"withGlobalTauri": true,
"trayIcon": { "iconPath": "png/weekly_512.png", "id": "pake-tray" },
"security": { "headers": {}, "csp": null }
},
"build": { "frontendDist": "../dist" }
}
| Cargo feature | Effect |
|---|---|
cli-build |
Read config from .pake/ (CLI builds) instead of tracked root files |
custom-protocol (default) |
Production asset protocol |
macos-proxy |
Enable proxy support on macOS 14+ |
The release profile (Cargo.toml) is tuned for size: panic = "abort", codegen-units = 1, lto = "thin", opt-level = "z", strip = true.
4.5 Build environment variables
| Variable | Where | Effect |
|---|---|---|
PAKE_USE_CN_MIRROR=1 |
CLI prepare | npmmirror registry + rsproxy.cn Cargo registry |
NO_STRIP=1 |
Linux AppImage build | Skip the linuxdeploy strip step (glibc 2.38+ fix; auto-retried) |
PAKE_CREATE_APP=1 |
macOS build/tests | Produce a .app bundle instead of a .dmg |
MACOSX_DEPLOYMENT_TARGET |
macOS build | Set by getBuildEnvironment() to 14.0; overridable for SDK pinning |
GITHUB_ACTIONS |
CLI | Switches name validation to non-interactive defaults |
5. Security Analysis
5.1 The trust boundary
Pake's most important security property is the relationship between the wrapped remote site and the Tauri backend.
// src-tauri/capabilities/default.json
{
"identifier": "pake-capability",
"webviews": ["pake"],
"remote": { "urls": ["https://*.*"] },
"permissions": [ "shell:allow-open", "notification:allow-notify", ... ]
}
Combined with "withGlobalTauri": true in tauri.conf.json, this means any HTTPS page loaded in the webview can reach window.__TAURI__ and invoke the 8 backend commands. The capability grant is deliberately broad (https://*.*) because Pake wraps arbitrary user-chosen sites.
Caution
Because the remote-site ACL is wide open to all HTTPS origins, a malicious or compromised page inside a Pake app can call
download_file,send_notification, theme/badge commands, andclear_cache_and_restart. Pake mitigates the blast radius by exposing a small, narrow command set (no arbitrary filesystem, no shell command execution beyondshell:allow-open), but operators packaging untrusted sites should understand this boundary. Only package sites you trust, and review the granted permissions before widening them.
5.2 Command-level safeguards
download_filewrites only into the OS Downloads directory; filenames are collision-checked (check_file_or_append), and au32::MAXoverflow guard prevents an infinite-loop DoS on pathological filenames (regression guard for issue #1183).- Badge commands clamp counts to 1–99999 and labels to 16 characters.
shell:allow-openpermits opening URLs/files via the OS handler but not arbitrary command execution.- DevTools (
core:webview:allow-internal-toggle-devtools) are only functional in debug builds (cfg!(debug_assertions)).
5.3 Network and TLS
--proxy-urlroutes all traffic through an HTTP/HTTPS/SOCKS5 proxy.--ignore-certificate-errorsdisables TLS validation — intended for intranet/dev servers with self-signed certificates only. Never ship a production app with this flag against a public site.- Automation-detection blink features are disabled by default to reduce bot-detection friction, not as a security feature.
5.4 macOS entitlements and permissions
By default the app requests no camera or microphone entitlement. --camera and --microphone add com.apple.security.device.camera / audio-input respectively. Grant these only for sites that need them; macOS prompts the user on first use.
5.5 Data isolation
Each app stores webview data under a per-app config directory (get_data_dir in util.rs, keyed by product name). --incognito disables persistence entirely. Distinct URL + name pairs produce distinct bundle identifiers, so two apps for the same site keep separate cookie jars.
6. Operations
6.1 Building for distribution
# Standard release build (current platform)
pake https://example.com --name Example
# macOS universal binary
pake https://example.com --name Example --multi-arch
# Linux deb + appimage (default), or specific format
pake https://example.com --name Example --targets rpm
# Keep a standalone binary alongside the installer
pake https://example.com --name Example --keep-binary
6.2 CI/CD via GitHub Actions
The repository ships a Build App With Pake CLI workflow and a reusable action (action.yml). Fork the repo, run the workflow with the same parameters as CLI options, and download artifacts. First runs take 10–15 min (cache warm-up); subsequent runs ≈5 min with a 400–600 MB cache.
6.3 Containerized builds
docker run --rm --privileged \
--device /dev/fuse --security-opt apparmor=unconfined \
-v $(pwd)/output:/output \
ghcr.io/tw93/pake:latest \
https://example.com --name MyApp --targets appimage
Warning
AppImage tooling requires
/dev/fuse; the container must run privileged (or be granted FUSE explicitly). Output files may beroot-owned —chownthem afterward.
6.4 First-build timing and tuning
| Platform | First build | Subsequent |
|---|---|---|
| macOS/Linux | 5–10 min | fast |
| Windows | 10–15 min | fast |
Install timeout is 900 s on Windows, 600 s elsewhere; build timeout is 900 s (bin/builders/env.ts). For slow networks in China, PAKE_USE_CN_MIRROR=1 switches to domestic mirrors.
7. Developer Internals
7.1 Repository structure
├── bin/ # CLI source (TypeScript, bundled by Rollup → dist/cli.js)
│ ├── builders/ # BaseBuilder + Mac/Win/Linux builders + env.ts
│ ├── helpers/ # cli-program, merge, rust, tauriConfig
│ ├── options/ # option/icon/logger processing
│ └── utils/ # url, name, icon, shell, validate, mirror, platform
├── src-tauri/ # Rust container
│ ├── src/
│ │ ├── app/ # config, invoke, menu, setup, window
│ │ ├── inject/ # 8 injected JS scripts
│ │ ├── lib.rs # app bootstrap (run_app)
│ │ ├── main.rs # thin entry → app_lib::run()
│ │ └── util.rs # config load, data dir, toast, i18n, filename guard
│ ├── capabilities/ # Tauri permission grants
│ ├── icons/ png/ # per-app icon assets
│ ├── pake.json # default PakeConfig template
│ └── tauri.*.conf.json # Tauri config (+ platform variants)
├── default_app_list.json # preset popular-app definitions
├── docs/ # cli-usage, advanced-usage, faq, github-actions (EN + CN)
├── scripts/ # release helpers
└── tests/ # Vitest unit/integration + build/release smoke tests
7.2 Customizing the container
Per the project's advanced-usage guidance:
- Restyle / remove ads: edit
src-tauri/src/inject/style.js, or pass a CSS file with--inject. - Add shortcuts / behavior: edit
src-tauri/src/inject/event.js, or inject a JS file. - Add a backend command: add a
#[tauri::command]ininvoke.rsand register it in thegenerate_handler!macro inlib.rs; call it from the page viawindow.__TAURI__.invoke('name', args).
7.3 Development workflow
git clone https://github.com/tw93/Pake.git && cd Pake
pnpm install
pnpm run dev # hot-reload container dev build
pnpm run build # release build
pnpm run build:debug # debug build (devtools enabled)
pnpm run cli:build # bundle the CLI (rollup) → dist/cli.js
For CLI hot-reload development, edit DEFAULT_DEV_PAKE_OPTIONS in bin/defaults.ts and run pnpm run cli:dev.
7.4 Testing
pnpm test # build CLI + Vitest + real build smoke + release smoke
pnpm test -- --no-build # skip the heavy real-build phase
npx vitest run # fast unit/integration suite only
node ./tests/release.js # release-workflow smoke test only
Test runner flags (tests/index.js): --no-unit, --no-integration, --no-builder, --no-build, --e2e, --pake-cli. The Vitest suite under tests/unit/ covers builders, CLI options, icon handling, name normalization, window-option merging, and identifier generation.
7.5 Code patterns worth knowing
- Platform abstraction via generics —
PlatformSpecific<T>with acfg-gatedget()selects the right value at compile time, avoiding runtime branching for per-OS config. - Atomic state — the dock badge uses
AtomicI64; multi-window indices useAtomicU32. - Graceful degradation — invalid shortcuts, missing tray icons, and download-dir resolution failures log and continue rather than aborting launch.
- i18n by locale sniffing —
get_download_message_with_langpicks Chinese vs English from the params language orLANG/LC_ALL/LC_MESSAGES/LANGUAGEenv vars.
8. Troubleshooting (Operational)
Error: feature 'edition2024' is required
Cause: Rust toolchain older than 1.85.0.
Solution: rustup update stable.
Prevention: Pin rust-toolchain.toml (already in repo: 1.85.0).
Error: Can't detect any appindicator library
Cause: Ubuntu 24.04 dropped libappindicator3-dev.
Solution: sudo apt-get install -y libayatana-appindicator3-dev.
Error: failed to run linuxdeploy / strip: Unable to recognise the format
Cause: linuxdeploy's strip tool can't parse the .relr.dyn section (glibc 2.38+).
Solution: Pake auto-retries with NO_STRIP=1; set it manually to skip from the start, or build --targets deb, or use Docker.
Error: Command timed out after 900000ms
Cause: First Windows build exceeded the install timeout (native compilation + AV scanning).
Solution: Retry; pre-warm with PAKE_USE_CN_MIRROR=1 in China; disable real-time AV temporarily.
Error: Panic in check_file_or_append
Cause: Historical crash (#1183) when a filename had no extension or a huge numeric suffix.
Status: Fixed — check_file_or_append handles extensionless files and guards against u32 overflow. Covered by unit tests in util.rs.
Error: macOS CoreFoundation / _Builtin_float module errors
Cause: macOS 26 beta SDK incompatibility.
Solution: Create src-tauri/.cargo/config.toml pinning MACOSX_DEPLOYMENT_TARGET and SDKROOT (see User Guide §10.7).
Linux white screen (Ubuntu 24.04 / GNOME)
Cause: WebKitGTK DMABUF/compositing issues; GNOME map-event focus timing (#1122).
Status: Mitigated — the container forces WEBKIT_DISABLE_DMABUF_RENDERER/WEBKIT_DISABLE_COMPOSITING_MODE and delays focus after map.
9. Known Limitations & Improvement Opportunities
Observed from the source and the community issue tracker:
- Embedded-webview sign-in. Google (and some providers) block OAuth inside embedded webviews regardless of
--new-window/--multi-window. This is provider policy and cannot be fixed in Pake; document it for users rather than attempting workarounds (#907, #1188). - Broad remote-site ACL.
remote.urls: ["https://*.*"]pluswithGlobalTauriis convenient but maximally permissive. A future hardening option could let packagers scope the ACL to the wrapped site's origin (the lencx/ChatGPT pattern uses a single-origin ACL). - Linux AppImage fragility. The glibc-2.38 strip incompatibility is handled by auto-retry, but AppImage remains the least reliable Linux target;
debis more stable. - pnpm version pinning. The CLI falls back to npm when the local pnpm major version differs from the pinned one — surfacing as a warning. Users on the wrong pnpm major silently get npm behavior; worth a clearer message (partially addressed via #1202).
- Single-window config model.
build_windowonly ever readswindows.first(); theVec<WindowConfig>is effectively single-entry today. Multi-distinct-window apps would need this generalized. - CSP is
nullby default. No content security policy is enforced on the wrapped site, relying entirely on the remote site's own headers. Acceptable for a generic wrapper, but worth flagging for security-sensitive packaging.
10. Quick Reference
| Need | Command / file |
|---|---|
| Authoritative CLI flag list | bin/helpers/cli-program.ts |
| CLI defaults | bin/defaults.ts |
| IPC command list | tauri::generate_handler! in src-tauri/src/lib.rs |
| Window behavior | src-tauri/src/app/window.rs |
| Permission grants | src-tauri/capabilities/default.json |
| Per-app config (CLI build) | src-tauri/.pake/{pake,tauri.conf}.json |
| Default config template | src-tauri/{pake,tauri.conf}.json |
| Dev build | pnpm run dev |
| Release build | pnpm run build |
| CLI bundle | pnpm run cli:build |
| Full test suite | pnpm test |
| CN mirror | PAKE_USE_CN_MIRROR=1 |
| AppImage strip fix | NO_STRIP=1 |