iPhone Installer Store Bridge 0.4.12
===================================

Purpose
-------
A local, persistent JSON-lines adapter around a pinned ipatool library.
It signs in to the user's own Apple Account, lists every mobile/legacy record
returned by Apple's purchase-history service, and downloads selected apps.
An explicit direct-download operation accepts a known numeric App Store ID even
when the history response omits it; Apple still verifies the current account's
license. Delisted/hidden apps are not guaranteed to appear in the history or to
remain available for download from Apple.
It does not purchase apps, transfer purchases, remove FairPlay, or re-sign IPA files.

Upstream and source
-------------------
Repository: https://github.com/majd/ipatool
Pinned upstream commit: d9c0247daea564d3ec7d04a9b015d0a29af66b04
Upstream commit date: 2026-10-25 02:20:01 +0200
Source archive: ipatool_bridge_source.zip
The source archive is a complete clean working source snapshot without .git or
module/build caches. It includes go.mod, go.sum, upstream sources and tests,
generated mock test helpers, the bridge, and the local extension/tests.

Changes relative to upstream:
1. New bridge/ executable with JSONL input/output and fixed public error codes.
2. Exported appstore.OwnedAppsAll helper. The normal upstream paging API remains
   unchanged; the helper performs the same full fetch/merge/sort once and skips
   local page slicing. Repeated network fetches for every 100 entries are avoided.
3. A regression test covers 251 purchases returned by one complete fetch.
4. Bridge 0.4 adds explicit download_direct without a local history-membership
   requirement. Both download paths call the same authenticated Download and
   identity-verification pipeline. No Purchase or Lookup call is added.
5. Bridge 0.4.1 connects a bounded DownloadInput.OnProgress observer to actual
   HTTP-file writes. It replaces temporary-file polling with measured byte
   counts and validated Content-Length/Content-Range totals, and exposes
   processing phases after the body transfer. The existing progress-bar API,
   authentication, license requests, identity checks and payload bytes remain
   unchanged.

Build provenance
----------------
Bridge 0.4.12 is built with Go 1.27.3 windows/amd64 on Windows.
The build receipt records the local compiler hash, source archive entry set,
executable hash, tests and credential-free hello/quit protocol verification.

Windows x64 build (from the extracted source root):
  GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -trimpath -buildvcs=false -ldflags="-s -w" -o store_bridge.exe ./bridge

Linux build:
  go build -trimpath -buildvcs=false -ldflags="-s -w" -o store_bridge ./bridge

On PowerShell, set GOOS, GOARCH and CGO_ENABLED through the corresponding
$env: variables before the go build command. Go 1.26.5 or newer is required
by the pinned upstream module. No Go compiler is required on the user's computer.

manifest.json gives the hash and size of the delivered Windows executable.
The PE machine field is verified as AMD64 (0x8664). Credential-free hello/quit
is executed on Windows. This check is not a real Apple Account login or Mac test.

Protocol 1
----------
One JSON object per UTF-8 line on stdin and stdout. Maximum request: 64 KiB.
Every request needs an id matching [A-Za-z0-9_.:-]{1,100}.
Requests are sequential. All events and results carry the same request id.
A result has type "result", ok true and data, or ok false and error {code,message}.
Progress events have type "progress" and stage. Download events carry integer
current bytes, including a validated resume offset; total is the full size from
validated HTTP Content-Length/Content-Range when known. The total field is absent
for unknown/chunked lengths. No guessed percent is sent. Counts advance only
after a successful file write. Updates are limited to four per second, plus the
initial and final byte counts. The final byte count alone is not a successful
download or installation result.
Non-transfer phases carry no byte count: waiting (Apple/HTTP response), verify
(package checks), package (artwork/metadata/license preparation), and save
(publishing the checked IPA). Python performs its own verify/SHA phase afterward.
Phase and diagnostic text is selected from fixed strings; no URL, token, header,
account metadata, raw response or raw error is included in progress.

Operations:
- hello: local/offline information; does not initialize a session or contact Apple.
  Returns bridge_version "0.4.1", protocol 1, and
  capabilities {"direct_download":true,"download_progress":true};
  purchase_supported remains false.
- login: email/password; on two_factor_required, repeat login with auth_code only.
- list: retrieves all returned history records once, merges the upstream
  storefront responses, and filters explicitly non-mobile platforms.
  iPhone, iPad, empty/unknown legacy platforms remain visible.
  data.apps records: id, name, bundle_id, version, purchase_date, platforms.
  count and total_count both equal the returned mobile/legacy record count.
  These counts describe this service response, not proof of every past purchase.
