Puzzlebind — Privacy Policy

Last updated 2026-09-01 · applies to Puzzlebind for iOS and Android (com.gapfound.puzzlebind)

This page is generated directly from the app's data inventory, which is kept in the source repository and updated in the same commit as any change to how the app handles data. What follows is that inventory, unedited.


Status: no data collected, no data shared. That statement is literally true, not a marketing position. There is no server to collect anything to.


What leaves the device

Nothing, unless you deliberately export a file — a printable PDF, or an encrypted backup — and choose where to send it. Both are described below.

Puzzlebind has no backend, no account system, no login, no sync and no analytics. The app makes no network requests of its own — not at launch, not while you build a book, not while it generates puzzles or renders a PDF. All puzzle generation and all PDF layout happen on the device, offline.

The only outbound traffic the binary is capable of comes from the platform purchase SDK (RevenueCat), which talks to RevenueCat and the platform store to answer one question: has this install bought the one-time unlock. It is given an anonymous store-generated identifier and none of your content — no book titles, no word lists, no clues, no email, no device identifier we set.

What is stored on the device

ItemWhereEncryptedLeaves device
Books (titles, subtitles, author line, page size, structure)App private storage, one fileAES-256-GCMNever
Puzzle sections and word lists, including your crossword cluesSame fileAES-256-GCMNever
App settingsSame fileAES-256-GCMNever
Document encryption keyiOS Keychain / Android KeystoreProtected by the OSNever
Exported PDFApp cache, written only when you tap ExportNo — it is a PDF for you to printOnly where you send it
Encrypted backup fileApp cache, written only when you tap Create backupAES-256-GCM under a key derived from your passphrase by scryptOnly where you send it
Backup passphraseNot stored anywheren/aNever
Purchase entitlementNot stored by us; held by the App Store / Play Store receiptn/an/a

The whole library is serialised to JSON and sealed as a single document. The 256-bit key is generated on the device on first launch, held in expo-secure-store under the alias puzzlebind.vault.key.v1 with WHEN_UNLOCKED_THIS_DEVICE_ONLY, and never transmitted. Every write draws a fresh random 96-bit nonce, because reusing a nonce under one AES-GCM key would destroy the guarantee entirely.

The envelope header (format magic, version, nonce length) is passed to AES-GCM as additional authenticated data, so it is covered by the authentication tag and cannot be rewritten. If the stored file is modified by anything other than the app, decryption fails the tag check and Puzzlebind refuses to open it and tells you — it will not show you silently corrupted books.

What you can export

Two things can leave the device, both only when you choose:

  1. A PDF of your book, generated entirely on the device and handed to the operating system's share sheet. Where it goes from there — your printer, your files, a message, a print shop — is your choice and happens outside the app.
  2. An encrypted backup file (.puzzlebind), protected by a passphrase you set. The passphrase is stretched with scrypt (N=215, r=8, p=1) and the library is sealed under the derived key with AES-256-GCM, with a fresh random salt and nonce every time. It is handed to the share sheet like the PDF. We never see the passphrase and hold no copy of the file, and we cannot open it — losing the passphrase means losing the backup.

The backup's header, including the scrypt cost parameters, is authenticated as part of the encryption. A file whose work factor has been edited downwards to make it cheap to attack is rejected rather than opened.

There is no photo import, no document import, no audio, and no camera roll access in this app, so there is nothing else that could be attached to a book. Restoring a backup uses the system file picker, which grants the app access to the one file you choose and nothing else.

Permissions

Verified against the generated AndroidManifest.xml, not just the config. Only these two are present in a build:

PermissionWhyUser-facing
INTERNETThe purchase SDK asking the store whether this install has bought the unlock, and the purchase and restore flows themselves. Nothing else makes a network request.No prompt; not shown by Android
com.android.vending.BILLINGThe one-time in-app purchaseNo prompt

Explicitly stripped via blockedPermissions, because the libraries we depend on would otherwise add them by default:

ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, CAMERA, RECORD_AUDIO, READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE, SYSTEM_ALERT_WINDOW, VIBRATE

Every one of those was present in the generated manifest before it was blocked, contributed by transitive dependencies rather than by anything Puzzlebind does. The app has no feature that uses any of them, so each is removed at manifest-merge time rather than left in place unused. SYSTEM_ALERT_WINDOW — draw over other apps — is worth calling out specifically: an offline puzzle-book maker has no business holding it.

A few normal-level permissions arrive from transitive Android libraries rather than from our configuration, such as ACCESS_NETWORK_STATE and the biometric pair USE_BIOMETRIC / USE_FINGERPRINT that comes with the OS keystore integration behind expo-secure-store. None is a runtime-prompted permission, none grants access to personal data, and none is used to collect anything.

No location, no microphone, no contacts, no photo library, no camera, no background access, no notifications, no draw-over-other-apps.

On iOS, no permission usage strings are declared at all, because the app asks for nothing. The expo-secure-store config plugin would otherwise add NSFaceIDUsageDescription, so it is explicitly disabled (faceIDPermission: false), which deletes the key rather than leaving a prompt the app would never show. The keystore entry is not gated behind biometrics. The app ships a PrivacyInfo.xcprivacy declaring no tracking, no tracking domains and no collected data types, with required-reason declarations only for file timestamp, user defaults and disk space.

Updates, and what survives

An app update replaces the code, never the stored data. The key alias (puzzlebind.vault.key.v1) and the filename (puzzlebind.vault) are fixed, the envelope format is versioned, and nothing in the startup path deletes anything — erasing is reachable only from you tapping "Erase everything on this device" in Settings.

What DOES lose your books, by design:

That is the honest cost of the privacy promise: because we hold no copy and the key never leaves the device, there is no way for us to recover your books. The encrypted backup described above is the only way to carry a library to a new phone, and it is the only thing that survives any of the three events above. Make one before you change devices.

Your purchase

Puzzlebind is free to build with. A single one-time purchase unlocks full-book PDF export. There is no subscription and no recurring charge.

Because there is no account, your purchase is tied to your Apple ID or Google account rather than to anything we hold — which is exactly why "Restore purchases" works on a new device and why there is nothing for you to sign in to. If the purchase SDK is not configured in a given build, it is never initialised, and that build makes no network requests whatsoever.

Not present in this app

Children

Puzzlebind is rated for ages 18+ on both stores and is not directed at children. It collects no data from anyone, of any age.

Changes to this policy

If the way the app handles data ever changes, this page changes in the same commit, and the date at the top moves. Previous versions remain in the public git history.

Contact

Questions about this policy: gapfound.apps@gmail.com