# 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), `web/` (the FTStore Admin server it talks to) and `admin/` (that > server's console app). 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. ## No operator surface lives here The `web/` dashboard used to ride along as a second tab (`FTStore/Admin/`), hidden behind a developer-mode switch. It is its own app now — [`admin/`](../admin) — and it must stay there: - **This app is for normal users.** One screen, no account, no tabs. Anything that needs a dashboard login belongs in `admin/`, never behind a hidden gate here. - The install path must stay account-free — `/api/ftstore-app/*` is UA-gated only, on purpose, so a fresh device can always get FTStore onto it. - `ManagerLegacyConsole.purge()` runs at launch and wipes what the old in-app console left behind (its Keychain session, its `manager.admin.*` defaults, the now-dead `FTStore.developerMode` flag). Keep it: an install that updated across the split is otherwise still holding a live server session nothing can reach. ## Where things live - New Manager code: `FTStore/Manager/*` + `FTStore/FTStoreManagerApp.swift` (the `@main`, which shows `ManagerHomeView` directly — there is no root/tab container). - 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` (`FTSTORE_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.