SentryKey is a self-contained TOTP (two-factor authentication) authenticator for Garmin watches, with companion apps for Android and iOS. Scan your 2FA QR codes on your phone, sync them to your watch over Bluetooth, and read your rotating codes right from your wrist β no phone required after sync.
I'm looking for testers for the SentryKey Android app β now in closed beta on Google Play. It's free; try it out, kick the tires, and tell me what breaks or feels confusing.
Become a tester β 3 quick steps:
- Join the testers group: groups.google.com/g/sentrykey-testers β click Join group
- Opt in on Google Play: play.google.com/apps/testing/com.fennell.sentrykey β tap Become a tester
- Install SentryKey from the Play Store and give it a spin π
β οΈ Do both steps 1 and 2 β joining the group alone doesn't opt you in, and the Play opt-in only works once you're a group member.
Hit a bug or have feedback? Open an issue β it genuinely helps. π
Prefer to sideload? Grab the APK from the latest release.
- β Standalone watch authenticator β RFC 6238 TOTP codes generated on-device, even with no phone nearby.
- π Fully self-contained crypto β Base32, HMAC-SHA1, and SHA-1 implemented in pure Monkey C (no flaky native dependency).
- π· QR scanning β add accounts by scanning
otpauth://QR codes on your phone. - π One-tap Bluetooth sync β push your vault from phone to watch via the Garmin Connect IQ SDK.
- π¨ Premium dark UI β black background, brand-orange countdown ring that turns red in the final 5 seconds, auto-fitting account labels, and multi-account page dots.
- ποΈ Persistent & secure β vault survives watch restarts; phone secrets are encrypted at rest (Android Keystore AES-256-GCM / iOS Keychain).
- π Encrypted backups β export a passphrase-locked backup (PBKDF2 + AES-256-GCM) that's useless without the passphrase; plaintext export still available with a warning.
βββββββββββββββββββββββ βββββββββββββββββββββββ
β Android companion β β iOS companion β
β (Kotlin + Compose)β β (Swift + SwiftUI) β
ββββββββββββ¬βββββββββββ ββββββββββββ¬βββββββββββ
β "label:secret,label:secret" β
βββββββββββββββββ¬βββββββββββββββββ
β Connect IQ SDK (BLE)
βΌ
ββββββββββββββββββββββββ
β Garmin watch app β
β (Monkey C) β
β β’ Base32 decode β
β β’ HMAC-SHA1 / SHA-1 β
β β’ TOTP every 30s β
ββββββββββββββββββββββββ
Both companion apps serialize the vault to a simple label:secret,label:secret
string and send it to the watch, which parses, stores, and computes codes
locally. The watch never needs the phone again until you add a new account.
| Path | What it is |
|---|---|
source/ |
Garmin watch app (Monkey C) |
resources/ |
Watch strings, settings, drawables |
manifest.xml |
Watch app manifest (UUID, products, permissions) |
companion/ |
Android companion (Kotlin + Jetpack Compose) |
companion-ios/ |
iOS companion (Swift + SwiftUI) |
.github/workflows/build.yml |
CI: build all targets + publish releases |
Runs on 100+ round Garmin watches (Connect IQ API 3.2+) β the whole UI is resolution-independent (proportional countdown ring, round-screen-aware labels, auto-scaling fonts), so it adapts from 218px to 454px screens. Square/rectangular devices are intentionally excluded (the ring UI is built for round faces).
Families covered include fenix (5 Plus β 8), epix, Forerunner (55/165/2xx/9xxβ¦), venu (1/2/3/441/445), vivoactive (3β6), Instinct (2/3/Crossover/E), MARQ (1/2), Descent, Enduro, D2, Approach S7x, and the tactix / legacy hero series.
The full device list is in manifest.xml; add or remove products there.
Requires the Connect IQ SDK and a developer key.
# Build for a specific device
./build.ps1 -Device fenix8solar51mm
# Run in the Connect IQ simulator
./build.ps1 -Device fenix8solar51mm -Run
# Package a store .iq
./build.ps1 -ExportThe Android app ships in two distribution flavors that differ only in how they self-update:
| Flavor | Update mechanism | Goes to |
|---|---|---|
play |
Google Play In-App Updates (no install permission) | Google Play |
github |
Self-installs the APK from GitHub Releases (sideload) | GitHub Releases |
cd companion
./gradlew assembleGithubDebug # sideload build β app/build/outputs/apk/github/debug/
./gradlew assemblePlayDebug # Play build (uses Play In-App Updates)Only the github flavor carries the REQUEST_INSTALL_PACKAGES permission, so
the Play upload stays clean. CI builds and publishes the github flavor to
GitHub Releases.
Requires macOS + Xcode. See companion-ios/README.md for full setup (the Connect IQ Mobile SDK framework + URL-scheme handshake).
Every push of a v* tag triggers CI to build the watch binaries (all targets),
the Android APK, and publish them as a GitHub Release.
git tag -a v1.0.0-beta.x -m "release notes"
git push origin v1.0.0-beta.x- TOTP secrets are sensitive. On Android the vault is encrypted at rest with an AES-256-GCM key held in the Android Keystore (hardware-backed where the device supports it); on iOS they're stored in the Keychain. On the watch they live in app Storage in plaintext (Connect IQ has no keystore).
- Backups can be exported encrypted: the passphrase is stretched with
PBKDF2-HMAC-SHA256 (210k iterations) and the vault sealed with AES-256-GCM.
There's no passphrase recovery β lose it and the backup is gone. A plaintext
JSON/
otpauthexport is still available behind an explicit warning for moving accounts to other authenticators. - The
label:secretBLE sync string can optionally be encrypted end-to-end: set a sync passphrase in the phone app and the same passphrase in the watch's Connect IQ settings. The payload is then sealed with an HMAC-SHA1-based passphrase scheme (PBKDF2 β keystream + MAC, markerSKENC1:) that all three platforms implement from the same primitive. With no passphrase set, sync stays plaintext (and should be treated as secret material). This is defense-in-depth over an already-link-encrypted BLE channel, not an audited protocol. - This is a personal/hobby project, not a security-audited product. Keep a backup of your 2FA secrets elsewhere.
- Comma-safe label sanitization in the sync string
- Issuer-only label display option on the watch
- iOS TestFlight distribution
SentryKey is free forever β no paid tiers, no subscriptions, no ads. If it's useful to you, a donation funds new features and keeps the managed cloud at sentrykey.app running:
- π GitHub Sponsors
- β Ko-fi
Not a donor? Starring the repo and filing good bug reports helps just as much.
Made with β‘ for the wrist.