mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-07-22 07:48:27 +00:00
revert(desktop): drop ProGuard keep-rule swap — binary PoW refuted the strip hypothesis
Built ./gradlew :desktopApp:proguardReleaseJars on both main and this branch and inspected the shrunk java-keyring-1.0.4-*.jar in desktopApp/build/compose/tmp/main-release/proguard/. Both branches contain byte-identical macOS Keychain backend bytecode: OsxKeychainBackend, ModernOsxKeychainBackend, pt/davidafsilva/apple/OSXKeychain, plus all _addGenericPassword / _findGenericPassword / _deleteGenericPassword / loadSharedObject native methods. ProGuard is NOT stripping the macOS backend. The compose-rules.pro comment had misled me. pt.davidafsilva.apple IS a real transitive runtime dep of com.github.javakeyring:java-keyring — ModernOsxKeychainBackend has a private pt.davidafsilva.apple.OSXKeychain field. The original keep rule was correct; restore it and clarify the comment about the transitive relationship so the next person to read this code doesn't repeat the same mistake. The AccountManager keychain-unavailable diagnostic + LoginScreen banner introduced earlier in this branch are kept — they're useful for any future failure mode in this area, not just the (refuted) ProGuard one. See https://github.com/vitorpamplona/amethyst/pull/3260#issuecomment-4740073787 for the full PoW jar inspection. Remaining hypotheses (H2 hardened-runtime unsigned-dylib block, H4 jpackage stripping the bundled libosxkeychain.dylib, H5 v1.11.0 migration gap) are documented in the plan doc. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -82,14 +82,12 @@
|
||||
# - androidx.sqlite (sqlite-bundled): nativeThreadSafeMode() stripped,
|
||||
# so `BundledSQLiteDriver.threadingMode` threw NoSuchMethodError on the
|
||||
# first relay-store query (LocalRelayStore.refreshStats).
|
||||
# - com.github.javakeyring (java-keyring): Keyring.create() reflection-loads
|
||||
# the OS-specific backend (OSXKeychainBackend / SecretServiceBackend /
|
||||
# WinCredentialStoreBackend). Without these keeps the macOS backend was
|
||||
# stripped, BackendNotSupportedException fired on every cold boot, the
|
||||
# SecureKeyStorage fallback silently returned null, and any account whose
|
||||
# key lived in the keychain (nsec, NIP-46 bunker ephemeral, NWC secret)
|
||||
# was forced to re-log-in every launch of the release DMG. The dev/Gradle
|
||||
# run path skips ProGuard, which is why it never surfaced in development.
|
||||
# - pt.davidafsilva.apple (jkeychain): macOS Keychain JNI for nsec storage.
|
||||
# Note: this lives in the dependency graph as a transitive runtime dep of
|
||||
# `com.github.javakeyring:java-keyring` — `ModernOsxKeychainBackend` holds
|
||||
# a `private pt.davidafsilva.apple.OSXKeychain` field. Keep both the
|
||||
# class and its private native methods (loadSharedObject / the
|
||||
# _addGenericPassword / _findGenericPassword bridge).
|
||||
#
|
||||
# secp256k1-kmp is already covered by the `-keep class fr.acinq.secp256k1.**`
|
||||
# rule mirrored from mobile above.
|
||||
@@ -98,10 +96,9 @@
|
||||
native <methods>;
|
||||
static <methods>;
|
||||
}
|
||||
-keep class com.github.javakeyring.** { *; }
|
||||
-keepclassmembers class com.github.javakeyring.internal.** {
|
||||
-keep class pt.davidafsilva.apple.** { *; }
|
||||
-keepclassmembers class pt.davidafsilva.apple.** {
|
||||
native <methods>;
|
||||
<init>(...);
|
||||
}
|
||||
|
||||
# kmp-tor — loads native Tor daemon via JNI reflection
|
||||
|
||||
@@ -6,22 +6,29 @@ date: 2026-06-18
|
||||
origin: docs/brainstorms/2026-06-18-fix-macos-bunker-relogin-brainstorm.md
|
||||
---
|
||||
|
||||
## Implementation Status (2026-06-18)
|
||||
## Implementation Status (2026-06-18, updated)
|
||||
|
||||
**Landed in PR 1 (Phases 1 + 2 + partial 3):**
|
||||
- ✅ ProGuard keep rules fix (`desktopApp/compose-rules.pro`) — the actual bug fix
|
||||
**H1 (ProGuard strip) is REFUTED.** Binary PoW: `./gradlew :desktopApp:proguardReleaseJars` on both `main` and the fix branch produces `java-keyring-1.0.4-*.jar` outputs with byte-identical macOS keychain backend bytecode (`OsxKeychainBackend`, `ModernOsxKeychainBackend`, `pt/davidafsilva/apple/OSXKeychain` including all `_addGenericPassword` / `_findGenericPassword` / `_deleteGenericPassword` / `loadSharedObject` native methods). The original `pt.davidafsilva.apple.**` keep rule was correctly targeting the transitive JNI bridge (`ModernOsxKeychainBackend` holds a `private pt.davidafsilva.apple.OSXKeychain` field). See PR #3260 comment for the full keep-rule audit.
|
||||
|
||||
**Landed in PR 1 (defense-in-depth only):**
|
||||
- ✅ `AccountManager._keychainUnavailable: StateFlow<Boolean>` mirroring the existing `_storageCorruption` / `_forceLogoutReason` diagnostic channels
|
||||
- ✅ `loadInternalAccount` + `loadBunkerAccount` raise the signal when `accounts.json.enc` points at a key the keychain cannot return
|
||||
- ✅ `LoginScreen` observes the signal, renders a single-line error banner above the LoginCard; `clearKeychainUnavailable()` invoked from all successful login paths
|
||||
- ✅ Four new unit tests in `AccountManagerLoadAccountTest`: Internal-no-privkey signals, Bunker-no-ephemeral signals, `clearKeychainUnavailable()` resets, happy-path does NOT signal
|
||||
- ✅ Four new unit tests in `AccountManagerLoadAccountTest`
|
||||
|
||||
**Deferred to follow-up (verification + cross-platform sweep):**
|
||||
- Phase 0 Track B: visual repro on a signed release DMG — needs a Mac. The ProGuard rule fix is what solves the reported bug; reproducing the broken state requires building + installing the unfixed DMG.
|
||||
- Phase 4: Linux DEB + Windows MSI cold-boot smoke test. Same keep rule covers all `internal.**` backends so should incidentally fix them; needs CI matrix run.
|
||||
- Phase 5: signed + notarized DMG verification — needs Mac + signing cert.
|
||||
- Phase 0 Track A unit test that mocks `Keyring.create()` throwing — required adding a production injection seam for marginal regression value; the actual ProGuard regression is only catchable on a shrunk artifact (Phase 3 #7 smoke test in the plan below), not unit tests.
|
||||
- Phase 3 #7 release-DMG smoke test extension — needs work on the existing smoke-test harness (out of scope for one-shot fix).
|
||||
- ProGuard `mapping.txt` regression guard gradle task — nice-to-have, deferred.
|
||||
**Reverted in PR 1:**
|
||||
- ❌ ProGuard keep-rule change — the hypothesis it was based on is refuted by binary PoW; the original `pt.davidafsilva.apple.**` keep is correct and stays in place.
|
||||
|
||||
**Open — actual root cause still unidentified.** Working hypotheses:
|
||||
- **H2 — Hardened runtime blocks unsigned dylib load.** `OSXKeychain.loadSharedObject()` extracts `libosxkeychain.dylib` to `/tmp` and `System.load()`s it. Hardened-runtime + Gatekeeper on a notarized DMG **rejects loading an unsigned dylib at runtime** unless the app entitlements include `com.apple.security.cs.disable-library-validation`. Dev `./gradlew :desktopApp:run` doesn't apply hardened runtime — explains the dev-works / release-fails split.
|
||||
- **H4 — jpackage strips the dylib resource.** Compose-Multiplatform's jpackage path could re-pack jars without preserving `META-INF/native/*` or resource-style `.dylib` files. Worth examining the DMG bundle structure.
|
||||
- **H5 — pre-hardening accounts.json.enc migration gap.** If user installed `v1.11.0` then upgraded, the migration from `bunker_uri.txt` → `accounts.json.enc` (commit `46caa4d79`) might leave the bunker entry missing. But this would not happen "every restart, always" — it would happen ONCE post-upgrade. Lower likelihood.
|
||||
|
||||
**Verification plan (still needs a Mac + ideally the affected user):**
|
||||
- `./gradlew :desktopApp:packageReleaseDmg` locally, install, log in with bunker, quit, relaunch — DOES the bug reproduce on a locally-built (unsigned) DMG?
|
||||
- If reproduces on unsigned DMG → not hardened runtime, look at jpackage stripping.
|
||||
- If only reproduces on signed/notarized DMG → H2 is the answer; fix is `--mac-entitlements` or `--mac-hardened-runtime` flag config.
|
||||
- `codesign --display --entitlements - /Applications/Amethyst.app` on the affected user's installed DMG to see what entitlements were actually applied.
|
||||
|
||||
# 🐛 fix(desktop): macOS forced re-login on cold boot — ProGuard strips java-keyring backend
|
||||
|
||||
|
||||
Reference in New Issue
Block a user