- download: app_id and an absolute, new output_path ending in .ipa.
  The existing parent directory must be an ordinary directory, not a symlink.
  Only an id from the last successful list for the current session is allowed.
  No Lookup, Purchase, or automatic new-license acquisition is performed.
- download_direct: positive numeric app_id, optional bundle_id, and an absolute,
  new output_path ending in .ipa. Requires a successful login, but a prior list
  request or membership in that returned list is not required. The app_id is
  sent unchanged to Apple's authenticated download service. A missing license
  is rejected as license_required. A supplied bundle_id must match the package.
  An omitted bundle_id is resolved only from the downloaded package metadata;
  itemId must still match exactly. No public catalog lookup, acquisition,
  entitlement transfer, or DRM removal takes place.
- logout: drops the in-memory account, cookies, keychain and ownership allowlist.
- quit: logout and exit.

login, list, download and download_direct results include session_id. A caller
may supply it on list/download/download_direct; a mismatch is rejected.
Both download operations return path, app_id, bundle_id, version and session_id.
The Python parent additionally tracks
process/session generation so cancelled responses cannot affect a later session.

Session storage
---------------
The bridge does not call the ordinary ipatool CLI or its disk keyring.
It injects a mutex-protected memory Keychain and standard in-memory CookieJar
with public-suffix rules. CookieJar.Save is a no-op.
The Apple password is retained only while completing login/2FA, then removed
from the bridge account and overwritten in the in-memory keychain account.
Tokens and cookies remain only in process memory until logout/process exit.
No password, token, code, raw server response or raw error is written to
stdout, stderr, a log, a command line, or an application credential file.

This describes deliberate application storage. It does not claim secure RAM
erasure against OS paging, operating-system crash dumps or a compromised OS.
An IPA itself contains Apple-issued license metadata, which can include the
account email; an IPA is not an anonymous file.

Pinned ipatool runtime code can cache downloaded Apple framework assets and
the Unicorn runtime under the normal OS cache directory. These are program
assets, not login sessions. Asset downloads use upstream-pinned hashes.
The first authenticated operation therefore needs internet and can take time.

Download safety and cancellation
--------------------------------
The download goes into a private .iphone-download-* directory adjacent to the
requested output. Apple-issued sinfs are added through the actual ipatool
ReplicateSinf function. Before publishing, the bridge checks the downloaded
iTunesMetadata itemId against the selected numeric ID and verifies consistency
with the main app's CFBundleIdentifier. A known/supplied bundle identifier must
match. A newly available version is allowed; a different app identity is not.
The final file is created with O_EXCL so an existing file is never overwritten.
Failures clean only the bridge's own staging directory and a file it created.

Cancellation is performed by terminating the bridge process from the Python
parent. That discards the login session. A forced termination may leave a
partial file/staging directory in that job's private download directory; the
Python parent owns cleanup of that job. A completed result is emitted only
after identity checks and final file sync/close.

Verification
------------
- Bridge tests: offline hello/JSONL, 2FA continuation, password removal, no secret
  output, complete >100 history, unknown legacy platform, rejection of partial
  history, ownership allowlist, expired/stale session, exact downloaded identity,
  latest-version acceptance, license errors, cleanup, and overwrite protection.
  Direct-download tests cover an ID omitted from history (including no prior
  list request), an optional bundle, missing license, incorrect itemId/bundle,
  missing/stale authentication, invalid input, and cleanup after failure.
- Real streaming loopback HTTP fixtures cover known and chunked/unknown sizes,
  exact on-disk intermediate byte counts, resume offsets, server restart,
  short bodies, context cancellation and bounded event frequency. A real
  DownloadInput-to-downloader test checks processing phase order; bridge tests
  check request IDs, callback wiring and suppression of untrusted fields.
- Upstream OwnedApps regression suite, including the new 251-item test.
- Real Linux executable offline JSONL smoke: hello, unauthenticated
  list/download/download_direct, logout, quit. No Apple account or real device
  was used.
- An actual authenticated Windows download and actual iPhone launch still need
  to be checked with the user's account and selected app.

Tests from source:
  go test ./bridge
  go test ./pkg/appstore -run "Test(DownloadProgress|DownloadInputObserver|DownloadFileResumes|DownloadFileValidates)"
  go generate ./pkg/...
  go test ./pkg/appstore -run "^TestAppStore$" -ginkgo.focus="AppStore \(OwnedApps\)"

