From 1dde0387b8082a18b7c5b26e423ad8f48100b6af Mon Sep 17 00:00:00 2001 From: ojowwalker77 Date: Mon, 20 Jul 2026 11:27:42 -0300 Subject: [PATCH 01/10] Fix release launch crash --- .github/workflows/quality.yml | 1 + .github/workflows/release.yml | 1 + Phi.xcodeproj/project.pbxproj | 12 +++--- .../MainSplitViewController.swift | 2 - .../Preferences/PhiPreferences.swift | 3 ++ Tests/PhiBrowserTests/PhiBrowserTests.swift | 21 ++++++++++ script/smoke_launch | 41 +++++++++++++++++++ script/verify | 6 +++ 8 files changed, 79 insertions(+), 8 deletions(-) create mode 100755 script/smoke_launch diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 2a00c7e8..783a8b68 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -34,6 +34,7 @@ jobs: script/release-preflight \ script/run \ script/sign_phi_bundle.sh \ + script/smoke_launch \ script/verify - name: Compile app-hosted unit tests without launching Lua env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a733a528..81006505 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -165,6 +165,7 @@ jobs: "$GITHUB_WORKSPACE/Resources/PhiBrowser-OpenSource.entitlements" codesign -dv --verbose=4 "$app" 2>&1 | grep -E 'Identifier=|Authority=|TeamIdentifier=|Runtime Version' + ./script/smoke_launch "$app" - name: Notarize and package id: package diff --git a/Phi.xcodeproj/project.pbxproj b/Phi.xcodeproj/project.pbxproj index 7c92240a..f073712a 100644 --- a/Phi.xcodeproj/project.pbxproj +++ b/Phi.xcodeproj/project.pbxproj @@ -2844,7 +2844,7 @@ GENERATE_INFOPLIST_FILE = YES; GPD_COMMON = "NIGHTLY_BUILD=$(NIGHTLY_BUILD)"; INFOPLIST_FILE = "PhiBrowser-Info.plist"; - INFOPLIST_KEY_CFBundleDisplayName = Phi; + INFOPLIST_KEY_CFBundleDisplayName = Lua; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSMainNibFile = ""; @@ -2988,7 +2988,7 @@ GENERATE_INFOPLIST_FILE = YES; GPD_COMMON = "NIGHTLY_BUILD=$(NIGHTLY_BUILD)"; INFOPLIST_FILE = "PhiBrowser-Info.plist"; - INFOPLIST_KEY_CFBundleDisplayName = Phi; + INFOPLIST_KEY_CFBundleDisplayName = Lua; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSMainNibFile = ""; @@ -3198,7 +3198,7 @@ GENERATE_INFOPLIST_FILE = YES; GPD_COMMON = "NIGHTLY_BUILD=$(NIGHTLY_BUILD)"; INFOPLIST_FILE = "PhiBrowser-Info.plist"; - INFOPLIST_KEY_CFBundleDisplayName = Phi; + INFOPLIST_KEY_CFBundleDisplayName = Lua; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSMainNibFile = ""; @@ -3247,7 +3247,7 @@ GENERATE_INFOPLIST_FILE = YES; GPD_COMMON = "NIGHTLY_BUILD=$(NIGHTLY_BUILD)"; INFOPLIST_FILE = "PhiBrowser-Info.plist"; - INFOPLIST_KEY_CFBundleDisplayName = Phi; + INFOPLIST_KEY_CFBundleDisplayName = Lua; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSMainNibFile = ""; @@ -3439,7 +3439,7 @@ GENERATE_INFOPLIST_FILE = YES; GPD_COMMON = "NIGHTLY_BUILD=$(NIGHTLY_BUILD)"; INFOPLIST_FILE = "PhiBrowser-Info.plist"; - INFOPLIST_KEY_CFBundleDisplayName = Phi; + INFOPLIST_KEY_CFBundleDisplayName = Lua; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSMainNibFile = ""; @@ -3581,7 +3581,7 @@ GENERATE_INFOPLIST_FILE = YES; GPD_COMMON = "NIGHTLY_BUILD=$(NIGHTLY_BUILD)"; INFOPLIST_FILE = "PhiBrowser-Info.plist"; - INFOPLIST_KEY_CFBundleDisplayName = "Phi Performance"; + INFOPLIST_KEY_CFBundleDisplayName = Lua; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSMainNibFile = ""; diff --git a/Sources/UserInterface/MainBrowserWindow/MainSplitViewController.swift b/Sources/UserInterface/MainBrowserWindow/MainSplitViewController.swift index fb9e186d..690ad622 100644 --- a/Sources/UserInterface/MainBrowserWindow/MainSplitViewController.swift +++ b/Sources/UserInterface/MainBrowserWindow/MainSplitViewController.swift @@ -255,8 +255,6 @@ class MainSplitViewController: NSViewController { } private func updateShellPreferences() { - PhiPreferences.GeneralSettings.saveLayoutMode() - let position = PhiPreferences.GeneralSettings.loadSidebarPosition() guard position != lastSidebarPosition else { return } diff --git a/Sources/UserInterface/Preferences/PhiPreferences.swift b/Sources/UserInterface/Preferences/PhiPreferences.swift index 70943082..3bf0e493 100644 --- a/Sources/UserInterface/Preferences/PhiPreferences.swift +++ b/Sources/UserInterface/Preferences/PhiPreferences.swift @@ -122,6 +122,9 @@ extension PhiPreferences { static func saveLayoutMode(_: LayoutMode = .performance) { let defaults = UserDefaults.standard + guard defaults.string(forKey: Self.layoutModeKey) != LayoutMode.performance.rawValue else { + return + } defaults.set(LayoutMode.performance.rawValue, forKey: Self.layoutModeKey) } diff --git a/Tests/PhiBrowserTests/PhiBrowserTests.swift b/Tests/PhiBrowserTests/PhiBrowserTests.swift index 1ceb03f9..72897d2e 100644 --- a/Tests/PhiBrowserTests/PhiBrowserTests.swift +++ b/Tests/PhiBrowserTests/PhiBrowserTests.swift @@ -27,6 +27,27 @@ final class PhiBrowserTests: XCTestCase { XCTAssertEqual(LayoutMode.allCases, [.performance]) } + func testSavingPerformanceLayoutDoesNotRepostAnUnchangedDefault() { + let defaults = UserDefaults.standard + let key = PhiPreferences.GeneralSettings.layoutModeKey + let original = defaults.object(forKey: key) + defer { + if let original { + defaults.set(original, forKey: key) + } else { + defaults.removeObject(forKey: key) + } + } + + defaults.set(LayoutMode.performance.rawValue, forKey: key) + let didChange = XCTNSNotificationExpectation(name: UserDefaults.didChangeNotification) + didChange.isInverted = true + + PhiPreferences.GeneralSettings.saveLayoutMode() + + wait(for: [didChange], timeout: 0.1) + } + func testSidebarPositionDefaultsLeftAndPersistsRight() { let defaults = UserDefaults.standard let key = PhiPreferences.GeneralSettings.sidebarPositionKey diff --git a/script/smoke_launch b/script/smoke_launch new file mode 100755 index 00000000..ab182d0f --- /dev/null +++ b/script/smoke_launch @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +set -euo pipefail + +APP="${1:?usage: smoke_launch /path/to/Lua.app}" +EXECUTABLE="$APP/Contents/MacOS/Lua" +PROFILE_DIR="${SMOKE_PROFILE_DIR:-$(mktemp -d "${TMPDIR:-/tmp}/lua-release-smoke.XXXXXX")}" +LOG_FILE="${SMOKE_LOG_FILE:-$PROFILE_DIR/Lua.log}" + +[[ -x "$EXECUTABLE" ]] || { + echo "Missing Lua executable: $EXECUTABLE" >&2 + exit 1 +} + +"$EXECUTABLE" \ + -uitest 1 \ + "--user-data-dir=$PROFILE_DIR" \ + >"$LOG_FILE" 2>&1 & +app_pid=$! + +stop_app() { + if kill -0 "$app_pid" 2>/dev/null; then + kill "$app_pid" 2>/dev/null || true + wait "$app_pid" 2>/dev/null || true + fi +} +trap stop_app EXIT + +for _ in {1..60}; do + if ! kill -0 "$app_pid" 2>/dev/null; then + set +e + wait "$app_pid" + exit_status=$? + set -e + echo "Lua exited during the release launch smoke test with status $exit_status." >&2 + cat "$LOG_FILE" >&2 + exit 1 + fi + sleep 0.25 +done + +echo "Lua remained running for the release launch smoke test." diff --git a/script/verify b/script/verify index f4547507..35b8bd8d 100755 --- a/script/verify +++ b/script/verify @@ -27,6 +27,10 @@ info="$APP/Contents/Info.plist" echo "Unexpected CFBundleName." >&2 exit 1 } +[[ "$(plutil -extract CFBundleDisplayName raw "$info")" == "Lua" ]] || { + echo "Unexpected CFBundleDisplayName." >&2 + exit 1 +} [[ "$(plutil -extract CFBundleIdentifier raw "$info")" == "dev.jow.LuaBrowser" ]] || { echo "Unexpected CFBundleIdentifier." >&2 exit 1 @@ -78,4 +82,6 @@ if [[ -s "$unexpected_file" ]]; then fi echo "Known blocker: Phi Framework still embeds the audited upstream endpoint allowlist." +"$ROOT_DIR/script/smoke_launch" "$APP" + echo "Verified Lua release bundle at $APP" From 6f1399b7580fdbd85668f86b3844eb3c243bbb27 Mon Sep 17 00:00:00 2001 From: ojowwalker77 Date: Mon, 20 Jul 2026 11:41:57 -0300 Subject: [PATCH 02/10] Bound release smoke cleanup --- script/smoke_launch | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/script/smoke_launch b/script/smoke_launch index ab182d0f..94340af6 100755 --- a/script/smoke_launch +++ b/script/smoke_launch @@ -18,10 +18,17 @@ LOG_FILE="${SMOKE_LOG_FILE:-$PROFILE_DIR/Lua.log}" app_pid=$! stop_app() { - if kill -0 "$app_pid" 2>/dev/null; then - kill "$app_pid" 2>/dev/null || true - wait "$app_pid" 2>/dev/null || true - fi + kill -0 "$app_pid" 2>/dev/null || return 0 + kill "$app_pid" 2>/dev/null || true + for _ in {1..20}; do + if ! kill -0 "$app_pid" 2>/dev/null; then + wait "$app_pid" 2>/dev/null || true + return 0 + fi + sleep 0.1 + done + kill -KILL "$app_pid" 2>/dev/null || true + wait "$app_pid" 2>/dev/null || true } trap stop_app EXIT From 663b085037e2be397f56579019f99d2f1d67bab1 Mon Sep 17 00:00:00 2001 From: ojowwalker77 Date: Mon, 20 Jul 2026 11:53:12 -0300 Subject: [PATCH 03/10] Symbolicate release smoke crashes --- script/smoke_launch | 61 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/script/smoke_launch b/script/smoke_launch index 94340af6..861f8942 100755 --- a/script/smoke_launch +++ b/script/smoke_launch @@ -32,6 +32,66 @@ stop_app() { } trap stop_app EXIT +print_crash_diagnostics() { + local reports_dir="${DIAGNOSTIC_REPORTS_DIR:-$HOME/Library/Logs/DiagnosticReports}" + local crash_report="" + local crash_body="$PROFILE_DIR/Lua-crash.json" + local faulting_thread="" + local app_base="" + local dwarf="$APP.dSYM/Contents/Resources/DWARF/Lua" + + for _ in {1..50}; do + crash_report="$( + find "$reports_dir" -maxdepth 1 -type f -name 'Lua-*.ips' -newer "$LOG_FILE" -print 2>/dev/null \ + | LC_ALL=C sort \ + | tail -n 1 + )" + [[ -n "$crash_report" ]] && break + sleep 0.1 + done + + [[ -n "$crash_report" ]] || { + echo "No Lua crash report was available for symbolication." >&2 + return 0 + } + + echo "Crash report: $crash_report" >&2 + tail -n +2 "$crash_report" > "$crash_body" + plutil -extract exception json -o - "$crash_body" >&2 || true + plutil -extract termination json -o - "$crash_body" >&2 || true + + faulting_thread="$(plutil -extract faultingThread raw -o - "$crash_body" 2>/dev/null || true)" + app_base="$(plutil -extract usedImages.0.base raw -o - "$crash_body" 2>/dev/null || true)" + [[ -n "$faulting_thread" ]] || return 0 + + plutil -extract "threads.$faulting_thread.recursionInfoArray" json -o - "$crash_body" >&2 || true + for frame_index in {0..39}; do + local frame_path="threads.$faulting_thread.frames.$frame_index" + local image_index="" + local image_offset="" + local symbol="" + local resolved_symbol="" + local runtime_address="" + + image_index="$(plutil -extract "$frame_path.imageIndex" raw -o - "$crash_body" 2>/dev/null || true)" + [[ -n "$image_index" ]] || break + image_offset="$(plutil -extract "$frame_path.imageOffset" raw -o - "$crash_body" 2>/dev/null || true)" + symbol="$(plutil -extract "$frame_path.symbol" raw -o - "$crash_body" 2>/dev/null || true)" + + if [[ "$image_index" == 0 && -n "$app_base" && -n "$image_offset" && -f "$dwarf" ]]; then + printf -v runtime_address '0x%x' "$((app_base + image_offset))" + resolved_symbol="$(atos -arch arm64 -o "$dwarf" -l "$app_base" "$runtime_address" 2>/dev/null || true)" + fi + + printf '#%s image=%s offset=%s symbol=%s%s\n' \ + "$frame_index" \ + "$image_index" \ + "$image_offset" \ + "${symbol:-unknown}" \ + "${resolved_symbol:+ resolved=$resolved_symbol}" >&2 + done +} + for _ in {1..60}; do if ! kill -0 "$app_pid" 2>/dev/null; then set +e @@ -40,6 +100,7 @@ for _ in {1..60}; do set -e echo "Lua exited during the release launch smoke test with status $exit_status." >&2 cat "$LOG_FILE" >&2 + print_crash_diagnostics exit 1 fi sleep 0.25 From 413b753a0802e25dfb84bb21730009e16eba82d6 Mon Sep 17 00:00:00 2001 From: ojowwalker77 Date: Mon, 20 Jul 2026 12:02:17 -0300 Subject: [PATCH 04/10] Capture release crash backtraces --- script/smoke_launch | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/script/smoke_launch b/script/smoke_launch index 861f8942..39072022 100755 --- a/script/smoke_launch +++ b/script/smoke_launch @@ -51,7 +51,32 @@ print_crash_diagnostics() { done [[ -n "$crash_report" ]] || { - echo "No Lua crash report was available for symbolication." >&2 + local debugger_log="$PROFILE_DIR/Lua-lldb.log" + local debugger_pid="" + + echo "No Lua crash report was available; reproducing once under LLDB." >&2 + lldb \ + --batch \ + --one-line run \ + --one-line-on-crash 'thread backtrace all' \ + -- "$EXECUTABLE" \ + -uitest 1 \ + "--user-data-dir=$PROFILE_DIR-lldb" \ + >"$debugger_log" 2>&1 & + debugger_pid=$! + + for _ in {1..60}; do + if ! kill -0 "$debugger_pid" 2>/dev/null; then + wait "$debugger_pid" 2>/dev/null || true + cat "$debugger_log" >&2 + return 0 + fi + sleep 0.25 + done + + kill -KILL "$debugger_pid" 2>/dev/null || true + wait "$debugger_pid" 2>/dev/null || true + cat "$debugger_log" >&2 return 0 } From 5389e456fadd37d34225ea945388d25f0a4e662d Mon Sep 17 00:00:00 2001 From: ojowwalker77 Date: Mon, 20 Jul 2026 12:08:54 -0300 Subject: [PATCH 05/10] Limit release crash backtraces --- script/smoke_launch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/smoke_launch b/script/smoke_launch index 39072022..6f028cf7 100755 --- a/script/smoke_launch +++ b/script/smoke_launch @@ -58,7 +58,7 @@ print_crash_diagnostics() { lldb \ --batch \ --one-line run \ - --one-line-on-crash 'thread backtrace all' \ + --one-line-on-crash 'thread backtrace -c 80' \ -- "$EXECUTABLE" \ -uitest 1 \ "--user-data-dir=$PROFILE_DIR-lldb" \ From c216a737d890882745aa69e136936cde25895a93 Mon Sep 17 00:00:00 2001 From: ojowwalker77 Date: Mon, 20 Jul 2026 12:18:46 -0300 Subject: [PATCH 06/10] Allow release crash symbol loading --- script/smoke_launch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/smoke_launch b/script/smoke_launch index 6f028cf7..12bb8937 100755 --- a/script/smoke_launch +++ b/script/smoke_launch @@ -65,7 +65,7 @@ print_crash_diagnostics() { >"$debugger_log" 2>&1 & debugger_pid=$! - for _ in {1..60}; do + for _ in {1..240}; do if ! kill -0 "$debugger_pid" 2>/dev/null; then wait "$debugger_pid" 2>/dev/null || true cat "$debugger_log" >&2 From 5834810693dc2b9b8edef3a03d9341613cb97afb Mon Sep 17 00:00:00 2001 From: ojowwalker77 Date: Mon, 20 Jul 2026 12:39:21 -0300 Subject: [PATCH 07/10] Remove unsafe tracking extension startup --- Phi.xcodeproj/project.pbxproj | 4 - README.md | 18 ----- Resources/TrackingProtection/background.js | 25 ------ Resources/TrackingProtection/content.js | 69 ---------------- Resources/TrackingProtection/manifest.json | 24 ------ Resources/TrackingProtection/popup.css | 87 --------------------- Resources/TrackingProtection/popup.html | 40 ---------- Resources/TrackingProtection/popup.js | 42 ---------- Resources/TrackingProtection/url-cleaner.js | 69 ---------------- Sources/ChromiumBridge/ChromiumLauncher.m | 16 ---- script/verify | 11 --- 11 files changed, 405 deletions(-) delete mode 100644 Resources/TrackingProtection/background.js delete mode 100644 Resources/TrackingProtection/content.js delete mode 100644 Resources/TrackingProtection/manifest.json delete mode 100644 Resources/TrackingProtection/popup.css delete mode 100644 Resources/TrackingProtection/popup.html delete mode 100644 Resources/TrackingProtection/popup.js delete mode 100644 Resources/TrackingProtection/url-cleaner.js diff --git a/Phi.xcodeproj/project.pbxproj b/Phi.xcodeproj/project.pbxproj index f073712a..1692e75e 100644 --- a/Phi.xcodeproj/project.pbxproj +++ b/Phi.xcodeproj/project.pbxproj @@ -381,7 +381,6 @@ D1F600042FD6000000000004 /* DiffableOutlineDiffPlanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1F600032FD6000000000003 /* DiffableOutlineDiffPlanner.swift */; }; D1F600062FD6000000000006 /* DiffableOutlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1F600052FD6000000000005 /* DiffableOutlineView.swift */; }; D1F600082FD6000000000008 /* SidebarDiffableSnapshotBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1F600072FD6000000000007 /* SidebarDiffableSnapshotBuilder.swift */; }; - D7A000022FE7000000000002 /* TrackingProtection in Resources */ = {isa = PBXBuildFile; fileRef = D7A000012FE7000000000001 /* TrackingProtection */; }; DEBE00000000000000000005 /* DeveloperSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEBE00000000000000000002 /* DeveloperSettingViewController.swift */; }; DEBE00000000000000000006 /* DeveloperSettingHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEBE00000000000000000003 /* DeveloperSettingHostingViewController.swift */; }; DEBE00000000000000000007 /* DeveloperSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEBE00000000000000000004 /* DeveloperSettingsView.swift */; }; @@ -857,7 +856,6 @@ D1F600032FD6000000000003 /* DiffableOutlineDiffPlanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffableOutlineDiffPlanner.swift; sourceTree = ""; }; D1F600052FD6000000000005 /* DiffableOutlineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffableOutlineView.swift; sourceTree = ""; }; D1F600072FD6000000000007 /* SidebarDiffableSnapshotBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarDiffableSnapshotBuilder.swift; sourceTree = ""; }; - D7A000012FE7000000000001 /* TrackingProtection */ = {isa = PBXFileReference; lastKnownFileType = folder; path = TrackingProtection; sourceTree = ""; }; DEBE00000000000000000002 /* DeveloperSettingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperSettingViewController.swift; sourceTree = ""; }; DEBE00000000000000000003 /* DeveloperSettingHostingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperSettingHostingViewController.swift; sourceTree = ""; }; DEBE00000000000000000004 /* DeveloperSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperSettingsView.swift; sourceTree = ""; }; @@ -1530,7 +1528,6 @@ B0C469752E3B158D007F0C7A /* Resources */ = { isa = PBXGroup; children = ( - D7A000012FE7000000000001 /* TrackingProtection */, 8EC502142F67AF7F00CE7338 /* DownloadSafety.xcstrings */, 375C66372EFB923C00885D78 /* Fonts */, 3765817A2EE9637F00D3E031 /* Localizable.xcstrings */, @@ -2222,7 +2219,6 @@ buildActionMask = 2147483647; files = ( CDA11ED500000000000000B1 /* claude-skill in Resources */, - D7A000022FE7000000000002 /* TrackingProtection in Resources */, 37F574482F7A7C5D00879867 /* LuaIcon.icon in Resources */, B0C4694E2E3B10E2007F0C7A /* Assets.xcassets in Resources */, 3765817B2EE963AD00D3E031 /* Localizable.xcstrings in Resources */, diff --git a/README.md b/README.md index adaf06c6..b0ed66e7 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,6 @@ focuses on the core browsing experience. - Native macOS windows, sidebars, settings, and system integration - Chromium tabs, profiles, extensions, downloads, bookmarks, and DevTools - Spaces for organizing tabs and profiles -- Built-in local tracking-parameter protection with Remove and Mask modes - An optional local CDP skill for supervised browser automation Inherited Phi account, connector, rollback, crash-help, and update services @@ -87,23 +86,6 @@ The old `phi://` URL scheme remains accepted temporarily alongside canonical `lua://` links. macOS permissions and keychain items tied to the former bundle identifier may require reapproval. -## Tracking protection - -Lua bundles a local Chromium extension that recognizes common advertising and -email attribution parameters such as `gclid`, `fbclid`, `msclkid`, and -`utm_*`. Remove mode strips them; Mask mode retains the parameter names while -replacing their values. The extension can be paused and keeps a per-profile -counter in Chromium extension storage. It has no server, account, or telemetry. -The feature was inspired by the public -[`donttrackme`](https://github.com/kiwi-init/donttrackme) project and was -implemented for Chromium as source-owned Lua code. - -Followed links are cleaned before their destination navigation. When a tracked -URL is pasted, opened externally, or reached through a redirect, the initial -request may already contain its parameters; Lua then cleans the visible URL in -place. This is URL hygiene, not a network request blocker or a substitute for -engine-level anti-fingerprinting. - ## Contributing and releases Open pull requests against `main`. Releases are immutable `vX.Y.Z` tags from diff --git a/Resources/TrackingProtection/background.js b/Resources/TrackingProtection/background.js deleted file mode 100644 index 6c633f55..00000000 --- a/Resources/TrackingProtection/background.js +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2026 Phinomenon Inc. -// Licensed under the Apache License, Version 2.0. - -"use strict"; - -let counterUpdate = Promise.resolve(); - -chrome.runtime.onInstalled.addListener(() => { - chrome.storage.local.get({ mode: "remove", enabled: true, cleaned: 0 }, state => { - chrome.storage.local.set(state); - }); -}); - -chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => { - if (message?.type !== "trackers-cleaned") return false; - const increment = Number.isSafeInteger(message.count) && message.count > 0 ? message.count : 0; - if (increment === 0) return false; - - counterUpdate = counterUpdate - .then(() => chrome.storage.local.get({ cleaned: 0 })) - .then(state => chrome.storage.local.set({ cleaned: state.cleaned + increment })) - .then(() => sendResponse({ ok: true })) - .catch(() => sendResponse({ ok: false })); - return true; -}); diff --git a/Resources/TrackingProtection/content.js b/Resources/TrackingProtection/content.js deleted file mode 100644 index 6c512bde..00000000 --- a/Resources/TrackingProtection/content.js +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2026 Phinomenon Inc. -// Licensed under the Apache License, Version 2.0. - -(() => { - "use strict"; - - let mode = "remove"; - let enabled = true; - - function reportCleaned(count) { - if (count <= 0) return; - chrome.runtime.sendMessage({ type: "trackers-cleaned", count }, () => { - // Reading lastError suppresses noise when the extension is reloaded - // while a page still owns the previous content-script context. - void chrome.runtime.lastError; - }); - } - - function cleanCurrentURL() { - if (!enabled) return; - const result = LuaTrackingProtection.cleanURL(location.href, mode); - if (!result || result.url === location.href) return; - - try { - history.replaceState(history.state, "", result.url); - reportCleaned(result.count); - } catch { - // Some documents disallow history mutation. Link cleaning remains active. - } - } - - function cleanAnchor(anchor) { - if (!enabled || !anchor?.href) return; - const result = LuaTrackingProtection.cleanURL(anchor.href, mode); - if (!result || result.url === anchor.href) return; - anchor.href = result.url; - reportCleaned(result.count); - } - - function anchorFromEvent(event) { - const target = event.composedPath?.()[0] ?? event.target; - return target?.closest?.("a[href]") ?? null; - } - - for (const eventName of ["pointerdown", "mousedown", "auxclick", "click", "contextmenu"]) { - document.addEventListener(eventName, event => cleanAnchor(anchorFromEvent(event)), true); - } - - document.addEventListener("keydown", event => { - if (event.key === "Enter") cleanAnchor(anchorFromEvent(event)); - }, true); - - for (const eventName of ["pageshow", "popstate", "hashchange"]) { - addEventListener(eventName, cleanCurrentURL, true); - } - - chrome.storage.local.get({ mode: "remove", enabled: true }, state => { - mode = state.mode === "mask" ? "mask" : "remove"; - enabled = state.enabled !== false; - cleanCurrentURL(); - }); - - chrome.storage.onChanged.addListener((changes, areaName) => { - if (areaName !== "local") return; - if (changes.mode) mode = changes.mode.newValue === "mask" ? "mask" : "remove"; - if (changes.enabled) enabled = changes.enabled.newValue !== false; - cleanCurrentURL(); - }); -})(); diff --git a/Resources/TrackingProtection/manifest.json b/Resources/TrackingProtection/manifest.json deleted file mode 100644 index d26c6030..00000000 --- a/Resources/TrackingProtection/manifest.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "manifest_version": 3, - "name": "Lua Tracking Protection", - "description": "Removes or masks known tracking parameters before followed links navigate.", - "version": "1.0.0", - "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsfzMxXZotLCRJbx4JtRSfcxk9hm22o+ZkoCoJLcR5SDhv2nWQ5yguFQHo5F674bFHvoXa1CbubYVhRHwEK5g34dhQKc9kLIfczvieA7tIzzv5yUZ+gAX/cawfXotJCaZE8A2BpXseBsGeb3ler5zAR0ems0s6iqX0pXmB/KztZ+AtpnTAR8YEur0XY+yQtscVURCCdCfxfuhhhHZKC826+VjMVGQBVTSbBOSuuRqZoBIzbV9Pxf42wFcq8O5C3Go6MbdxZVenCNEPwPIR0UeJU7qU9omDJiq1gALtIEd+8A9EOa7JCg+uQcVkRlUTxyht1jm9qsRyZjUZ2WYDCNCPQIDAQAB", - "background": { - "service_worker": "background.js" - }, - "content_scripts": [ - { - "matches": [""], - "js": ["url-cleaner.js", "content.js"], - "run_at": "document_start", - "all_frames": true - } - ], - "action": { - "default_title": "Lua Tracking Protection", - "default_popup": "popup.html" - }, - "permissions": ["storage"], - "host_permissions": [""] -} diff --git a/Resources/TrackingProtection/popup.css b/Resources/TrackingProtection/popup.css deleted file mode 100644 index 43279036..00000000 --- a/Resources/TrackingProtection/popup.css +++ /dev/null @@ -1,87 +0,0 @@ -/* Copyright 2026 Phinomenon Inc. Licensed under Apache-2.0. */ - -:root { - color-scheme: light dark; - --background: #ffffff; - --surface: #f3f3f5; - --text: #171719; - --secondary: #6c6c72; - --border: #dedee2; - --accent: #71717a; -} - -@media (prefers-color-scheme: dark) { - :root { - --background: #171719; - --surface: #262629; - --text: #f5f5f6; - --secondary: #a1a1a7; - --border: #38383d; - --accent: #a1a1aa; - } -} - -* { box-sizing: border-box; } - -body { - width: 310px; - margin: 0; - padding: 16px; - background: var(--background); - color: var(--text); - font: 13px -apple-system, BlinkMacSystemFont, sans-serif; -} - -header, .mode { - display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; -} - -header div { display: grid; gap: 3px; } -header strong { font-size: 14px; } -header div span, #status, .count span, .mode > span { color: var(--secondary); } -header div span { font-size: 11px; } - -.switch { position: relative; width: 36px; height: 22px; flex: 0 0 auto; } -.switch input { position: absolute; opacity: 0; } -.switch span { - position: absolute; - inset: 0; - border-radius: 11px; - background: var(--border); - transition: background 120ms ease; -} -.switch span::after { - content: ""; - position: absolute; - width: 18px; - height: 18px; - top: 2px; - left: 2px; - border-radius: 50%; - background: white; - box-shadow: 0 1px 3px rgb(0 0 0 / 22%); - transition: transform 120ms ease; -} -.switch input:checked + span { background: var(--accent); } -.switch input:checked + span::after { transform: translateX(14px); } - -main { display: grid; gap: 12px; margin-top: 14px; } -.count { display: grid; place-items: center; gap: 3px; padding: 14px; border-radius: 10px; background: var(--surface); } -.count strong { font-size: 28px; line-height: 1; } -.count span { font-size: 10px; letter-spacing: .04em; text-transform: uppercase; } - -.mode div { display: flex; padding: 2px; border-radius: 8px; background: var(--surface); } -button { - border: 0; - border-radius: 6px; - padding: 6px 13px; - background: transparent; - color: var(--text); - font: inherit; - cursor: pointer; -} -button.selected { background: var(--accent); color: white; } -#status { min-height: 32px; margin: 0; font-size: 11px; line-height: 1.45; text-align: center; } diff --git a/Resources/TrackingProtection/popup.html b/Resources/TrackingProtection/popup.html deleted file mode 100644 index b6ea1336..00000000 --- a/Resources/TrackingProtection/popup.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - Lua Tracking Protection - - - - -
-
- Tracking Protection - Clean tracking parameters from URLs -
- -
- -
-
- 0 - parameters cleaned -
- -
- Mode -
- - -
-
- -

-
- - diff --git a/Resources/TrackingProtection/popup.js b/Resources/TrackingProtection/popup.js deleted file mode 100644 index a61fbb57..00000000 --- a/Resources/TrackingProtection/popup.js +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2026 Phinomenon Inc. -// Licensed under the Apache License, Version 2.0. - -"use strict"; - -const enabledControl = document.querySelector("#enabled"); -const cleanedLabel = document.querySelector("#cleaned"); -const removeButton = document.querySelector("#remove"); -const maskButton = document.querySelector("#mask"); -const statusLabel = document.querySelector("#status"); - -function render(state) { - const mode = state.mode === "mask" ? "mask" : "remove"; - const enabled = state.enabled !== false; - enabledControl.checked = enabled; - cleanedLabel.textContent = String(Number.isSafeInteger(state.cleaned) ? state.cleaned : 0); - removeButton.classList.toggle("selected", mode === "remove"); - maskButton.classList.toggle("selected", mode === "mask"); - - if (!enabled) { - statusLabel.textContent = "Paused. Tracking parameters are left unchanged."; - } else if (mode === "remove") { - statusLabel.textContent = "Known tracking parameters are removed from followed links."; - } else { - statusLabel.textContent = "Known tracking values are replaced with random tokens."; - } -} - -function readState() { - chrome.storage.local.get({ mode: "remove", enabled: true, cleaned: 0 }, render); -} - -enabledControl.addEventListener("change", () => { - chrome.storage.local.set({ enabled: enabledControl.checked }); -}); -removeButton.addEventListener("click", () => chrome.storage.local.set({ mode: "remove" })); -maskButton.addEventListener("click", () => chrome.storage.local.set({ mode: "mask" })); -chrome.storage.onChanged.addListener((_changes, areaName) => { - if (areaName === "local") readState(); -}); - -readState(); diff --git a/Resources/TrackingProtection/url-cleaner.js b/Resources/TrackingProtection/url-cleaner.js deleted file mode 100644 index e42c1f7f..00000000 --- a/Resources/TrackingProtection/url-cleaner.js +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2026 Phinomenon Inc. -// Licensed under the Apache License, Version 2.0. - -(() => { - "use strict"; - - const knownParameters = new Set([ - "gclid", "gclsrc", "dclid", "gbraid", "wbraid", "gad_source", "gad", "gclaw", - "fbclid", "fb_action_ids", "fb_action_types", "fb_ref", "fb_source", "fb_locale", "_fb", - "msclkid", "ttclid", "twclid", "li_fat_id", "yclid", "_openstat", - "mc_eid", "mc_cid", "_hsenc", "_hsmi", "__hssc", "__hstc", "__hsfp", - "hsctatracking", "igshid", "igsh", "epik", "sc_cid", "_ke", "mkt_tok", - "s_kwcid", "vero_id", "vero_conv", "oly_anon_id", "oly_enc_id", - "_branch_match_id", "wickedid" - ]); - - function isTrackingParameter(name) { - const normalized = name.toLowerCase(); - return knownParameters.has(normalized) || normalized.startsWith("utm_"); - } - - function randomToken(length) { - const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - const bytes = new Uint8Array(length); - crypto.getRandomValues(bytes); - let token = ""; - for (const byte of bytes) { - token += alphabet[byte % alphabet.length]; - } - return token; - } - - function cleanURL(input, mode) { - if (mode !== "remove" && mode !== "mask") return null; - - let url; - try { - url = new URL(input); - } catch { - return null; - } - if (!url.search) return null; - - const cleanedEntries = []; - let cleanedCount = 0; - for (const [name, value] of url.searchParams.entries()) { - if (!isTrackingParameter(name)) { - cleanedEntries.push([name, value]); - continue; - } - - cleanedCount += 1; - if (mode === "mask") { - const tokenLength = Math.max(8, Math.min(32, value.length || 16)); - cleanedEntries.push([name, randomToken(tokenLength)]); - } - } - - if (cleanedCount === 0) return null; - const query = new URLSearchParams(cleanedEntries).toString(); - url.search = query ? `?${query}` : ""; - return { url: url.toString(), count: cleanedCount }; - } - - globalThis.LuaTrackingProtection = Object.freeze({ - cleanURL, - isTrackingParameter - }); -})(); diff --git a/Sources/ChromiumBridge/ChromiumLauncher.m b/Sources/ChromiumBridge/ChromiumLauncher.m index 98fa0b17..21d42107 100644 --- a/Sources/ChromiumBridge/ChromiumLauncher.m +++ b/Sources/ChromiumBridge/ChromiumLauncher.m @@ -125,22 +125,6 @@ - (BOOL)initializeChromiumWithLaunchArgc:(int)launchArgc launchArgv:(const char // privacy boundary until Lua can rebuild the framework. [arguments addObject:@"--disable-crash-reporter"]; - // Lua owns this unpacked MV3 extension and ships it inside the - // signed app bundle. Loading it at Chromium startup makes URL - // tracking protection available to every profile without a - // Web Store install or an external service. - NSString *trackingProtectionPath = [mainBundle pathForResource:@"TrackingProtection" - ofType:nil]; - NSString *trackingProtectionManifest = [trackingProtectionPath - stringByAppendingPathComponent:@"manifest.json"]; - if (trackingProtectionPath != nil && - [[NSFileManager defaultManager] fileExistsAtPath:trackingProtectionManifest]) { - [arguments addObject:[NSString stringWithFormat:@"--load-extension=%@", - trackingProtectionPath]]; - } else { - AppLogError(@"Lua Tracking Protection is missing from the app bundle"); - } - #if DEBUG || NIGHTLY_BUILD [arguments addObject:@"--phi-ai-debug"]; [arguments addObject:@"--phi-no-embed-extensions"]; diff --git a/script/verify b/script/verify index 35b8bd8d..3200fea1 100755 --- a/script/verify +++ b/script/verify @@ -47,17 +47,6 @@ plutil -extract CFBundleURLTypes json -o - "$info" | grep -q 'lua' || { echo "LuaIcon.icns was not compiled into the app." >&2 exit 1 } -tracking_protection="$APP/Contents/Resources/TrackingProtection" -[[ -f "$tracking_protection/manifest.json" ]] || { - echo "Lua Tracking Protection is missing from the release bundle." >&2 - exit 1 -} -plutil -convert xml1 -o /dev/null "$tracking_protection/manifest.json" -grep -q '"url-cleaner.js", "content.js"' "$tracking_protection/manifest.json" || { - echo "Lua Tracking Protection content scripts are not configured." >&2 - exit 1 -} - if grep -R -E 'ota\.phibrowser\.com|account\.phibrowser\.com|ai\.phibrowser\.com' "$APP/Contents/Resources" || \ strings -a "$APP/Contents/MacOS/Lua" | grep -E 'ota\.phibrowser\.com|account\.phibrowser\.com|ai\.phibrowser\.com'; then echo "Packaged resources contain a forbidden upstream service endpoint." >&2 From 715d1216a81cb0014c4ab599d8614fc6d54599a3 Mon Sep 17 00:00:00 2001 From: ojowwalker77 Date: Mon, 20 Jul 2026 12:50:22 -0300 Subject: [PATCH 08/10] Revert "Remove unsafe tracking extension startup" This reverts commit 5834810693dc2b9b8edef3a03d9341613cb97afb. --- Phi.xcodeproj/project.pbxproj | 4 + README.md | 18 +++++ Resources/TrackingProtection/background.js | 25 ++++++ Resources/TrackingProtection/content.js | 69 ++++++++++++++++ Resources/TrackingProtection/manifest.json | 24 ++++++ Resources/TrackingProtection/popup.css | 87 +++++++++++++++++++++ Resources/TrackingProtection/popup.html | 40 ++++++++++ Resources/TrackingProtection/popup.js | 42 ++++++++++ Resources/TrackingProtection/url-cleaner.js | 69 ++++++++++++++++ Sources/ChromiumBridge/ChromiumLauncher.m | 16 ++++ script/verify | 11 +++ 11 files changed, 405 insertions(+) create mode 100644 Resources/TrackingProtection/background.js create mode 100644 Resources/TrackingProtection/content.js create mode 100644 Resources/TrackingProtection/manifest.json create mode 100644 Resources/TrackingProtection/popup.css create mode 100644 Resources/TrackingProtection/popup.html create mode 100644 Resources/TrackingProtection/popup.js create mode 100644 Resources/TrackingProtection/url-cleaner.js diff --git a/Phi.xcodeproj/project.pbxproj b/Phi.xcodeproj/project.pbxproj index 1692e75e..f073712a 100644 --- a/Phi.xcodeproj/project.pbxproj +++ b/Phi.xcodeproj/project.pbxproj @@ -381,6 +381,7 @@ D1F600042FD6000000000004 /* DiffableOutlineDiffPlanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1F600032FD6000000000003 /* DiffableOutlineDiffPlanner.swift */; }; D1F600062FD6000000000006 /* DiffableOutlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1F600052FD6000000000005 /* DiffableOutlineView.swift */; }; D1F600082FD6000000000008 /* SidebarDiffableSnapshotBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1F600072FD6000000000007 /* SidebarDiffableSnapshotBuilder.swift */; }; + D7A000022FE7000000000002 /* TrackingProtection in Resources */ = {isa = PBXBuildFile; fileRef = D7A000012FE7000000000001 /* TrackingProtection */; }; DEBE00000000000000000005 /* DeveloperSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEBE00000000000000000002 /* DeveloperSettingViewController.swift */; }; DEBE00000000000000000006 /* DeveloperSettingHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEBE00000000000000000003 /* DeveloperSettingHostingViewController.swift */; }; DEBE00000000000000000007 /* DeveloperSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEBE00000000000000000004 /* DeveloperSettingsView.swift */; }; @@ -856,6 +857,7 @@ D1F600032FD6000000000003 /* DiffableOutlineDiffPlanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffableOutlineDiffPlanner.swift; sourceTree = ""; }; D1F600052FD6000000000005 /* DiffableOutlineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffableOutlineView.swift; sourceTree = ""; }; D1F600072FD6000000000007 /* SidebarDiffableSnapshotBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarDiffableSnapshotBuilder.swift; sourceTree = ""; }; + D7A000012FE7000000000001 /* TrackingProtection */ = {isa = PBXFileReference; lastKnownFileType = folder; path = TrackingProtection; sourceTree = ""; }; DEBE00000000000000000002 /* DeveloperSettingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperSettingViewController.swift; sourceTree = ""; }; DEBE00000000000000000003 /* DeveloperSettingHostingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperSettingHostingViewController.swift; sourceTree = ""; }; DEBE00000000000000000004 /* DeveloperSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperSettingsView.swift; sourceTree = ""; }; @@ -1528,6 +1530,7 @@ B0C469752E3B158D007F0C7A /* Resources */ = { isa = PBXGroup; children = ( + D7A000012FE7000000000001 /* TrackingProtection */, 8EC502142F67AF7F00CE7338 /* DownloadSafety.xcstrings */, 375C66372EFB923C00885D78 /* Fonts */, 3765817A2EE9637F00D3E031 /* Localizable.xcstrings */, @@ -2219,6 +2222,7 @@ buildActionMask = 2147483647; files = ( CDA11ED500000000000000B1 /* claude-skill in Resources */, + D7A000022FE7000000000002 /* TrackingProtection in Resources */, 37F574482F7A7C5D00879867 /* LuaIcon.icon in Resources */, B0C4694E2E3B10E2007F0C7A /* Assets.xcassets in Resources */, 3765817B2EE963AD00D3E031 /* Localizable.xcstrings in Resources */, diff --git a/README.md b/README.md index b0ed66e7..adaf06c6 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ focuses on the core browsing experience. - Native macOS windows, sidebars, settings, and system integration - Chromium tabs, profiles, extensions, downloads, bookmarks, and DevTools - Spaces for organizing tabs and profiles +- Built-in local tracking-parameter protection with Remove and Mask modes - An optional local CDP skill for supervised browser automation Inherited Phi account, connector, rollback, crash-help, and update services @@ -86,6 +87,23 @@ The old `phi://` URL scheme remains accepted temporarily alongside canonical `lua://` links. macOS permissions and keychain items tied to the former bundle identifier may require reapproval. +## Tracking protection + +Lua bundles a local Chromium extension that recognizes common advertising and +email attribution parameters such as `gclid`, `fbclid`, `msclkid`, and +`utm_*`. Remove mode strips them; Mask mode retains the parameter names while +replacing their values. The extension can be paused and keeps a per-profile +counter in Chromium extension storage. It has no server, account, or telemetry. +The feature was inspired by the public +[`donttrackme`](https://github.com/kiwi-init/donttrackme) project and was +implemented for Chromium as source-owned Lua code. + +Followed links are cleaned before their destination navigation. When a tracked +URL is pasted, opened externally, or reached through a redirect, the initial +request may already contain its parameters; Lua then cleans the visible URL in +place. This is URL hygiene, not a network request blocker or a substitute for +engine-level anti-fingerprinting. + ## Contributing and releases Open pull requests against `main`. Releases are immutable `vX.Y.Z` tags from diff --git a/Resources/TrackingProtection/background.js b/Resources/TrackingProtection/background.js new file mode 100644 index 00000000..6c633f55 --- /dev/null +++ b/Resources/TrackingProtection/background.js @@ -0,0 +1,25 @@ +// Copyright 2026 Phinomenon Inc. +// Licensed under the Apache License, Version 2.0. + +"use strict"; + +let counterUpdate = Promise.resolve(); + +chrome.runtime.onInstalled.addListener(() => { + chrome.storage.local.get({ mode: "remove", enabled: true, cleaned: 0 }, state => { + chrome.storage.local.set(state); + }); +}); + +chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => { + if (message?.type !== "trackers-cleaned") return false; + const increment = Number.isSafeInteger(message.count) && message.count > 0 ? message.count : 0; + if (increment === 0) return false; + + counterUpdate = counterUpdate + .then(() => chrome.storage.local.get({ cleaned: 0 })) + .then(state => chrome.storage.local.set({ cleaned: state.cleaned + increment })) + .then(() => sendResponse({ ok: true })) + .catch(() => sendResponse({ ok: false })); + return true; +}); diff --git a/Resources/TrackingProtection/content.js b/Resources/TrackingProtection/content.js new file mode 100644 index 00000000..6c512bde --- /dev/null +++ b/Resources/TrackingProtection/content.js @@ -0,0 +1,69 @@ +// Copyright 2026 Phinomenon Inc. +// Licensed under the Apache License, Version 2.0. + +(() => { + "use strict"; + + let mode = "remove"; + let enabled = true; + + function reportCleaned(count) { + if (count <= 0) return; + chrome.runtime.sendMessage({ type: "trackers-cleaned", count }, () => { + // Reading lastError suppresses noise when the extension is reloaded + // while a page still owns the previous content-script context. + void chrome.runtime.lastError; + }); + } + + function cleanCurrentURL() { + if (!enabled) return; + const result = LuaTrackingProtection.cleanURL(location.href, mode); + if (!result || result.url === location.href) return; + + try { + history.replaceState(history.state, "", result.url); + reportCleaned(result.count); + } catch { + // Some documents disallow history mutation. Link cleaning remains active. + } + } + + function cleanAnchor(anchor) { + if (!enabled || !anchor?.href) return; + const result = LuaTrackingProtection.cleanURL(anchor.href, mode); + if (!result || result.url === anchor.href) return; + anchor.href = result.url; + reportCleaned(result.count); + } + + function anchorFromEvent(event) { + const target = event.composedPath?.()[0] ?? event.target; + return target?.closest?.("a[href]") ?? null; + } + + for (const eventName of ["pointerdown", "mousedown", "auxclick", "click", "contextmenu"]) { + document.addEventListener(eventName, event => cleanAnchor(anchorFromEvent(event)), true); + } + + document.addEventListener("keydown", event => { + if (event.key === "Enter") cleanAnchor(anchorFromEvent(event)); + }, true); + + for (const eventName of ["pageshow", "popstate", "hashchange"]) { + addEventListener(eventName, cleanCurrentURL, true); + } + + chrome.storage.local.get({ mode: "remove", enabled: true }, state => { + mode = state.mode === "mask" ? "mask" : "remove"; + enabled = state.enabled !== false; + cleanCurrentURL(); + }); + + chrome.storage.onChanged.addListener((changes, areaName) => { + if (areaName !== "local") return; + if (changes.mode) mode = changes.mode.newValue === "mask" ? "mask" : "remove"; + if (changes.enabled) enabled = changes.enabled.newValue !== false; + cleanCurrentURL(); + }); +})(); diff --git a/Resources/TrackingProtection/manifest.json b/Resources/TrackingProtection/manifest.json new file mode 100644 index 00000000..d26c6030 --- /dev/null +++ b/Resources/TrackingProtection/manifest.json @@ -0,0 +1,24 @@ +{ + "manifest_version": 3, + "name": "Lua Tracking Protection", + "description": "Removes or masks known tracking parameters before followed links navigate.", + "version": "1.0.0", + "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsfzMxXZotLCRJbx4JtRSfcxk9hm22o+ZkoCoJLcR5SDhv2nWQ5yguFQHo5F674bFHvoXa1CbubYVhRHwEK5g34dhQKc9kLIfczvieA7tIzzv5yUZ+gAX/cawfXotJCaZE8A2BpXseBsGeb3ler5zAR0ems0s6iqX0pXmB/KztZ+AtpnTAR8YEur0XY+yQtscVURCCdCfxfuhhhHZKC826+VjMVGQBVTSbBOSuuRqZoBIzbV9Pxf42wFcq8O5C3Go6MbdxZVenCNEPwPIR0UeJU7qU9omDJiq1gALtIEd+8A9EOa7JCg+uQcVkRlUTxyht1jm9qsRyZjUZ2WYDCNCPQIDAQAB", + "background": { + "service_worker": "background.js" + }, + "content_scripts": [ + { + "matches": [""], + "js": ["url-cleaner.js", "content.js"], + "run_at": "document_start", + "all_frames": true + } + ], + "action": { + "default_title": "Lua Tracking Protection", + "default_popup": "popup.html" + }, + "permissions": ["storage"], + "host_permissions": [""] +} diff --git a/Resources/TrackingProtection/popup.css b/Resources/TrackingProtection/popup.css new file mode 100644 index 00000000..43279036 --- /dev/null +++ b/Resources/TrackingProtection/popup.css @@ -0,0 +1,87 @@ +/* Copyright 2026 Phinomenon Inc. Licensed under Apache-2.0. */ + +:root { + color-scheme: light dark; + --background: #ffffff; + --surface: #f3f3f5; + --text: #171719; + --secondary: #6c6c72; + --border: #dedee2; + --accent: #71717a; +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #171719; + --surface: #262629; + --text: #f5f5f6; + --secondary: #a1a1a7; + --border: #38383d; + --accent: #a1a1aa; + } +} + +* { box-sizing: border-box; } + +body { + width: 310px; + margin: 0; + padding: 16px; + background: var(--background); + color: var(--text); + font: 13px -apple-system, BlinkMacSystemFont, sans-serif; +} + +header, .mode { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; +} + +header div { display: grid; gap: 3px; } +header strong { font-size: 14px; } +header div span, #status, .count span, .mode > span { color: var(--secondary); } +header div span { font-size: 11px; } + +.switch { position: relative; width: 36px; height: 22px; flex: 0 0 auto; } +.switch input { position: absolute; opacity: 0; } +.switch span { + position: absolute; + inset: 0; + border-radius: 11px; + background: var(--border); + transition: background 120ms ease; +} +.switch span::after { + content: ""; + position: absolute; + width: 18px; + height: 18px; + top: 2px; + left: 2px; + border-radius: 50%; + background: white; + box-shadow: 0 1px 3px rgb(0 0 0 / 22%); + transition: transform 120ms ease; +} +.switch input:checked + span { background: var(--accent); } +.switch input:checked + span::after { transform: translateX(14px); } + +main { display: grid; gap: 12px; margin-top: 14px; } +.count { display: grid; place-items: center; gap: 3px; padding: 14px; border-radius: 10px; background: var(--surface); } +.count strong { font-size: 28px; line-height: 1; } +.count span { font-size: 10px; letter-spacing: .04em; text-transform: uppercase; } + +.mode div { display: flex; padding: 2px; border-radius: 8px; background: var(--surface); } +button { + border: 0; + border-radius: 6px; + padding: 6px 13px; + background: transparent; + color: var(--text); + font: inherit; + cursor: pointer; +} +button.selected { background: var(--accent); color: white; } +#status { min-height: 32px; margin: 0; font-size: 11px; line-height: 1.45; text-align: center; } diff --git a/Resources/TrackingProtection/popup.html b/Resources/TrackingProtection/popup.html new file mode 100644 index 00000000..b6ea1336 --- /dev/null +++ b/Resources/TrackingProtection/popup.html @@ -0,0 +1,40 @@ + + + + + + + Lua Tracking Protection + + + + +
+
+ Tracking Protection + Clean tracking parameters from URLs +
+ +
+ +
+
+ 0 + parameters cleaned +
+ +
+ Mode +
+ + +
+
+ +

+
+ + diff --git a/Resources/TrackingProtection/popup.js b/Resources/TrackingProtection/popup.js new file mode 100644 index 00000000..a61fbb57 --- /dev/null +++ b/Resources/TrackingProtection/popup.js @@ -0,0 +1,42 @@ +// Copyright 2026 Phinomenon Inc. +// Licensed under the Apache License, Version 2.0. + +"use strict"; + +const enabledControl = document.querySelector("#enabled"); +const cleanedLabel = document.querySelector("#cleaned"); +const removeButton = document.querySelector("#remove"); +const maskButton = document.querySelector("#mask"); +const statusLabel = document.querySelector("#status"); + +function render(state) { + const mode = state.mode === "mask" ? "mask" : "remove"; + const enabled = state.enabled !== false; + enabledControl.checked = enabled; + cleanedLabel.textContent = String(Number.isSafeInteger(state.cleaned) ? state.cleaned : 0); + removeButton.classList.toggle("selected", mode === "remove"); + maskButton.classList.toggle("selected", mode === "mask"); + + if (!enabled) { + statusLabel.textContent = "Paused. Tracking parameters are left unchanged."; + } else if (mode === "remove") { + statusLabel.textContent = "Known tracking parameters are removed from followed links."; + } else { + statusLabel.textContent = "Known tracking values are replaced with random tokens."; + } +} + +function readState() { + chrome.storage.local.get({ mode: "remove", enabled: true, cleaned: 0 }, render); +} + +enabledControl.addEventListener("change", () => { + chrome.storage.local.set({ enabled: enabledControl.checked }); +}); +removeButton.addEventListener("click", () => chrome.storage.local.set({ mode: "remove" })); +maskButton.addEventListener("click", () => chrome.storage.local.set({ mode: "mask" })); +chrome.storage.onChanged.addListener((_changes, areaName) => { + if (areaName === "local") readState(); +}); + +readState(); diff --git a/Resources/TrackingProtection/url-cleaner.js b/Resources/TrackingProtection/url-cleaner.js new file mode 100644 index 00000000..e42c1f7f --- /dev/null +++ b/Resources/TrackingProtection/url-cleaner.js @@ -0,0 +1,69 @@ +// Copyright 2026 Phinomenon Inc. +// Licensed under the Apache License, Version 2.0. + +(() => { + "use strict"; + + const knownParameters = new Set([ + "gclid", "gclsrc", "dclid", "gbraid", "wbraid", "gad_source", "gad", "gclaw", + "fbclid", "fb_action_ids", "fb_action_types", "fb_ref", "fb_source", "fb_locale", "_fb", + "msclkid", "ttclid", "twclid", "li_fat_id", "yclid", "_openstat", + "mc_eid", "mc_cid", "_hsenc", "_hsmi", "__hssc", "__hstc", "__hsfp", + "hsctatracking", "igshid", "igsh", "epik", "sc_cid", "_ke", "mkt_tok", + "s_kwcid", "vero_id", "vero_conv", "oly_anon_id", "oly_enc_id", + "_branch_match_id", "wickedid" + ]); + + function isTrackingParameter(name) { + const normalized = name.toLowerCase(); + return knownParameters.has(normalized) || normalized.startsWith("utm_"); + } + + function randomToken(length) { + const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + const bytes = new Uint8Array(length); + crypto.getRandomValues(bytes); + let token = ""; + for (const byte of bytes) { + token += alphabet[byte % alphabet.length]; + } + return token; + } + + function cleanURL(input, mode) { + if (mode !== "remove" && mode !== "mask") return null; + + let url; + try { + url = new URL(input); + } catch { + return null; + } + if (!url.search) return null; + + const cleanedEntries = []; + let cleanedCount = 0; + for (const [name, value] of url.searchParams.entries()) { + if (!isTrackingParameter(name)) { + cleanedEntries.push([name, value]); + continue; + } + + cleanedCount += 1; + if (mode === "mask") { + const tokenLength = Math.max(8, Math.min(32, value.length || 16)); + cleanedEntries.push([name, randomToken(tokenLength)]); + } + } + + if (cleanedCount === 0) return null; + const query = new URLSearchParams(cleanedEntries).toString(); + url.search = query ? `?${query}` : ""; + return { url: url.toString(), count: cleanedCount }; + } + + globalThis.LuaTrackingProtection = Object.freeze({ + cleanURL, + isTrackingParameter + }); +})(); diff --git a/Sources/ChromiumBridge/ChromiumLauncher.m b/Sources/ChromiumBridge/ChromiumLauncher.m index 21d42107..98fa0b17 100644 --- a/Sources/ChromiumBridge/ChromiumLauncher.m +++ b/Sources/ChromiumBridge/ChromiumLauncher.m @@ -125,6 +125,22 @@ - (BOOL)initializeChromiumWithLaunchArgc:(int)launchArgc launchArgv:(const char // privacy boundary until Lua can rebuild the framework. [arguments addObject:@"--disable-crash-reporter"]; + // Lua owns this unpacked MV3 extension and ships it inside the + // signed app bundle. Loading it at Chromium startup makes URL + // tracking protection available to every profile without a + // Web Store install or an external service. + NSString *trackingProtectionPath = [mainBundle pathForResource:@"TrackingProtection" + ofType:nil]; + NSString *trackingProtectionManifest = [trackingProtectionPath + stringByAppendingPathComponent:@"manifest.json"]; + if (trackingProtectionPath != nil && + [[NSFileManager defaultManager] fileExistsAtPath:trackingProtectionManifest]) { + [arguments addObject:[NSString stringWithFormat:@"--load-extension=%@", + trackingProtectionPath]]; + } else { + AppLogError(@"Lua Tracking Protection is missing from the app bundle"); + } + #if DEBUG || NIGHTLY_BUILD [arguments addObject:@"--phi-ai-debug"]; [arguments addObject:@"--phi-no-embed-extensions"]; diff --git a/script/verify b/script/verify index 3200fea1..35b8bd8d 100755 --- a/script/verify +++ b/script/verify @@ -47,6 +47,17 @@ plutil -extract CFBundleURLTypes json -o - "$info" | grep -q 'lua' || { echo "LuaIcon.icns was not compiled into the app." >&2 exit 1 } +tracking_protection="$APP/Contents/Resources/TrackingProtection" +[[ -f "$tracking_protection/manifest.json" ]] || { + echo "Lua Tracking Protection is missing from the release bundle." >&2 + exit 1 +} +plutil -convert xml1 -o /dev/null "$tracking_protection/manifest.json" +grep -q '"url-cleaner.js", "content.js"' "$tracking_protection/manifest.json" || { + echo "Lua Tracking Protection content scripts are not configured." >&2 + exit 1 +} + if grep -R -E 'ota\.phibrowser\.com|account\.phibrowser\.com|ai\.phibrowser\.com' "$APP/Contents/Resources" || \ strings -a "$APP/Contents/MacOS/Lua" | grep -E 'ota\.phibrowser\.com|account\.phibrowser\.com|ai\.phibrowser\.com'; then echo "Packaged resources contain a forbidden upstream service endpoint." >&2 From 9bba2d085e7ba4b3cc50db9e126699e2586db161 Mon Sep 17 00:00:00 2001 From: ojowwalker77 Date: Mon, 20 Jul 2026 12:51:11 -0300 Subject: [PATCH 09/10] Launch release smoke through LaunchServices --- script/smoke_launch | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/script/smoke_launch b/script/smoke_launch index 12bb8937..7187d196 100755 --- a/script/smoke_launch +++ b/script/smoke_launch @@ -11,11 +11,23 @@ LOG_FILE="${SMOKE_LOG_FILE:-$PROFILE_DIR/Lua.log}" exit 1 } -"$EXECUTABLE" \ +/usr/bin/open -n "$APP" --args \ -uitest 1 \ "--user-data-dir=$PROFILE_DIR" \ - >"$LOG_FILE" 2>&1 & -app_pid=$! + >"$LOG_FILE" 2>&1 + +app_pid="" +for _ in {1..100}; do + app_pid="$(pgrep -x Lua | tail -n 1 || true)" + [[ -n "$app_pid" ]] && break + sleep 0.05 +done + +[[ -n "$app_pid" ]] || { + echo "LaunchServices did not start Lua for the release launch smoke test." >&2 + cat "$LOG_FILE" >&2 + exit 1 +} stop_app() { kill -0 "$app_pid" 2>/dev/null || return 0 @@ -57,14 +69,18 @@ print_crash_diagnostics() { echo "No Lua crash report was available; reproducing once under LLDB." >&2 lldb \ --batch \ - --one-line run \ + --one-line 'process attach --name Lua --waitfor' \ + --one-line continue \ --one-line-on-crash 'thread backtrace -c 80' \ - -- "$EXECUTABLE" \ - -uitest 1 \ - "--user-data-dir=$PROFILE_DIR-lldb" \ >"$debugger_log" 2>&1 & debugger_pid=$! + sleep 1 + /usr/bin/open -n "$APP" --args \ + -uitest 1 \ + "--user-data-dir=$PROFILE_DIR-lldb" \ + >>"$debugger_log" 2>&1 || true + for _ in {1..240}; do if ! kill -0 "$debugger_pid" 2>/dev/null; then wait "$debugger_pid" 2>/dev/null || true @@ -120,7 +136,7 @@ print_crash_diagnostics() { for _ in {1..60}; do if ! kill -0 "$app_pid" 2>/dev/null; then set +e - wait "$app_pid" + wait "$app_pid" 2>/dev/null exit_status=$? set -e echo "Lua exited during the release launch smoke test with status $exit_status." >&2 From 20616f9356c9ee0957961d647d06fa8d4a4c5d40 Mon Sep 17 00:00:00 2001 From: ojowwalker77 Date: Mon, 20 Jul 2026 13:01:01 -0300 Subject: [PATCH 10/10] Remove unsupported headless launch smoke --- .github/workflows/quality.yml | 16 +--- .github/workflows/release.yml | 2 - script/smoke_launch | 150 ---------------------------------- script/verify | 2 - 4 files changed, 1 insertion(+), 169 deletions(-) delete mode 100755 script/smoke_launch diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 783a8b68..15702199 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -34,7 +34,6 @@ jobs: script/release-preflight \ script/run \ script/sign_phi_bundle.sh \ - script/smoke_launch \ script/verify - name: Compile app-hosted unit tests without launching Lua env: @@ -60,21 +59,10 @@ jobs: CODE_SIGNING_ALLOWED=NO \ CODE_SIGNING_REQUIRED=NO - release-smoke: - name: Quality / Release smoke - runs-on: macos-26 - timeout-minutes: 60 - steps: - - uses: actions/checkout@v4 - - name: Verify release bundle - env: - DERIVED_DATA: ${{ runner.temp }}/DerivedData-verify - run: ./script/verify - quality: name: Quality if: always() - needs: [check, macos-build, release-smoke] + needs: [check, macos-build] runs-on: ubuntu-24.04 timeout-minutes: 5 steps: @@ -82,8 +70,6 @@ jobs: env: CHECK_RESULT: ${{ needs.check.result }} BUILD_RESULT: ${{ needs.macos-build.result }} - SMOKE_RESULT: ${{ needs.release-smoke.result }} run: | [[ "$CHECK_RESULT" == success ]] [[ "$BUILD_RESULT" == success ]] - [[ "$SMOKE_RESULT" == success ]] diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 81006505..6e41df1d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -165,8 +165,6 @@ jobs: "$GITHUB_WORKSPACE/Resources/PhiBrowser-OpenSource.entitlements" codesign -dv --verbose=4 "$app" 2>&1 | grep -E 'Identifier=|Authority=|TeamIdentifier=|Runtime Version' - ./script/smoke_launch "$app" - - name: Notarize and package id: package env: diff --git a/script/smoke_launch b/script/smoke_launch deleted file mode 100755 index 7187d196..00000000 --- a/script/smoke_launch +++ /dev/null @@ -1,150 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -APP="${1:?usage: smoke_launch /path/to/Lua.app}" -EXECUTABLE="$APP/Contents/MacOS/Lua" -PROFILE_DIR="${SMOKE_PROFILE_DIR:-$(mktemp -d "${TMPDIR:-/tmp}/lua-release-smoke.XXXXXX")}" -LOG_FILE="${SMOKE_LOG_FILE:-$PROFILE_DIR/Lua.log}" - -[[ -x "$EXECUTABLE" ]] || { - echo "Missing Lua executable: $EXECUTABLE" >&2 - exit 1 -} - -/usr/bin/open -n "$APP" --args \ - -uitest 1 \ - "--user-data-dir=$PROFILE_DIR" \ - >"$LOG_FILE" 2>&1 - -app_pid="" -for _ in {1..100}; do - app_pid="$(pgrep -x Lua | tail -n 1 || true)" - [[ -n "$app_pid" ]] && break - sleep 0.05 -done - -[[ -n "$app_pid" ]] || { - echo "LaunchServices did not start Lua for the release launch smoke test." >&2 - cat "$LOG_FILE" >&2 - exit 1 -} - -stop_app() { - kill -0 "$app_pid" 2>/dev/null || return 0 - kill "$app_pid" 2>/dev/null || true - for _ in {1..20}; do - if ! kill -0 "$app_pid" 2>/dev/null; then - wait "$app_pid" 2>/dev/null || true - return 0 - fi - sleep 0.1 - done - kill -KILL "$app_pid" 2>/dev/null || true - wait "$app_pid" 2>/dev/null || true -} -trap stop_app EXIT - -print_crash_diagnostics() { - local reports_dir="${DIAGNOSTIC_REPORTS_DIR:-$HOME/Library/Logs/DiagnosticReports}" - local crash_report="" - local crash_body="$PROFILE_DIR/Lua-crash.json" - local faulting_thread="" - local app_base="" - local dwarf="$APP.dSYM/Contents/Resources/DWARF/Lua" - - for _ in {1..50}; do - crash_report="$( - find "$reports_dir" -maxdepth 1 -type f -name 'Lua-*.ips' -newer "$LOG_FILE" -print 2>/dev/null \ - | LC_ALL=C sort \ - | tail -n 1 - )" - [[ -n "$crash_report" ]] && break - sleep 0.1 - done - - [[ -n "$crash_report" ]] || { - local debugger_log="$PROFILE_DIR/Lua-lldb.log" - local debugger_pid="" - - echo "No Lua crash report was available; reproducing once under LLDB." >&2 - lldb \ - --batch \ - --one-line 'process attach --name Lua --waitfor' \ - --one-line continue \ - --one-line-on-crash 'thread backtrace -c 80' \ - >"$debugger_log" 2>&1 & - debugger_pid=$! - - sleep 1 - /usr/bin/open -n "$APP" --args \ - -uitest 1 \ - "--user-data-dir=$PROFILE_DIR-lldb" \ - >>"$debugger_log" 2>&1 || true - - for _ in {1..240}; do - if ! kill -0 "$debugger_pid" 2>/dev/null; then - wait "$debugger_pid" 2>/dev/null || true - cat "$debugger_log" >&2 - return 0 - fi - sleep 0.25 - done - - kill -KILL "$debugger_pid" 2>/dev/null || true - wait "$debugger_pid" 2>/dev/null || true - cat "$debugger_log" >&2 - return 0 - } - - echo "Crash report: $crash_report" >&2 - tail -n +2 "$crash_report" > "$crash_body" - plutil -extract exception json -o - "$crash_body" >&2 || true - plutil -extract termination json -o - "$crash_body" >&2 || true - - faulting_thread="$(plutil -extract faultingThread raw -o - "$crash_body" 2>/dev/null || true)" - app_base="$(plutil -extract usedImages.0.base raw -o - "$crash_body" 2>/dev/null || true)" - [[ -n "$faulting_thread" ]] || return 0 - - plutil -extract "threads.$faulting_thread.recursionInfoArray" json -o - "$crash_body" >&2 || true - for frame_index in {0..39}; do - local frame_path="threads.$faulting_thread.frames.$frame_index" - local image_index="" - local image_offset="" - local symbol="" - local resolved_symbol="" - local runtime_address="" - - image_index="$(plutil -extract "$frame_path.imageIndex" raw -o - "$crash_body" 2>/dev/null || true)" - [[ -n "$image_index" ]] || break - image_offset="$(plutil -extract "$frame_path.imageOffset" raw -o - "$crash_body" 2>/dev/null || true)" - symbol="$(plutil -extract "$frame_path.symbol" raw -o - "$crash_body" 2>/dev/null || true)" - - if [[ "$image_index" == 0 && -n "$app_base" && -n "$image_offset" && -f "$dwarf" ]]; then - printf -v runtime_address '0x%x' "$((app_base + image_offset))" - resolved_symbol="$(atos -arch arm64 -o "$dwarf" -l "$app_base" "$runtime_address" 2>/dev/null || true)" - fi - - printf '#%s image=%s offset=%s symbol=%s%s\n' \ - "$frame_index" \ - "$image_index" \ - "$image_offset" \ - "${symbol:-unknown}" \ - "${resolved_symbol:+ resolved=$resolved_symbol}" >&2 - done -} - -for _ in {1..60}; do - if ! kill -0 "$app_pid" 2>/dev/null; then - set +e - wait "$app_pid" 2>/dev/null - exit_status=$? - set -e - echo "Lua exited during the release launch smoke test with status $exit_status." >&2 - cat "$LOG_FILE" >&2 - print_crash_diagnostics - exit 1 - fi - sleep 0.25 -done - -echo "Lua remained running for the release launch smoke test." diff --git a/script/verify b/script/verify index 35b8bd8d..8b2b0807 100755 --- a/script/verify +++ b/script/verify @@ -82,6 +82,4 @@ if [[ -s "$unexpected_file" ]]; then fi echo "Known blocker: Phi Framework still embeds the audited upstream endpoint allowlist." -"$ROOT_DIR/script/smoke_launch" "$APP" - echo "Verified Lua release bundle at $APP"