uttrflow / uttrflow/uttrflow-swift
Model downloads follow the hub's main branch with no pinned revision or checksum, and the suggestion model download sends any Hugging Face token found on the Mac
- Dominant language
- Swift
- Stars
- 4
- Forks
- 17
- Avg merge
- 3h 32m
- Merged PRs (30d)
- 277
Description
## What happens
App code is pinned (`Package.resolved` is committed, and every workflow action is pinned by SHA). The models the app downloads and runs are not:
- **Speech tokenizer.** It is fetched from `https://huggingface.co//resolve/main/` (`Sources/UttrflowSpeech/TokenizerDownload.swift:11`).
- **Speech weights.** `WhisperKit.download(variant:downloadBase:progressCallback:)` is called with no revision (`Sources/UttrflowSpeech/WhisperKitBackend.swift:135-139`), so it takes whatever the repository's default branch holds at install time.
- **Suggestion model.** It is resolved with `ModelConfiguration(id: identifier)`, again with no revision (`Sources/UttrflowLocalModel/CachedSnapshot.swift:182-184`).
- **No integrity check.** The only check on what arrives is a size floor (`minimumWeightBytes`, `CachedSnapshot.swift:167`, and the speech store's install check). Nothing compares a hash.
- **Token.** The downloader is `#hubDownloader()` (`Sources/UttrflowLocalModel/MLXCandidateScorer.swift:67`). It expands to `HubClient()` (mlx-swift-lm, `HuggingFaceIntegrationMacros.swift:22`), which uses `tokenProvider: .environment` (swift-huggingface, `HubClient.swift:118`). That provider reads `HF_TOKEN`, `HUGGING_FACE_HUB_TOKEN`, `HF_TOKEN_PATH`, `$HF_HOME/token`, `~/.cache/huggingface/token` and `~/.huggingface/token` (`TokenProvider.swift:17-25`). On a Mac where the person has ever used the hub's command-line tools, their personal access token is attached to the app's model download.
- **Endpoint.** The same client honours `HF_ENDPOINT` for the download host (`HubClient.swift:186-194`).
## Why it matters
- **Supply chain.** A change pushed to a model repository's main branch reaches every new install without a release of this app. Nobody reviews it, and nothing in this repository can tell. Model weights and tokenizer files are code-adjacent: tokenizer JSON drives how dictated and typed text is processed.
- **Reproducibility.** Two installs a day apart can run different models, which invalidates the accuracy and memory measurements in `Docs/`.
- **Privacy.** Sending a developer's hub token ties this app's downloads to their personal hub account, and the app has no reason to authenticate to fetch public models.
## Acceptance criteria
- Each downloadable model names a commit revision next to its identifier (the model catalogue), and the download uses it: `resolve//` for the tokenizer, and a revision argument for WhisperKit and `ModelConfiguration`.
- After download, file sizes plus a SHA-256 of the weights and tokenizer files are checked against values recorded with the revision, and a mismatch is a failed install.
- Model downloads use an anonymous client (`HubClient(tokenProvider: .none)` or equivalent) and ignore `HF_ENDPOINT` in release builds.
- `Docs/speech-model-install.md` and `Docs/predict-llm.md` say how to bump a revision.
Contributor guide
Research direction
Start with Sources/UttrflowSpeech/TokenizerDownload.swift, WhisperKitBackend.swift, CachedSnapshot.swift, and MLXCandidateScorer.swift, then inspect the referenced HubClient and TokenProvider implementations. Map each model identifier to its revision and recorded hashes, and review Docs/speech-model-install.md and Docs/predict-llm.md for the revision-bump process. Done means downloads are pinned, verified by SHA-256, anonymous, and release builds ignore HF_ENDPOINT.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- huggingface, macos, swift
- Domain
- desktop-dev, documentation, machine-learning, security
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100