Licenses
--------
ipatool is MIT-licensed: LICENSE-ipatool.txt.
Bridge additions are MIT-licensed: LICENSE-bridge.txt.
The application's Python/device backend distribution has its own licenses.
licenses/modules.json records the pinned Go modules and included license files.
licenses/Go-LICENSE is the Go standard-library license.
licenses/Unicorn-COPYING records the license of the upstream runtime that ipatool
downloads separately; this package does not redistribute a Unicorn DLL or
Apple framework binaries.


6. Bridge 0.4.2 also reads /commerce/account/purchases with the existing in-memory
   Apple session. Each calendar year's pages are checked and merged by numeric ID.
   The result exposes explicit completeness and fixed warning codes. Only software
   identity fields leave this parser: billing/card/price/receipt fields are ignored.
   Non-Apple redirects, malformed pages, repeated incomplete pages and page limits
   cannot masquerade as a complete history. No license acquisition is added.
   Protocol reference: https://github.com/pdx15/ipatool-webGUI/blob/main/cmd/gui_purchases.go
   Cookie persistence and silent password-based relogin from that implementation
   are not adopted. Cookies and credentials remain local and memory-only.

7. Bridge 0.4.3 parses app IDs after identifying software, so subscriptions with
   null/text IDs cannot poison a mixed purchase page. Financial order IDs are
   ignored. A failed year no longer discards older ranges; three consecutive
   failures stop the scan. Fixed diagnostic codes/year/page/HTTP status expose
   the failure location without logging tokens, invoice bodies or payment data.

8. Bridge 0.4.4 distinguishes a repeated full purchase page from merely repeated
   app identities. Re-download invoices with matching software fields cannot
   truncate following pages. Full purchase arrays are compared by a temporary
   in-memory SHA-256 digest; no order/payment fields are emitted or persisted.
   App identity, merge, search and selection remain keyed by numeric Store ID,
   including when different apps have identical titles. Genuine repeated pages
   still produce an explicit incomplete-history warning and bounded termination.

9. Bridge 0.4.5 keeps display-name provenance: library name, earliest available
   dated purchase name/date, and other observed titles. Names without dates do
   not acquire invented dates or a claim to be the app's global original title.
   A partial refresh keeps previously read identities from this Apple login's
   memory, labels retained rows, and remains explicitly incomplete. A complete
   response controls membership afresh; logout/login clears this memory cache.
   License/download/package/device checks remain authoritative before payment.
   Commerce cookies use a separate memory-only jar, seeded per allowed Apple
   host, so later DAAP cookie writes cannot overwrite a previously used commerce
   host's cookies. Real authenticated refresh still needs user validation.

10. Bridge 0.4.6 follows Apple's opaque pagination-token, using page=2 for cursor
    continuations. A fresh cursor advances past overlapping purchase arrays.
    Tokens reset for each calendar range; cycles, malformed markers and missing
    continuations remain explicit failures. After a repeated JSON API page the
    same range is retried once in Apple's legacy response format, without
    dropping software already read. Explicit HTTP 204 finishes an empty range.
    Both formats retain strict completion/type checks and Apple host allowlists.
    Caps: 50 pages per format/range, at most two formats, 5 minutes overall.
    Opaque markers stay memory-only; only cursor_used is emitted. Regression
    fixtures cover cursor overlap, encoding, legacy fallback, year isolation,
    cycles, malformed/conflicting markers, HTTP 204 and absent output secrets.
    Protocol reference (independent client implementation):
    https://github.com/muz011/WaffleStore/blob/ae5a469e227824dec4f9ac157affc5c91be58dac/WFSPurchasedAppsViewController.m
    Its raw-response logging and persisted account state are NOT adopted.

11. Bridge 0.4.7 adds an explicit metadata-only availability check for a selected
    App Store ID. It uses existing-entitlement download endpoints and validates
    the exact requested identity, HTTPS package offer and nonempty sinf data.
    It returns only app identity; no URLs/sinfs/private metadata, no transfer,
    filesystem write, artwork, Purchase, installation or billing occurs.
    Positive checks are tagged license_check and kept only in the Apple login's
    memory, separately from purchase-history membership. Logout clears them.
    SDK mock regressions fail on any acquisition, transfer or filesystem call.
    Failed-range paging traces expose only numeric/boolean request controls and
    types at a fixed allowlist of schema paths. They never expose arbitrary
    field names, values, purchase entries, signed URLs, tokens or headers.
    User-triggered local export is supported by the native UI; no server upload.

