The island has been dead since June. The two iOS apps in this repo shared one Xcode build directory: the Manager is a fork of the app, so both build a project, a scheme and a target of the same name out of a project file carrying the same internal ids, and Xcode's build database is keyed on exactly those. Only the source path differs, which is not enough to tell them apart. The Manager has no Live Activity target, so its build treated the app's extension as a leftover file and swept it, while the app's build picked up the Manager's object files. What shipped was an app whose embedded extension came from some older build and no longer matched the code around it, so the island had nothing to render. It only ever happened on the self-hosted runner. The hosted ones handed every job a fresh machine, so the directory was always empty and every build was a clean one. Each product now derives its build directory from its own folder, so no two can share one. Both release workflows also assert the extension is inside the IPA before publishing, because an app missing it installs and runs perfectly and says nothing about what is wrong.
FTStore Manager
A tiny companion app that installs and updates FTStore from a private FTStore Admin — after you import a signing certificate.
FTStore Manager is a stripped-down fork of FTStore (itself a fork of Feather, GPL-3.0). It keeps only the parts needed to:
- Import a signing certificate (
.p12+.mobileprovision). - Download FTStore from a private FTStore Admin, sign it with your certificate, and install it on-device (OTA) — no computer, no pairing file.
- Keep FTStore up to date on a Stable or Developer channel.
Everything else from FTStore (the store browser, the app library, tweaks, advanced signing options, the device-pairing install method) has been removed.
It has one screen and no account. The operator console that used to ride along as a
second tab is its own app now: admin/ — FTStore Admin. Nothing in
here needs a dashboard login, and nothing in here should ever grow one.
The PAT never ships in the app
FTStore lives in a private GitHub repo, so downloading it needs
a GitHub token. Embedding a token in an iOS app is not secure — anyone with the
.ipa can recover an embedded secret at runtime, no matter how it's obfuscated.
So the Manager never contains the token. Instead, FTStore Admin (the Next.js server you already run) exposes a small proxy that holds the token server-side and streams FTStore back to the app:
FTStore Manager ──HTTPS──▶ FTStore Admin proxy ──token (server-side)──▶ GitHub (private repo)
(no token) /api/ftstore-app/*
The only thing the app needs is your FTStore Admin address. The GitHub token stays server-side in FTStore Admin and is never exposed to the Manager.
Part 1 — FTStore Admin proxy (server)
Two endpoints were added to FTStore Admin (src/app/api/ftstore-app/). They reuse FTStore Admin's
existing server-side GitHub token (GITHUB_TOKEN / DB setting):
| Endpoint | Purpose |
|---|---|
GET /api/ftstore-app/manifest?channel=stable|dev |
Returns { version, updatedAt, size, downloadURL } for the channel. |
GET /api/ftstore-app/download?channel=stable|dev |
Streams the FTStore IPA bytes (the only path that touches the token). |
Channels
- stable → the newest non-
devrelease of the app repo that has anFTStore*.ipaasset. Version = the release tag (e.g.v2.8.2). - dev → the release tagged
dev. The version string never changes, so freshness is tracked by the asset'supdated_attimestamp.
Prerequisites: FTStore Admin must be reachable from your device, have GITHUB_TOKEN /
GITHUB_OWNER / GITHUB_REPO configured, and contain an FTStore*.ipa asset on a
stable release and/or on the dev release.
Part 2 — the app
- Identity: display name
FTStore Manager, bundle idapp.ftstore.manager(distinct fromapp.ftstore, so installing FTStore is never a self-install — that's why OTA works without a pairing file). - Config: inject the FTStore Admin address at build time via the
FTSTORE_ADMIN_BASE_URLuser-defined setting inFTStore.xcconfig(it flows intoInfo.plistasFTStoreAdminBaseURL), or use the built-in default. Nothing secret is committed. - Flow: the home screen shows FTStore's state (Not installed / Up to date / Update available) and one button. Tapping it downloads via the proxy, auto-signs with your certificate (prompting you to import one the first time), and installs over the air.
New / changed source (everything else is unmodified FTStore engine)
FTStore/Manager/
ManagerConfig.swift FTStore Admin base URL (Info.plist + override)
FTStoreAdminClient.swift Calls the FTStore Admin manifest endpoint
FTStoreUpdateManager.swift Decides install vs update (stable=version, dev=updatedAt)
ManagerHomeView.swift The installer screen — the whole app
ManagerSettingsView.swift Channel, install method, notifications, certificates
ManagerLegacyConsole.swift Wipes what the old in-app console left behind
FTStore/FTStoreManagerApp.swift @main entry (replaces FTStoreApp.swift)
Small edits: AppInstallManager gained an install(InstallRequest) entry point and
DownloadManager gained an optional auth-header parameter; ServerPackModel moved into
Backend/Server/Models/.
Where the console went
Up to manager-v1.0 this app carried the /admin dashboard as a second tab, hidden
behind a developer-mode switch (tap the version ten times). That surface now lives in
FTStore Admin, a separate app you sideload yourself — so the Manager is
one screen again and a normal user is never one gesture away from the worker controls.
If you used the in-app console, sign in once more in the new app: its session lives under
its own Keychain service and cannot be read across apps. The Manager wipes the leftover
one at launch (ManagerLegacyConsole), and you can revoke it server-side from
Settings → Users.
The installer deliberately stays account-free, so a fresh device is never locked out of getting FTStore onto it.
Building
macOS + Xcode required (this is a Swift/Xcode project — it can't build on Linux/Windows). Dependencies are vendored (no submodule init needed):
git clone --recurse-submodules https://github.com/FTStuff/FTStore.git
cd FTStore/manager
make # builds packages/FTStore-Manager.ipa (the Manager)
Or open FTStore.xcworkspace in Xcode, or run the Manager Release GitHub Action
(workflow_dispatch or push a v* tag).
The optional
LicensePlist/AltSourceKit/IDeviceKittenSwift packages remain linked (the OTA installer reuses a couple of helpers from them). They can be pruned in Xcode if you want a leaner build.
License & credits
GPL-3.0, inherited from FTStore / Feather. The original Feather copyright notices are
preserved (LICENSE). Huge thanks to:
- Samara / claration — Feather, which FTStore (and this) builds on.
- Zsign, idevice, Vapor, Nuke, *.backloop.dev, plistserver.