Repository navigation
iOS 4.2.1 (2026.11.11): PIN lifecycle fix and wallet release - #1315
Closed
takemiyamakoto wants to merge 57 commits into
Closed
takemiyamakoto wants to merge 57 commits into
takemiyamakoto wants to merge 57 commits into
Conversation
Two Polkaswap screens broke on iPhone with the 4.2.0 redesign (#1305): - The token picker stretched its search field over most of the sheet and left the list a strip at the bottom. SearchTextField took its size from BackgroundedContentControl's empty contentView, a zero height, and #1305 replaced its fixed height with a 44 pt minimum, so the sheet's stack view stretched the field. The field now reports its own height and hugs it, and the list and its empty state share a container so the hidden empty view no longer fights the list's height. - Preview pushed the confirmation inside the new swap tab, where the tab bar and the raised Polkaswap button covered Confirm and the header sat under the status bar. The confirmation now hides the tab bar and keeps its header and button inside the safe area. Both tests fail on the previous code: the search field measured 653 pt in an iPhone-sized sheet, and a tap on Confirm reached a tab button. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On 4.2.0 a swap quote could take minutes to appear. Since #1305 every quote request carried its own ID and only the newest request's reply was shown, while every SORA block (about 6 s) cancelled the pending request, hid the shown quote and asked again. Any reply slower than a block was therefore thrown away, block after block, until one happened to be fast enough. Quotes also queued in the app-wide operation queue behind unrelated work, waited 0.8 s after each keystroke, and replaced block subscriptions kept refreshing because the subscription service cancels them as requests, which leaves them running on the node. - A reply is shown when it answers the question on screen (pair, amount, direction, market) and nothing newer is shown, so a refresh no longer invalidates the reply already on its way. Replies to an old amount and older replies are still dropped. - A block refresh keeps the shown quote, sends nothing while a reply is awaited, and a failed refresh keeps the quote instead of clearing the inputs behind an alert. - Quotes go straight to the SORA socket with a 10 s timeout; fees and pair checks use the swap screen's own operation queue. - Typing waits 0.3 s instead of 0.8 s. - Updates from replaced block subscriptions are ignored. In the simulator, against the production SORA nodes, the time from keystroke to quote fell from about 1.8 s to 0.7 s. The new presenter test fails on the previous code: the awaited reply is discarded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026.11.5 went to TestFlight and App Review on 4 November 2026, so it joins the consumed builds and becomes a supported in-place upgrade predecessor in the usability gate. The startup capture keeps accepting it under its own name, every release tool now pins 2026.11.6, and the build-number check's test treats 2026.11.5 as the latest consumed build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Apple approved 4.2.0 with build 2026.11.5 (Pending Developer Release on 7 November 2026), which closes 4.2.0 to new uploads, so the Polkaswap fixes ship as 4.2.1 (2026.11.6). The Release target, the archive, identity, signed-artifact and build-number checks, the usability gate and fastlane now expect 4.2.1. The startup capture still expects 4.2.0 for every earlier build and 4.2.1 only for 2026.11.6. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The self-test parsed the check's output with a literal "Release 4.2.0" and used 4.2.0 in its version-drift cases, so it stopped recognising a passing repository once the release moved to 4.2.1. It now reads the version and build from the check's output and uses them in every case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the owner's word, 4.2.0 (2026.11.5) was removed from review, its version record renumbered to 4.2.1 with build 2026.11.6, and submitted on 7 November 2026 at 10:06 UTC. It is Waiting for Review with a manual release. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 7, 2026
verify-native-source.py accepted SOURCE_PACKAGES_DIR from its callers but never read it, so a moved tag or an edited package checkout passed the check. It now compares every pinned checkout's HEAD with its revision and refuses local changes, using git --no-optional-locks so the check never writes. verify-pods.py and config/pods-source-pins.json record digests of the 11 pods compiled into the app or its tests and of the rswift and swiftformat executables that Xcode runs. The rswift digest equals the upstream 6.1.0 release binary. Recorded from the reviewed 4.2.1 install. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
createPublicKeySoft copied SR25519_KEYPAIR_SIZE (96) bytes out of a 32-byte stack buffer, reading stack memory into the key and always failing. Nothing calls it today; it now copies the 32-byte public key and a test checks it against the soft-derived key pair. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tonkeeper/ton-api-swift 0.1.7 has no licence file. Packages/TonAPI holds the same client generated here by Apple's swift-openapi-generator 0.3.1 (Apache-2.0) from the MIT-licensed opentonapi specification, with provenance in its README. Client.swift is byte-identical to ton-api-swift's; Types.swift is byte-identical after postprocess.py drops Action._type, as ton-api-swift did, so a new tonapi action kind cannot fail a history page. scripts/deps/regenerate-tonapi.sh rebuilds the pinned generator and fails if the committed sources differ from fresh output. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner, 2026-11-07: fix the key-exposure audit findings, and "don't use any closed source libraries. make everything you need here in this repo, open source". Closed and non-open-source code: - Delete the closed Kotlin/Native sorawallet framework, loaded at every launch. Its only use, Polkaswap strategic-bonus APY, is now Swift (PolkaswapAPYWorker, PolkaswapPoolApyMapper) against pi.soramitsu.io; the framework's SubQuery endpoint answers 503. - Remove WalletConnect (reown-swift, WalletConnect Community License) with its prebuilt yttrium binary and five Reown-only packages. A one-time clean-up deletes only com.walletconnect.sdk generic passwords and WalletConnect defaults. Keep the Chainlink ABI code under its web3swift and MEWwalletKit licences. - Build against the in-repo TonAPI package instead of ton-api-swift. Key exposure: - The emergency recovery export becomes a password-encrypted file (scrypt plus XSalsa20-Poly1305, 8+ characters, verified before sharing) and never shows a secret; scripts/recovery/decrypt-emergency-material.py opens it. - Forget wallet (now PIN-gated) and logout erase the Keychain secrets of removed wallets that were backed up, after their rows are gone. Orphaned keys and never-backed-up wallets' keys stay recoverable through the encrypted export. - PIN lockout with backoff persisted in the Keychain, constant-time compare, biometric enrollment checks that trust an existing install's first unlock, a 5-minute monotonic background re-lock, and PIN or biometrics before every fund-moving or staking submission. - JSON exports need 8+ characters; secret files get complete protection, their own directories and removal after sharing; Inbox copies and old leftovers are removed. - Remote SVGs are filtered before SVGKit; seed and key share sheets exclude Copy; secrets hide while the screen is captured and screenshots warn; keyboard extensions are refused. - TON Connect never signs a ton_proof for a site its manifest does not come from, and gets a veto-only kill switch. - Xcode now runs the checkout and pod verification before any build tool. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026.11.6 is in App Review and counts as consumed; 2026.11.7 replaces it in review with the closed-source removal and the key-exposure fixes. Release tools, gates and their tests now pin 4.2.1 (2026.11.7), as cc8e3ec and 1092c5e did for 2026.11.6. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…26.11.7) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Packages/FearlessWalletConnect speaks WalletConnect v2 in the wallet role on Apple's system frameworks only (Foundation, CryptoKit, Security, Combine). It replaces Reown's reown-swift, which is under the WalletConnect Community License and is not open source. - X25519 key agreement, HKDF-SHA256 session keys, SHA-256 topics, and ChaCha20-Poly1305 type 0 envelopes in padded base64 (type 1 is read, never opened). - The relay's EdDSA did:key sign-in JWT, valid for 24 hours. - The relay over URLSessionWebSocketTask: subscribe, batch subscribe, publish, unsubscribe, the batch message fetch, acknowledging each delivery, keep-alive pings and reconnecting with back-off. - Pairing, proposals, approval and refusal, settlement, requests checked against the session's namespaces, answers, ping, disconnection and expiry, with each Sign method's relay tag and lifetime. - Verify: ES256 attestations from the relay, or the trusted service's answer by message hash. - A strict JSON reader that keeps numbers exact and refuses duplicate keys. - Its record in the app group's user defaults and its keys in one keychain item, and the import of what WalletConnectSwiftV2 (Fearless up to 4.1.0) and Reown 1.8.0 (4.2.x) saved, left in place for a downgrade. Tests run on macOS with swift test: RFC 7748 and WalletConnect relay-auth vectors, the wallet role against an in-memory relay that follows the live relay's delivery rules, and both earlier storage layouts. Two checks use the WalletConnect SDK installed outside the repository, never as a dependency: scripts/walletconnect/reference-vectors.mjs for a differential cryptography check, and reference-dapp.mjs as a dApp on the live relay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner, 2026-11-08, asked "Bring WalletConnect back on the new 4.2.1 head with this client?": "Bring it back". e0dda28 removed WalletConnect because reown-swift is not open source. This restores the WalletConnect part of that commit on Packages/FearlessWalletConnect and keeps everything else in it: the key-exposure fixes, the removal of sorawallet and ton-api-swift, and the source and pod verification phase. - The WalletConnect service, screens, coordinators, models and tests come back as they were in 4.2.1 (2026.11.6), on FearlessWalletConnect instead of WalletConnectSign. Reown's socket engine, socket factory and crypto provider stay deleted. - Forgetting a wallet and logging out disconnect its WalletConnect sessions again, after the new secret erasure. - RetiredWalletConnectDataCleanup is removed. It deleted the com.walletconnect.sdk Keychain items and WalletConnect defaults that the client imports, so sessions paired in 3.0.5 to 4.2.1 (2026.11.6) carry over again. Devices that already ran 2026.11.7 lost them; their dApps pair again. - RLPBigInt.swift and SignTypedData.swift (MEWwalletKit, MIT) return to WalletConnect/eip_mew with that licence and a NOTICE entry; the regex helper the ABI move dropped is String+Match.swift there. - The release audits again require WalletConnect.projectId (FL_WALLET_CONNECT_PROJECT_ID) and check that the WalletConnect service uses the entitled app group. - No package is added: the resolved graph stays at 40 external packages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/walletconnect-protocol.md describes Packages/FearlessWalletConnect: its files, the wire format, its storage, the import of what earlier SDKs saved, and how to rerun its checks, including the real-storage upgrade check from a Reown 1.8.0 build. It notes that 4.2.1 build 2026.11.7 deleted those records once per install, so a device that ran that build has nothing to carry over and its dApps pair again. The binary provenance record says the wallet now speaks WalletConnect with its own client on Apple's system frameworks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WalletConnect pairing codes use the `wc:` scheme (EIP-1328), but the QR matcher accepted `ws:` instead, so a scanned pairing code was reported as an invalid QR code. `WalletQRService` now matches `wc:` codes itself, from the camera and from gallery images, and no longer reports a `ws:` node address as a pairing code (the shared decoder still does; its result is refused). A new connection is only useful for signing, so `connect(uri:)` opens it together with signing: while `WalletConnectSigningPolicy` is off, a scanned code shows a localized "not available in this version" message and nothing is paired. This change also adds, in every bundled localization, the strings the reworked WalletConnect request flow uses in the following changes. Tests cover pairing codes and node addresses from the camera, the gallery and the scanner, connecting with signing off and on, malformed codes, and the new message in every shipped localization. The scanner tests now use `wc:` codes. Ported onto the in-repo client (Packages/FearlessWalletConnect): the service pairs through the client, or through an injected pairer in tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 805b624e2cadaa59856d89ea719e56ba9230e7b6)
…ation Every request is now prepared before its confirmation opens, and the confirmation and the signer use the same prepared values: - The request must name an account the session approved for the request's chain. The signing key is that account's, not a match picked by address from all wallets. - EVM transactions get their nonce, gas limit and gas price before the confirmation opens. The confirmation shows the network first, then the recipient (the token recipient or spender for ERC-20 transfers and approvals), the amount and the most the network can charge, and exactly those values are signed; nothing is quoted again after Confirm. Chain ids are parsed as the decimal values the app stores, a transaction for another chain is declined, and so is a request without a recipient. - EIP-712 typed data must carry the request's chain id when its domain names one, and permits show their spender and amount. The legacy array format, which no domain binds to a network, is declined. - Substrate transactions must carry the chain's genesis hash, spec version and transaction version. The call is decoded and must encode back to exactly the dApp's bytes; transfers show their recipient and amount, the fee is quoted for that exact extrinsic, and the tip is signed as sent. - Substrate messages are signed inside the `<Bytes>…</Bytes>` envelope that polkadot-js uses. - Personal-sign and typed-data signatures use the 27/28 recovery id that JSON-RPC callers and `ecrecover` expect. Declined requests show a localized reason and get an error response, and the request sheet no longer prepares the request again on every balance update. `WalletConnectSigningPolicy.production` stays off. Tests cover account binding, the quoted and exactly signed transaction for a decimal chain id, chain-id, address and quantity checks, ERC-20 decoding, the EIP-712 specification's example digest, permits, the `<Bytes>` envelope and its signature, Substrate genesis, runtime and call checks against the bundled Polkadot runtime, the signed payload, a runtime change before signing, and the confirmation rows for every method. Ported onto the in-repo client: the new files import FearlessWalletConnect, and the project changes were applied to this line's project file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 59c74e0bf33f93f29593006dc1219cb4b39e2b08)
…sts behind the lock - Signing now waits for the PIN or biometrics, asked from the confirmation after the user taps Sign. Closing the PIN screen signs nothing and leaves the request open to sign or reject; without a PIN nothing is signed, and a PIN screen that goes away without an answer counts as not confirmed. - The PIN screen that locks the app after inactivity now records that the app is locked. While it is shown, WalletConnect requests and notices wait in their queue, and a request already on screen is hidden until the app is unlocked. - A request the SDK delivers again while it is queued or on screen is shown only once. Tests cover signing with and without the user's confirmation, the PIN screen from the confirmation with its success and cancel paths, the missing-PIN case, requests waiting behind the lock and appearing after it, one request at a time, a repeated request shown once, the request window hidden while locked, and the lock ending with its PIN screen. Applied unchanged on the 4.2.1 line: the lock marker sits after the head's unlock-PIN guard in FearlessApplication.dropSession(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit cc3e7ee3a6275c65e84f58f077de2781bcd66ebe)
…s scams The SDK reports what WalletConnect Verify knows about the app behind each proposal and request, but the service dropped it. It now reaches the screens: - The proposal shows a Verification row below the app: verified domain, domain not verified, or domain mismatch. - The request sheet shows the same status under the app's name; a domain mismatch replaces the generic warning with one saying the app may not be the website it claims to be. The confirmation lists it as its last row. - A proposal or request from an app Verify flags as a known scam is declined before anything opens, and the user is told which app was blocked and that nothing was connected or signed. This applies while signing is off too. The notice presenter now serves both the signing-unavailable and the blocked-app notices. The domain-mismatch status is shortened for the rows, with its explanation as a separate warning, in every bundled localization. Tests cover how every Verify result maps to what is shown, declined and never shown scam proposals, scam requests declined before any confirmation with signing on and off, verified requests opening with their status, the blocked-app notice, the confirmation row, and the texts in every shipped localization. Ported onto the in-repo client: the client's sessionProposal and sessionRequest events carry the VerifyContext its Verify client produced, and a scam-flagged proposal is refused through the client (or an injected rejecter in tests). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 1b9a737d14a75b715d8f3d722c7a682b8dd9dc18)
…submission PIN Owner's decision for 4.2.1 (2026.11.8), relayed from the key-exposure session: "we need some wallet connect support of course", choosing "Connect and sign": pair from wc: codes and links, sign EVM, Polkadot and TON requests, ask for the PIN or Face ID before each signature, show the real requesting site, and let a remote kill switch turn signing off. - WalletConnectSigningPolicy.production is on. The ported WC-SIGN checks stay in front of every key: the account the session approved, the network, the decoded call that must encode back to the dApp's bytes, the quoted fee, the <Bytes> envelope, and scam-flagged apps declined. - Every signature asks through SubmissionAuthorizer, the step every fund-moving and staking submission takes, with its PIN lockout and biometric rules. Without a PIN nothing is signed. - Remote vetoes, absent means enabled and only an explicit false pauses: evm_dapp_signing_enabled and polkadot_dapp_signing_enabled for WalletConnect, ton_dapp_signing_enabled for TON Connect transactions and ton_proof sign-ins, the Android key names; never dapp_enabled. A paused request is declined before anything opens and the user is told nothing was signed. The confirmation checks again before and after the PIN. - The requesting site is shown as WalletConnect Verify reports it: on a domain mismatch the website row names the site the request came from, and a site Verify could not check is labelled "Domain not verified" with a warning. - wc: links and fearless://wc?uri= links pair (Info.plist registers wc). A link that opens the app waits until WalletConnect starts after the unlock; a failed link gets a notice. A wc: link without a key only brings the wallet forward. - New strings in every bundled localization. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scripts/walletconnect/reference-dapp-sign.mjs runs the WalletConnect SDK most dApps ship as a dApp on the live relay. It asks for personal_sign, eth_signTypedData_v4 and polkadot_signMessage, and checks every signature with code that is not Fearless's: ox recovers the EVM signer and @noble/curves verifies the Polkadot ed25519 signature over the <Bytes> envelope (and that it does not cover the bare message). The SDK and those libraries stay outside the repository. WalletConnectLiveSigningTests (skipped unless FEARLESS_WC_SIGN_LIVE=1) is the wallet: it pairs from the dApp's link, approves an EVM and a Polkadot account, and answers each request with the app's own request preparation and signer, as the confirmation does after the PIN. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The gate's reviewed test list came in with #1305 already out of step with the test sources, so the gate could not pass on this line. Replace the nine names that never existed here, add the 54 tests the selected classes gained, and require exactly 478 tests. The refreshed list equals the identities that ran in the Release -O gate on this source: 478 passed, none failed or skipped. Fix a race in the gate self-test's fake xcrun. Under pipefail, `printf | grep -q` fails when grep exits before printf finishes writing, so the fake sometimes answered a tests request with the summary JSON and the valid gate was rejected. Point the simulator rehearsal at 4.2.1 and baseline the two generator placeholder comments in the generated TON API client. Tooling only; nothing here is compiled into the app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its checks had never run against this source, and its self-test faked the steps that were wrong. Look for managed-object classes among defined symbols instead of external ones. CDTonDapp and CDTonConnectedApp are internal classes kept alive by ManagedObjectRuntimeClassRegistry, so `nm -g` can never list them. The fake `nm` now hides them from -g, so going back to it fails the self-test. Accept Core Data's complaint about the five transformable attributes that bundled historical models declare with customClassName="[String]". Those models must stay byte-exact to open the stores they wrote. Any other attribute, type or failure line beside it still fails, with cases for each. Make both Core Data self-tests fail unless they reach their end. An EXIT trap's own success could hide a crash and report a pass. Tooling only; nothing here is compiled into the app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An iPhone before iOS 26 gives UIActivityViewController no popover controller, even when asked for the popover style, so there is nothing to anchor. The tests assumed there always was one. They now accept that on iPhone only; an iPad with no popover still fails, because presenting one unanchored raises an exception. Let the seed-share ownership test wait up to 30 s for the system share sheet. It allowed 3 s, which a loaded machine can exceed, and passes alone in about 2 s. Tests only. On iOS 18.1 and 27.0 iPhones and an iOS 18.1 iPad: passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The system-share entry on the seed screen is Debug-only, so this test could only fail at -O. In Release it now asserts that the row offers no such entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Swift 6.4.2 at -O made SolanaTransactionBroadcaster.retry destroy the snapshot and only then retain record.signature, so the unavailable-recovery outcome carried a freed string. SolanaSendServiceTests crashed at two tests in every Release run, and the shipped 2026.11.9 binary has the same order. No screen reaches this branch today: retry is only offered after an unknown outcome on a bound record, and an unbound record stops earlier at unavailableSignature. The behaviour is otherwise unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 of 7 tasks
Replaces the stale App Review line, and records the test results, the Release use-after-free, its fix, the manual-release hold, and what was not run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026.11.9 carries the Solana retry use-after-free fixed in 46097e1. The owner chose to replace it in App Review with a build that has the fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026.11.10 replaced 2026.11.9 in App Review on 9 November: the build carries the Solana retry fix, and the owner chose the rebuild over keeping the queue place. Add its validation, failures and receipts, and correct the 2026.11.9 note: automatic release was first set at 13:46 UTC, and load was the probable cause of its two timeouts, not an established one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 of 7 tasks
takemiyamakoto
added a commit
that referenced
this pull request
Nov 10, 2026
Contributor
Author
|
Folded into #1326, which brings every open iOS PR into master in one change, so one code-owner approval covers it. This PR's branch is kept; new hotfix commits on it should be merged into release/consolidated-20261110 too. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fearless 4.2.1 (2026.11.11) fixes a PIN setup race that could send a user back to the main screen after they left the PIN screen. UIKit can defer its disappearance callback until after a pending save finishes. The controller now cancels that completion when UIKit begins removing it from its parent. The release also includes the Polkaswap fixes, PIN keypad sizing, app-owned WalletConnect client, service-key fixes and optimized Solana retry fix from the preceding builds.
The regression test reproduced the race in 18 of 20 attempts on iOS 18.1. The fixed build passed 20 of 20 attempts on each of iOS 18.1 and 27.0, including animated and nonanimated navigation. The TON WebKit test now uses a visible scene and tracks the intended navigation.
Local validation for release source
098dc861e48ebdec6e5595688170cb554f8b5f15is complete.4fc2b3a1a.a9ba1cba78615e55b131d2921aaf942766774d9fe4124236eb1f1f5a9eacab43. The device crypto host previously passed 11 of 11 sr25519 tests in optimized Debug with matching crypto inputs.Apple accepted 2026.11.11 for normal App Review on 10 November at 10:32:58 UTC. The verified release policy is automatic release on approval. The build is also available on internal TestFlight. The previous build was rejected. The reviewer’s specific reason was unavailable when the owner directed resubmission. The in-place iPhone update was verified and the cold-launch observation was pending at submission.
Release record and artifact hashes