12. Bridge 0.4.8 fixes premature year traversal termination confirmed by a
    user-exported sanitized 0.6.14 report: repeated pages for 2024, 2023 and
    2022 were incorrectly counted as three consecutive service failures,
    preventing requests for 2021 through 2008. Valid but incomplete ranges
    now keep their warning and allow older years to be requested. The global
    time bound, per-range page bound and service-failure breaker remain.
    Three consecutive transport/HTTP/schema failures still stop the scan;
    authentication, rate-limit and unsafe-redirect failures stop immediately.
    years_requested and oldest_year describe started requests only, without
    counting retries twice or treating reaching 2008 as complete history.

13. Bridge 0.4.9 tracks terminal evidence for every year in a frozen calendar
    year set from 2008. Bounded compatibility recovery rereads unresolved years
    only, at most once; successful recovery clears only that year's warning.
    The compatibility request uses the same SDK Machine identity, an in-memory
    GUID, fixed timezone, Configurator 2.18 UA and legacy Accept profile.
    Request parity evidence: WaffleStore WFSAppleIDDownloader.m lines 691-715,
    commit ae5a469e227824dec4f9ac157affc5c91be58dac. It is not proof that these
    parameters fix empty cursors. No guessed month/date filters are used.
    Trusted-host redirects must retain path/range/page/cursor/format/GUID controls
    before another HTTP request is sent. Partial/cyclic/repeated pages, errors,
    unknown kinds and bounds do not establish completion. Explicit terminal
    responses (is-complete=true or HTTP 204) must cover every calendar year.
    The native client separately checks contiguous coverage, current UTC year,
    attempts/pages and cross-counters; missing/forged complete proof is rejected.
    This confirms traversal of returned service ranges, not an absolute total of
    all historical licenses. DAAP local counts are not authoritative server totals.
    Apple account data, cookie/token/GUID values and financial history are not
    added to reports, logs or the billing server. Server/Docker remain untouched.

14. Bridge 0.4.10 retains all observed same-ID names (the former 32-title cap is
    removed; duplicate exact strings remain merged). The native list displays
    title variants separately while canonical account ownership, downloads,
    queue items and billing stay keyed to the exact App Store ID.
    A partial read can make one additional diagnostic GET to Apple's bag-named
    /commerce/account/purchases/count route for the first unresolved year.
    The route is live (unauthenticated response is 403/authentication), but its
    numeric units/scope are unknown. Fixed-path/trusted-host redirects and a
    64 KiB response limit apply. Only fixed count keys with integers 0..1e7 and
    allowlisted schema types leave the parser. Count values never certify full
    history or represent a verified distinct-app count. No acquisition/billing.
    Safe page observations add completion, fixed cursor state, bounded invoice/
    item counters and repeated-array flag. No cursor values/order IDs/financial
    entries/account data are included. Native validation strips unknown fields.
    Live 0.6.16 remained at 473 apps and 16/19 completed years; retries did not
    resolve 2022-2024. Full completeness is not claimed by this diagnostic build.

15. Bridge 0.4.11 separates a truly empty purchases array from a repeated
    nonempty array. Live 0.6.17 demonstrated 5, 2, 0 invoices for 2024 with an
    empty pagination-token and is-complete=false; calling the final empty array
    repeated_page was incorrect. Numeric traversal can end after TWO consecutive
    well-formed empty pages with explicit empty tokens, only after at least one
    nonempty invoice page and no used cursor. The first empty response is followed
    by a request for the next page: nonempty records there resume traversal.
    Complete coverage records carry end_reason=apple_complete or empty_tail and
    terminal_page. The latter is corroborated service exhaustion, NOT an explicit
    Apple completeness flag or an authoritative all-time account total.
    A first empty page without a terminal flag, missing/null/invalid continuation,
    nonempty repeats, unknown kinds, HTTP/auth errors and bounds remain incomplete.
    Native validation distinguishes the two reasons and the UI names them.
    No new acquisition, payment, IPA, account-storage or server flow is introduced.

16. Bridge 0.4.12 fixes misleading login error classification. Machine identity,
    bag preparation and SAP signer failures now have separate fixed public codes:
    auth_machine_identity, auth_bag and auth_signer. Other unclassified login
    failures and responses missing required session fields become auth_failed,
    not invalid_credentials. The latter remains only for missing/invalid input.
    Typed internal stages retain errors.Is/As; existing network, timeout,
    cancellation, HTTP 403 and two-factor continuation behavior is preserved.
    No raw error strings, Apple response metadata, snippets, credentials, tokens
    or machine identifiers are added to the public protocol. Regression tests
    cover classifications, wrapped causes, 2FA and private-data suppression.
    This corrects a confirmed reporting defect. It does not establish or claim
    to fix the underlying cause of the reported login failure on a physical Mac.
    Authentication requests, upstream timeouts and account storage are unchanged.
