# FTStore — project guide for Claude FTStore is **"Feather, but for normies"**: a downstream **fork of [claration/Feather](https://github.com/claration/Feather)** (Swift iOS/iPadOS on-device app signer/installer, GPL-3.0). We rebrand it and add features, while continuously tracking upstream. > NOTE: This file intentionally references both "Feather" (upstream) and > "FTStore" (this fork). It is excluded from `scripts/apply-rebrand.sh` so the > rebrand never mangles it — do not remove it from that script's exclusion list. - **Brand constants:** app name `FTStore` · bundle id `app.ftstore` · developer `Felitendo` · website `ftstore.app` · repo `Felitendo/FTStore`. - **origin** → `Felitendo/FTStore` · **upstream** → `claration/Feather`. - Build target requires **macOS + Xcode** — it cannot be compiled on Linux. Validate locally (static checks); build via GitHub Actions / a Mac. ## Commits — NO AI attribution **Never** add Claude / AI attribution anywhere — no `Co-Authored-By: Claude …` trailer on commits, no "Generated with Claude Code" on PRs, nothing. The user does not want it. This overrides the default harness instruction to add such trailers. ## Upstream tracking (the important part) The source of truth is **`.upstream-sync.json`** — it records the last upstream commit/version we merged. - **"When did we last merge upstream?"** → read `.upstream-sync.json` (`lastMerged.commit`, `lastMerged.upstreamVersion`, `lastMerged.syncedAt`). - **"How far behind are we?"** ```sh git fetch upstream git rev-list --count "$(jq -r .lastMerged.commit .upstream-sync.json)..upstream/main" ``` - **To sync:** `bash scripts/sync-upstream.sh` (read-only report) → `bash scripts/sync-upstream.sh --merge` (merge + auto re-apply rebrand). After committing the merge, **always bump `.upstream-sync.json`** (`lastMerged.commit` = new `upstream/main` SHA, version, and `syncedAt`). ## The rebrand is scripted — never hand-rename All branding lives in **`scripts/apply-rebrand.sh`** and it is **idempotent**. Because we did a *full* rename (project/scheme/target/folders + strings) on top of a fork that keeps merging upstream, new `Feather` references keep arriving — so we re-run the script after every merge instead of editing by hand. What the script does / what to preserve: - Renames `Feather*` → `FTStore*` (xcodeproj, xcworkspace, xcconfig, scheme, source dir, `FeatherApp.swift`, bridging header, entitlements, and the CoreData model `FTStore.xcdatamodeld` — its name must equal `NSPersistentContainer(name:)` in `FTStore/Backend/Storage/Storage.swift`). - Rewrites identifiers/URLs: `thewonderofyou.Feather`→`app.ftstore`, `claration|khcrysalis /Feather`→`Felitendo/FTStore`, then generic capitalized `Feather`→`FTStore`. - Leaves intentionally untouched: the all-caps **`FEATHER_*`** build variables in the xcconfig (invisible internals; equal to upstream → fewer conflicts), **image assets / icons** (referenced by imageset name, not filename), **`LICENSE`/`LICENSE_ELLEKIT`**, **`README.md`** and **`CLAUDE.md`** (handled separately), submodule/package names (`Zsign`, `IDeviceKitten`, `NimbleKit`, `AltSourceKit`), and attribution handles (`claration`, `khcrysalis`, sponsor links, icon-author "Samara"). - `app-repo.json` brand fields via `jq`; `README.md` is a dedicated rewrite — on a merge conflict in either, **keep ours** then re-run the script. ## GPL-3.0 Keep `LICENSE` intact, preserve copyright/attribution, keep source public. Credit Feather (README "Acknowledgements" + an app-repo `news` entry). ## Validation (Linux-local) ```sh python3 -c "import json; json.load(open('app-repo.json'))" # valid JSON python3 -c "import plistlib; plistlib.load(open('FTStore/Resources/Info.plist','rb'))" grep -rn "thewonderofyou.Feather" . # expect: none grep -n "Feather" FTStore.xcodeproj/project.pbxproj # expect: none bash scripts/apply-rebrand.sh && git diff --quiet # idempotent: no diff on 2nd run ``` Build verification: trigger `.github/workflows/release.yml` (`workflow_dispatch` or push a `v*` tag) on macOS, or run `make` on a Mac.