# Pake Administrator & Developer Guide

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:

1. **`ensureRustEnv()` / `checkRustInstalled()`** — if Rust is missing, prompt and `installRust()`. Non-interactive CI failures exit with code 1.
2. **`configureCargoRegistry()`** — when `PAKE_USE_CN_MIRROR=1`, copy `src-tauri/rust_proxy.toml` to `src-tauri/.cargo/config.toml` (rsproxy.cn); otherwise remove the auto-generated mirror config.
3. **`detectPackageManager()`** — prefer `pnpm`; fall back to `npm` if pnpm is absent or its major version doesn't match the pinned `packageManager` field in `package.json`.
4. **`getInstallCommand()`** — install dependencies (`--registry=https://registry.npmmirror.com` under CN mirror; `--legacy-peer-deps` for npm).

`BaseBuilder.buildAndCopy()` then:

1. **`mergeConfig(url, options, tauriConfig)`** — writes the merged Tauri configuration into `src-tauri/.pake/tauri.conf.json` (the build never mutates the tracked source config).
2. **`getBuildCommand()`** — runs `pnpm run build -c .pake/tauri.conf.json` (or `build:debug` in debug mode) with the `cli-build` Cargo feature, adding `macos-proxy` on macOS 14+ (Darwin ≥ 23).
3. **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-build` Cargo feature changes which config the container embeds. In `src-tauri/src/util.rs`, `get_pake_config()` uses `include_str!("../.pake/pake.json")` under `cli-build`, versus `include_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:

1. On Linux, sets `WEBKIT_DISABLE_DMABUF_RENDERER=1` and `WEBKIT_DISABLE_COMPOSITING_MODE=1` (unless already set) to avoid common WebKitGTK rendering failures (white-screen bugs).
2. Loads the embedded `PakeConfig` and Tauri `Config` via `get_pake_config()`.
3. Registers plugins: window-state, oauth, http, shell, notification, opener. Adds `single-instance` **only when** `multi_instance` is false.
4. Registers the IPC `invoke_handler` (see §3.2).
5. In `setup()`: builds the main window, the tray, the global shortcut, and (macOS) the menu.
6. Handles window close: when `hide_on_close` is set, `CloseRequested` is 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 to `set_badge_count` on the main window for other platforms. `update_theme_mode` is 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_height` apply 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.pakeConfig` is 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-errors` and WASM flags (`SharedArrayBuffer`, `--enable-unsafe-webgpu`) are appended conditionally.
- **Proxy** — `proxy_url` is parsed and applied via `proxy_url()`; on Windows an explicit `--proxy-server=` arg is also emitted for http/socks5.
- **Downloads** — an `on_download` handler 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 by `check_file_or_append`.
- **New windows** — when `new_window` is enabled, an `on_new_window` handler opens popups as additional Pake windows (used for auth flows).

> [!CAUTION]
> On macOS 26+, sharing the opener's `WKWebViewConfiguration` triggers duplicate `WKScriptMessageHandler` registrations and crashes the app (issue #1194). `build_window` therefore 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 via `tauri-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_click` dispatches menu items, most of which call `window.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 into `PakeConfig`).
- **`tauri.conf.json`** (+ platform variants `tauri.{macos,linux,windows}.conf.json`) — standard Tauri bundle/app config.

> [!IMPORTANT]
> The `pake.json` and `tauri.conf.json` tracked in the repo are the **default "Weekly" template**, not user configuration. When you run the CLI, `mergeConfig()` synthesizes a fresh config from your options into `src-tauri/.pake/`. Editing the root files only affects `pnpm run dev` builds.

### 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`)

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

```json
// 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, and `clear_cache_and_restart`. Pake mitigates the blast radius by exposing a **small, narrow command set** (no arbitrary filesystem, no shell command execution beyond `shell: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_file` writes only into the OS Downloads directory; filenames are collision-checked (`check_file_or_append`), and a `u32::MAX` overflow 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-open` permits 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-url` routes all traffic through an HTTP/HTTPS/SOCKS5 proxy.
- `--ignore-certificate-errors` disables 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

```bash
# 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

```bash
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 be `root`-owned — `chown` them 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

```text
├── 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]` in `invoke.rs` and register it in the `generate_handler!` macro in `lib.rs`; call it from the page via `window.__TAURI__.invoke('name', args)`.

### 7.3 Development workflow

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

```bash
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 a `cfg`-gated `get()` selects the right value at compile time, avoiding runtime branching for per-OS config.
- **Atomic state** — the dock badge uses `AtomicI64`; multi-window indices use `AtomicU32`.
- **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_lang` picks Chinese vs English from the params language or `LANG`/`LC_ALL`/`LC_MESSAGES`/`LANGUAGE` env 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://*.*"]` plus `withGlobalTauri` is 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; `deb` is 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_window` only ever reads `windows.first()`; the `Vec<WindowConfig>` is effectively single-entry today. Multi-distinct-window apps would need this generalized.
- **CSP is `null` by 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` |

---

_Generated June 2026 from tw93/Pake at 35fc57d. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
