Privacy & Security
How FableFrog handles your data, secures your credentials, enforces network security, and what stays on your device.
Data Handling
FableFrog's approach to data is simple: we don't collect any. The app's App Store privacy label is "Data Not Collected," and that's not marketing language. It's a literal description of how the app works.
Device-to-Server, Nothing In Between
FableFrog is a pure client application. All of your data flows directly between your iPhone and your Audiobookshelf server. There is no intermediary service, no cloud relay, no analytics backend. When you browse your library, stream an audiobook, or sync your progress, those requests go straight to the server you configured, and nowhere else.
There is no FableFrog account and no FableFrog backend. The app does read three static files from fablefrog.app, this website: the privacy policy, the terms of service, and the Service Notices feed. Those requests carry no authentication header, no query parameters, and no device or account identifier - they're the same anonymous GET your browser makes when it loads this page, and there's deliberately no mechanism by which they could identify you. Nothing about your library, your listening, or your server ever leaves your device.
No Analytics, No Telemetry, No Crash Reporting, No Ads
FableFrog does not include any analytics frameworks, telemetry SDKs, crash reporting services, or advertising networks. There are no hidden network calls to third-party services. The app contains no code from Google Analytics, Firebase, Sentry, Bugsnag, or any similar service. Your listening habits, library contents, and usage patterns are never transmitted to anyone.
This means that when you experience a crash, FableFrog doesn't automatically know about it. If you encounter a bug, the best way to report it is by emailing paul@devitodigitalsolutions.com with a description of what happened.
Credential Security
Keychain Storage
Your Audiobookshelf credentials (username, password, and authentication tokens) are stored exclusively in the iOS Keychain using the kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly protection level. This means:
- Credentials are encrypted at rest by the Secure Enclave
- They're only accessible after the device has been unlocked at least once since boot
- They cannot be transferred to another device through backup or migration
- They're never stored in files, UserDefaults, databases, or any other less-secure mechanism
Token Handling
FableFrog uses a dual-token authentication model:
- Access token: a short-lived token sent with API requests to authenticate your session. Included in the
Authorizationheader on most requests. - Refresh token: a longer-lived token used to obtain new access tokens when the current one expires. Token refresh is handled automatically and transparently. You won't be prompted to re-authenticate unless the refresh token itself has expired or been revoked.
FableFrog implements single-inflight token refresh deduplication, meaning if multiple requests need a token refresh simultaneously, only one refresh request is made and the result is shared. This prevents race conditions and redundant server calls.
Sign Out Behavior
When you sign out of FableFrog, the app clears all server-synced data:
- All authentication tokens and stored credentials are removed from the Keychain
- All cached library metadata (covers, book details, chapter lists, author information, progress, sessions, bookmarks, collections) is deleted
- All in-memory state is cleared and background services are stopped
Your per-book settings (playback speed, skip intervals, volume boost), ratings, playlists, favorites, and series following are preserved on the device but isolated to your account. If you sign back in, they're restored automatically. If a different user signs in on the same device, they won't see your data — see Multi-User Device Safety below.
Downloaded audiobook files and app-level preferences (theme, text size) are also preserved across sign-out.
Network Security
TLS Enforcement
FableFrog allows plain HTTP connections, because self-hosted Audiobookshelf servers frequently don't have TLS - a box on your LAN, a Tailscale address, a local IP. Refusing those connections outright would make the app unusable for a large share of the people it's built for. The login screen defaults to HTTPS and the scheme picker steers you toward it.
If your server uses HTTPS with a valid certificate (e.g., from Let's Encrypt), connections are encrypted end-to-end with no additional configuration needed. Using HTTPS is always recommended, especially for connections over the internet.
That allowance is scoped to your server, not to ours. fablefrog.app - the only first-party host the app talks to - is pinned back to full App Transport Security: TLS 1.2 minimum, forward secrecy required, insecure HTTP refused. The endpoints where both ends are under our control get no leniency, because they don't need any.
Token in URL for Media Endpoints
There is one important caveat to be aware of: for media streaming endpoints (audio file downloads and playback), the authentication token is included as a URL query parameter rather than in the Authorization header. This is a constraint of the Audiobookshelf API. The media endpoints require token-based URL authentication because iOS's media player framework (AVPlayer) does not support custom headers on media requests.
This means your access token may appear in server logs for media requests. If you're concerned about this, ensure your server logs are secured appropriately. The token is still short-lived and can be revoked, limiting the window of exposure.
Authorization Header
For all non-media API requests (library browsing, progress sync, metadata fetching, authentication, and settings), FableFrog sends the access token in the standard Authorization header. This is the more secure approach and is used wherever the API supports it.
Service Notices
A Service Notice is a short advisory FableFrog shows inside the app when something is known to be broken - usually a dependency the app relies on, like the Audible pages that Upcoming Releases reads release dates from. It exists because a fix can be written in a day and still take a week to reach you through App Store review, and during that week the app has no other way to say "we know, it's coming."
Notices are published as a static JSON file on this website and read by the app. That makes the channel useful, and it also makes it worth being strict about, so the design is deliberately narrow.
It Can Only Show You Words
A notice carries a title, a body, a severity, a date, and at most one link. That is the entire vocabulary, permanently. It carries no actions, no deep links, no feature flags, and no kill switch - nothing the app changes its behavior based on. FableFrog cannot be reconfigured, degraded, or remotely controlled through this channel, because the channel has no way to express any of those things.
The reasoning is worth stating plainly: publishing a notice means pushing to a website, which is a much weaker gate than App Store review. So the protection isn't "only the right person can publish" - it's that the worst a notice can do, no matter who wrote it, is show you some text and a link to an allowlisted address. Links are checked against that allowlist when the feed is read, not when you tap, and are never opened for you.
The Fetch Tells Us Nothing
The feed is fetched when you bring the app to the foreground, at most once every four hours, over its own connection with no authentication header, no query parameters, and no device or account identifier. There's no counter, no read receipt, and no way for us to learn who saw what. Notice text is delivered in your language, but which language your device asked for is never reported back.
The downloaded file is cached in the system-managed cache directory. It's public content anyone can fetch, holds nothing about you, and is re-checked against the same rules every time it's read - a tampered cache file gets no more trust than a tampered download.
Notices Retire Themselves
Every notice has a mandatory expiry date, and most also name the version that fixes the problem. Once you're on that version, the notice stops appearing for you. An advisory that outlives the problem it describes is worse than no advisory at all.
Where They Appear
A notice appears on one named surface: the whole app, Upcoming Books, or ReadMeABook. A notice naming anything else is dropped rather than promoted to the most prominent spot.
Notices are never modal and never block the app. If your server connection needs attention, that banner takes the top of the screen and the notice waits its turn - it comes back intact once the connection banner clears.
Dismissing a notice hides its banner on that device. It doesn't delete it: every live notice, dismissed or not, stays listed under Settings > About > Service Notices, which is also where you go to check whether anything is known to be wrong. Dismissals are stored on your device as a list of notice IDs and are never synced or transmitted.
Apple Watch
The Apple Watch app is a remote control for your phone. It never contacts your Audiobookshelf server, and it has no way to: it holds no server URL, no token, and no credentials.
What the Phone Sends
Your iPhone pushes the watch what it needs to draw the Now Playing screen: the title and author of the current book, your position and the book's duration, the speed, the chapter list and which chapter you're in, the sleep timer and its presets, your skip durations, your theme's colors, and a small cover thumbnail. Nothing in that payload can be used to reach your server.
Commands travel the other way - play, pause, skip, chapter, speed, timer - and your phone carries them out.
How Often It Sends
Updates go out about every 2 seconds while the watch is reachable and about every 30 seconds when it is not, with an immediate push whenever something changes state. The payload is capped: the phone drops the cover art first, then the chapter list, rather than sending a very long book's worth of data at your watch.
The Complication Snapshot
The complication reads a small snapshot stored in the watchOS app group - which book, and how far in. It holds no authentication material.
When you sign out on your phone, FableFrog clears the snapshot by pushing an empty one. That push lands the next time the watch app runs, so a watch that is off your wrist and out of range can still show the last book on its face until it reconnects.
What Stays on Device
Downloaded Files
Downloaded audiobook files are not deleted when you sign out. This is a deliberate design decision. Audiobook downloads can be large (hundreds of megabytes to several gigabytes), and users who sign out and back in (for example, when switching servers or troubleshooting) would not want to re-download their entire offline library.
Downloaded files contain only audio data. They do not contain authentication tokens, credentials, or personally identifiable information. They are stored in the app's sandboxed container and are not accessible to other apps.
Per-User Local Data
Your per-book playback settings (speed, volume boost, skip intervals), star ratings, playlists, favorites, and series following persist across sign-out. These are scoped to your account — if you sign back in, they're automatically restored. Other users who sign in on the same device never see your data.
App Preferences
App-level preferences (your selected theme, text size, and other non-server-specific settings) persist across sign-out and sign-in cycles. These preferences are not user-scoped and contain no sensitive information.
Multi-User Device Safety
FableFrog isolates per-user data on shared devices. Each user is identified by their account and server combination, and all local-only data — book settings, ratings, playlists, favorites, series following, and file-based caches — is partitioned accordingly. This means:
- Different accounts on the same server each have their own local data
- The same account on different servers is treated as a separate user
- No user data is deleted on sign-out — it's simply invisible to other users
- Signing back in restores everything right where you left it