# FTStore Manager — project guide for AI agents FTStore Manager is a **stripped-down fork of [FTStore](https://github.com/FTStuff/FTStore)** (itself a fork of [Feather](https://github.com/claration/Feather), GPL-3.0). Its only job: **install and update FTStore** from a **private** FTStore Admin, after the user imports a signing certificate. > **Monorepo.** This used to be the separate `FTStuff/FTStore-Manager` repo; it now > lives in `manager/` inside `FTStuff/FTStore`, alongside `ios/` (the app it > installs) and `web/` (the FTStore Admin server it talks to). Read the root > [AGENTS.md](../AGENTS.md) first — it owns the tag namespaces (`manager-v*`, > `manager-dev`) and the rule that `scripts/apply-rebrand.sh` must never touch > this directory. `Zsign` and `IDeviceKitten` are submodules here, not vendored > copies; clone with `--recurse-submodules`. ## Brand / identity - App name `FTStore Manager` · bundle id **`app.ftstore.manager`** (must differ from `app.ftstore` so installing FTStore is never a self-install → OTA works without pairing). - Internal Xcode target/scheme/product name is still `FTStore` (only the display name and bundle id changed), so `make` and the project file work unchanged. ## How it works (the important part) - **The GitHub PAT is never in the app.** Downloads go through the **FTStore Admin proxy** (`FTStore Admin` repo: `src/app/api/ftstore-app/manifest` + `/download`), which holds the token server-side. The app only knows the FTStore Admin base URL. - **Channels:** `stable` = newest non-`dev` release with an `FTStore*.ipa` asset (version = tag); `dev` = the `dev`-tagged release, tracked by the asset's `updatedAt` (the version string is constant). Compare logic lives in `FTStore/Manager/FTStoreUpdateManager.swift`. - **Install:** OTA only (itms-services via Vapor `ServerInstaller`). The device-pairing method was removed. `InstallPreviewView` already uses OTA for a non-self-install. ## Where things live - New Manager code: `FTStore/Manager/*` + `FTStore/FTStoreManagerApp.swift` (the `@main`). - Reused engine (unchanged): `Backend/Storage`, `Backend/Server`, `Backend/Observable` (`DownloadManager`, `AppInstallManager`, `OptionsManager`, `UpdateManager` [inert]), `Utilities/Handlers` (sign/archive/cert/zsign/tweak), `Utilities/{FR,CertificateReader,MachO,ARDecompression}`, `Extensions`, and the certificate/install/signing-setup views under `Views/`. - Config: `FTStore.xcconfig` (`FEATHER_PRODUCT_BUNDLE_IDENTIFIER`, `FTSTORE_ADMIN_BASE_URL`) → injected into `Info.plist` (`FTStoreAdminBaseURL`). **Never commit a real PAT.** ## Integration seams (when changing the engine) - `AppInstallManager.install(InstallRequest)` is the Manager's entry into the existing download → cert-gate → sign → install pipeline (reuses the private `_startStoreDownload`). - `FTStoreUpdateManager` reads installed state from `Storage.newestSignedApp(identifier: "app.ftstore")`. ## Dependencies Vendored (no submodules): `Zsign`, `NimbleKit`, `AltSourceKit`, `IDeviceKitten`. Remote SPM: `Vapor`, `Zip`, `SWCompression`, `Nuke`, `LicensePlist`. `AltSourceKit`/`IDeviceKitten`/ `LicensePlist` stay linked because the engine still references a few helpers from them; they can be pruned in Xcode if desired. ## Build / validate - macOS + Xcode only. `make` → `packages/FTStore-Manager.ipa`, or the `Manager Release` GitHub Action. - Static checks (CI/Linux ok): `plistlib`/`json` parse, and **grep the repo + built binary for `ghp_` / any token — there must be none** (the proxy design guarantees it). ## GPL-3.0 Keep `LICENSE` and Feather/FTStore attribution intact; keep source public.