1. Scope and developer information
This policy applies to Expiry Pilot, the iOS/iPadOS and Android applications for tracking expiration, renewal, cancellation, payment, service, best-before, use-by and other important dates.
Developer: Luke Garrison
Privacy/support contact: lukegarrison.dev@gmail.com
Support: support.php
Expiry Pilot does not require an Expiry Pilot account and these builds do not operate a developer-hosted account/profile, synchronization, analytics, advertising or app-data server. Optional cloud backup uses your Apple iCloud or Google Drive account directly, and the optional AI scan assistant uses the AI provider and API key you choose.
2. Privacy summary
3. Data map
| Data / action | Where processed or stored | Purpose | External transmission |
|---|---|---|---|
| Tracked items, dates, profiles, history, notes, tags, costs, issuer/reference/location, renewal or account links | Private app storage | Core tracking features | None by default; can leave the device through an explicit export/share, iOS system backup where applicable, or optional encrypted cloud backup |
| Recurring-spend summaries | Derived locally from active records that contain costs, recurrence or auto-renew information | Show monthly/yearly equivalents, category totals, one-off costs and the charges projected for the next 30 days | None; currencies remain separate and are not sent for exchange-rate conversion |
| Primary item photos | Normalized JPEG in private app storage; portable-backup payload capped at 5 MB per item | Associate a visual reference with a tracked item | Only through a user-initiated export/share, iOS system backup where applicable, or optional encrypted cloud backup |
| Supporting attachment files | Private app attachment folder, max 25 MB each | Keep supporting files with a record | Only through a user-initiated export/share, iOS system backup where applicable, or optional encrypted cloud backup |
| Camera/image/PDF scan content and recognized OCR/QR/barcode results | Core recognition is processed on-device; scan result is held transiently for review | Suggest likely dates/title and other fields | By default none, except eligible-barcode lookup. If the AI assistant is enabled, up to three downscaled JPEG scan pages (or one live-camera still) plus decoded barcode/QR payloads can be sent directly to the chosen AI provider (with OpenRouter, onward to the company that runs the chosen model) |
| AI-provider API key and assistant settings | iOS: provider key in a device-only non-synchronizing Keychain item. Android: provider key encrypted with a non-exportable Android Keystore key in no-backup storage. Non-secret preferences are local. | Authenticate optional AI requests and remember provider/model/automatic-analysis choices | The API key is sent only to the chosen provider as required to authenticate key verification and AI requests. Keys are not included in Expiry Pilot backups/exports or logged by the app. |
| Eligible product barcode used for automatic enrichment | Sent only when a checksum-valid retail-code lookup is attempted after recognition | Request product name/category metadata | Eligible UPC/EAN/GTIN may be sent to Open Food Facts / Open Products Facts; arbitrary QR content is excluded from this lookup |
| Encrypted cloud-backup payload, recovery key and backup metadata | The complete backup payload is encrypted on-device with AES-256-GCM, then stored in the user's private iCloud/CloudKit storage on iOS/iPadOS or hidden Google Drive appDataFolder on Android. The authoritative recovery key is escrowed in that same cloud account. iOS also keeps a synchronizable Keychain cache of the cloud-backup key; Android keeps a Keystore-wrapped local cache in no-backup storage. Listing/validation metadata such as creation time, item count, encrypted size/checksum, key identifier, device model, app build, platform and a random installation identifier is stored as ordinary provider metadata outside the encrypted payload. | Optional backup, restore and per-device retention of verified copies (see section 8) | Sent directly to Apple iCloud/CloudKit or Google Drive; no Expiry Pilot server is in the path |
| Android linked Google Drive account state | Selected account email, cloud-backup state and this installation's random cloud-backup identifier are stored device-locally outside Android Auto Backup/device transfer; Drive access tokens are held only for an operation | Keep cloud backup tied to the selected Google account | Google Identity/Drive processes authorization and Drive operations |
| Reminder configuration | Private app settings + OS notification scheduling | Advance, cancellation-warning, due-day and snoozed local alerts, plus a generic notice when cloud backup needs attention | Handled through local/platform notification services; no Expiry Pilot notification server |
| Next Due widgets / shortcuts / Quick Settings surfaces | Local widget/app-group or app data used by the OS launcher/widget/shortcut framework | Show or open upcoming items and Add/Scan/Next due actions outside the main app | No developer-server transmission. When App Lock is off, tracked-item titles/categories/due information can be visible on these app-owned device surfaces. When App Lock is on, current iOS and Android builds suppress tracked-item content from their app-owned Next Due/data-bearing quick surfaces, and reminder notifications use private wording without item names. |
| Share / Add to Calendar / calendar (.ics) export | A user-invoked text copy, event draft or .ics calendar file is handed to the OS share, calendar or file-saving UI | Share one reminder, create an all-day calendar event, or export tracked dates (one item or all active items) for import into a calendar app | Only to the app, provider or location you choose. Android uses a calendar insert intent without calendar permission and saves .ics files through the system file picker; iOS uses the system EventKit editor and hands .ics files to the share sheet. Expiry Pilot never uploads or reads back an .ics file. |
| Renewal or account link | Stored with the item in private app storage; included in backups, CSV, share text and calendar exports | Keep the web address where you renew, cancel or manage the item | None until you tap Open link: your browser then opens that address and the website receives the request under its own terms. Expiry Pilot does not fetch, preview or check the link. |
| Premium product/transaction/entitlement information | Apple App Store / Google Play and bounded local entitlement state | Show localized prices, remove the active-item limit and authorize cloud-backup creation | Apple/Google process store information; the app does not receive full payment-card details |
| Manual backup / CSV exports | Destination you choose | Portable cross-platform backup, platform-specific complete/large backup, or readable spreadsheet export of active and archived items | Only if the destination/provider you choose syncs or stores it externally |
| Support form fields | Web/PHP session temporarily; email to developer | Respond to support | Yes, only what you submit |
4. Tracked records, profiles and lifecycle history
A tracked item can contain a title, category, profile/owner, primary date and date type; optional issue/start date, per-item date-format override, up to 10 advance reminder intervals, recurrence, amount/currency, auto-renew flag, issuer/provider, reference number, location, notes, tags, related dates, an optional renewal or account link (an http/https web address of up to 2,048 characters), one primary item photo, supporting attachments and lifecycle history. Monetary values and links are validated and bounded by the app before persistence/import.
Lifecycle history can record renewal, postponement, cancellation, completion and reactivation events, including later history corrections. Cancelled and completed items are archived rather than silently discarded and can be shown/hidden in the Library according to the app setting.
An active item marked to renew automatically is moved to its next cycle by the app once its date has passed — by its recurrence, or yearly when none is set — and a Renewed automatically history entry is recorded on the device. Renewing an item also moves related dates that fell on or before the old date. These updates are computed locally; nothing is sent to the provider of the subscription.
Deleting an item hides it at once and offers Undo for a few seconds (about 6 seconds; longer when VoiceOver is running on iOS or when Android's accessibility timeout asks for more time). The deletion is written when that window ends, when the app goes to the background, at the next deletion, and before any export, import, restore or Back up now you start, so a just-deleted item is not included in them. If the system ends the app before the deletion is written, the item is still there the next time you open it.
Library sorting, bulk actions (Mark done, Move to category, Assign to profile, Delete) and the month calendar work only on the records stored on your device; the chosen sort order is a device-local setting.
Profiles are local labels such as a person, pet, home or work context. There is no developer-hosted identity/profile service.
The free tier supports up to 5 active items; archived items do not count toward that limit. The optional AI scan assistant is available without Premium. Premium removes the active-item limit and permits creation of encrypted cloud backups; it does not cause your records to be sent to an Expiry Pilot server.
5. Camera, image, PDF, OCR, barcode and optional AI scanning
Scanning is user-started. Expiry Pilot first analyzes live camera frames, selected images and selected PDF documents on-device to rank likely expiration/due dates and infer a possible title. The analyzer can also use passport and ID-card MRZ structures and QR/barcode payloads, including conservative GS1 date fields, as supporting input.
Automatic eligible-barcode enrichment: after on-device recognition, Expiry Pilot may automatically look up an eligible checksum-valid UPC/EAN/GTIN code to request a product name/category from Open Food Facts / Open Products Facts. This lookup is separate from expiry-date detection; arbitrary QR text/URLs and invalid retail codes are not eligible, and scanning remains usable when the lookup fails or is unavailable.
- iOS/iPadOS: Apple Vision performs text and barcode recognition on-device, while VisionKit supports native document capture. Selected files are accessed through Apple system pickers/security-scoped access. PDFs can use selectable text first and locally render pages when further recognition is useful.
- Android: CameraX provides live camera frames and on-device ML Kit performs text/barcode recognition; live text recognition is limited to the on-screen guide area. Imported PDFs are copied temporarily into the app cache for local rendering and the temporary PDF is deleted after scanning.
The local scan safety limit is 100 MB per selected scan file. For a PDF longer than 50 pages, the first 50 pages are scanned and the app tells you that only those were scanned. Large images/PDF pages are downscaled/bounded for processing.
Optional AI scan assistant
The AI scan assistant is off by default and is available to free and Premium users. In Settings → Security & privacy → AI scan assistant, you can choose ChatGPT (OpenAI), Claude (Anthropic), Grok (xAI), Gemini (Google), DeepSeek or OpenRouter, enter your own API key, choose a model preset or custom model ID, and optionally verify the key with the provider. Once enabled, automatic analysis can be turned off so AI runs only when you request it for a scan.
The built-in presets in these reference builds are GPT-6 Luna, GPT-6.1 Sol and GPT-6 Astra; Claude Haiku 5.5, Claude Sonnet 5.5 and Claude Opus 5.5; Grok 4.7; Gemini 3.5 Flash-Lite and Gemini 3.8 Flash; DeepSeek V4.1 Flash (deepseek-flash); and, through OpenRouter, GPT-6 Luna (OpenAI), Claude Haiku 5.5 (Anthropic) and Gemini 3.5 Flash-Lite (Google). Custom model IDs remain available, but scan analysis requires a model that accepts image input.
OpenRouter: OpenRouter is a routing service. When you choose it, each scan is sent to OpenRouter, which passes it on to the company that runs the model you selected (for the presets, OpenAI, Anthropic or Google). The privacy settings of your OpenRouter account decide which companies may receive your scans; their processing is governed by OpenRouter's and that company's terms.
When AI analysis runs, Expiry Pilot sends at most three JPEG scan pages downscaled to a 1,568-pixel long edge, plus barcode/QR payloads already decoded on-device, directly to the selected provider. For live camera scanning, one captured still can be sent after the on-device scanner has locked on or after an explicit Analyze with AI action. Decoded barcode/QR payloads are marked to the model as untrusted data, not as instructions or reliable evidence. The provider reply can propose a name, category, date type, expiry date, issue date, issuer, reference number, cost/currency and notes. A high-confidence on-device date and a barcode-database product match remain authoritative; if the AI proposes a different date, it is offered as a separate choice.
If the provider reports a temporary rate limit or service error, the same request may be repeated once automatically; if a provider rejects the speed tuning that presets request, the request is repeated once without it. Each attempt is a separate request to your provider account. Provider failures never block the local scanner. API keys are not logged, exported or included in any Expiry Pilot backup. The provider may process, log, retain or charge for requests according to the provider account, plan and privacy terms you selected; Expiry Pilot does not control those provider-side practices.
6. Notifications, reminders and system surfaces
Expiry Pilot schedules local reminders for items you choose to track. Each active tracked date can use up to 10 normalized advance warning intervals plus a final same-day alert. Snoozing creates an additional reminder without changing the stored deadline; a snooze that would land on a day that already has a planned reminder for the same date is dropped so you are not alerted twice. Primary-date notifications can expose actions such as Renew, Snooze 7d, Mark done and Open; for items that renew automatically they offer Renew, Snooze 7d and Open only. Secondary-date notifications are limited to actions that do not complete the whole item.
For an active item marked to renew automatically, Expiry Pilot can add a separate last-chance cancellation warning on the primary date schedule. The selectable lead is Off, 1, 3, 7 or 14 days (default 7). If that lead already exists among the item's normal reminders, it is upgraded to the cancellation warning instead of scheduling a duplicate notification.
You can disable reminders, choose a reminder time, select Standard or Quiet notification behavior and choose whether notification content shows or hides the item name. While App Lock is on, notifications always use private wording without item names, whatever that setting says. Android also exposes a separate notification-vibration setting and an optional precise final-alert setting. On Android, advance reminders use WorkManager, which receives the nearest reminders first; the final same-day reminder can use an exact alarm when the user grants Alarms & reminders access, with a duplicate-safe WorkManager fallback. A reminder that could not be shown on time (for example while the phone was off) is still delivered later while it is the most timely one for that date, and a missed final alert gets one extra day. On iOS/iPadOS, reminders are local notifications that follow the device's local time, so they keep their day and time when you travel across time zones; the app maintains a rolling priority queue of up to 60 future requests to stay within iOS pending-notification limits. The final same-day alert is marked Time Sensitive so it can break through Focus unless you chose Quiet notifications or turned Time Sensitive notifications off in iOS Settings. After a restore or import that brings active items, iOS asks for notification permission if you have not decided yet.
If cloud backup needs your attention (for example you need to sign in again, Premium has ended, storage is full, a larger backup was found, or no backup has succeeded for a while), the app shows it in Settings and on Home and can post one notification per problem. That notification contains no item data and opens Cloud backup; on Android it uses its own low-importance Backup status channel, which you can turn off in system settings.
The optional Next Due home-screen widget can display up to the next five active items. On iOS/iPadOS, a minimal snapshot of the next five active items is stored in the app's private App Group storage for WidgetKit, and App Shortcuts provide quick Add, Scan and Next due access. On Android, the Glance widget reads local records, static Add/Scan launcher shortcuts are available, a context-sensitive Next due launcher shortcut may include the next item's title, and the Quick Settings tile opens scanning. These surfaces are local to the device but can be visible without opening Expiry Pilot.
Notification permission can be denied or revoked in operating-system settings. OS scheduling, Focus/Do Not Disturb, battery controls, device shutdown, time-zone changes and other platform policies can affect delivery; reminders are not a guaranteed emergency or legal-notice service.
7. Private item photos and supporting attachments
Each tracked item can have one optional primary photo selected through the system photo picker or captured with the camera. Expiry Pilot normalizes the saved copy to a private JPEG, preserves orientation/aspect ratio, does not intentionally upscale smaller images, and caps the stored image at 1200 px on the longest side. Portable-backup photo payloads are capped at 5 MB per item.
When you add a supporting attachment, Expiry Pilot copies the selected file into its private app storage. Each supporting attachment is limited to 25 MB. Stored paths are validated to remain inside the app's private attachment directory.
Deleting an item first saves the record deletion safely (after the Undo window described in section 4); only after successful persistence are that item's private primary photo and supporting attachment files eligible for cleanup. Unreferenced private files can also be removed by orphan cleanup after restore/sanitization. When a backup is imported or restored, a photo already on the device is kept rather than replaced by an older photo from the backup; a backup photo only fills a missing one.
Portable JSON v3 can include normalized primary item photos and supporting attachment binaries when you explicitly export a portable backup. The platform-specific Android complete ZIP and iOS large .epbackup formats can also carry supported media without changing the app's per-file safety checks. Optional cloud backup uses the app's complete backup payload, so supported photos and attachments can be included there too, but the cloud payload is encrypted on-device before upload. Exported media is bounded/validated and private device-local paths are not exported.
8. Backups, import, cloud backup and CSV export
Portable and platform-specific manual backup
The normal cross-platform transfer format is portable JSON v3. Current iOS and Android builds can use this format for records, categories, profiles, reminder lead times, saved locations, lifecycle history, normalized primary item photos and supporting attachment files. Portable JSON is capped at 128 MB; each embedded primary-photo payload is capped at 5 MB and each supporting attachment at 25 MB. Embedded binaries are SHA-256 integrity-checked, private device-local paths are not exported, and the backup is fully validated before imported data is committed. Portable v1 and v2 backups remain importable.
Android: in addition to Export portable backup, the current app provides Export complete backup, a platform-specific ZIP container that stores records and media separately so photos and attachments do not count toward the portable JSON 128 MB aggregate limit. It still enforces the app's per-media safety limits and requires enough device storage to prepare/import safely. Android normally writes portable JSON v3; only bounded grandfathered metadata can cause its portable exporter to use its extended JSON v4 contract, which is not the normal iOS/Android interchange path.
iOS/iPadOS: Export backup first writes the shared portable JSON v3 form. If a valid library cannot fit the 128 MB JSON/Base64 envelope, the app automatically falls back to its platform-specific binary v4 .epbackup container instead of making that library impossible to back up. This large-backup container is distinct from Android's complete ZIP and is not the cross-platform interchange format.
Manual import is additive: unrelated local records are preserved, exact portable duplicates are refreshed, genuinely different records remain separate, and categories/profiles are reconciled conservatively. When the backup holds an older version of an item whose only later changes on the device were lifecycle actions (renewed, postponed, completed, cancelled or reactivated), that older version is skipped instead of coming back as a duplicate, and an older local version is brought up to date from a newer backup in the same way. An item that was edited in other ways is kept as a separate copy. A free installation can import a backup file only when the resulting active library remains within the 5-active-item limit; archived items do not count. Premium removes that active-item limit. For Android↔iOS transfer, use a v3-compatible portable JSON backup.
Encrypted cloud backup
Cloud backup is optional and separate from manual export. iOS/iPadOS stores encrypted backups in the app's private CloudKit database in your iCloud account. Android stores them in the hidden appDataFolder of the Google Drive account you connect, using only the drive.appdata scope. The complete backup payload is encrypted on-device with the shared EPCB v1 AES-256-GCM container before upload. A 256-bit recovery key is also kept in the same cloud account so a new device signed into/connected to that account can restore. iOS keeps a synchronizable Keychain cache of that cloud-backup key, while Android keeps a Keystore-wrapped cache in no-backup storage; the cloud copy remains authoritative.
Backup-list metadata is stored separately from the encrypted payload so the provider can hold fields such as creation time, item count, encrypted size/checksum, key identifier, device model (for example the Android model name, or “iPhone”/“iPad”), app build, platform and a random installation identifier. The installation identifier is generated by the app, contains no personal or hardware information and only tells your devices' copies apart. Android keeps it in device-local storage outside Android system backup; iOS keeps it in a device-only, non-synchronizing Keychain item, which can remain after the app is deleted so that a reinstalled app recognises its own copies. Because the recovery key is kept with the same cloud provider, this is not represented as a “zero knowledge” or provider-inaccessible backup.
Which copies are kept: copies are kept per device so that a new or second device does not push out another device's latest backup. On Android, each installation keeps its three most recent verified copies, the newest copy of up to five other installations using the same Google account is kept, and the copy with the most items is never removed while every kept copy is smaller. On iOS/iPadOS, the iCloud account holds up to three copies; the newest copy, the copy with the most items and the newest copy of each other device are protected while the three slots allow. With three or more devices, the oldest other device's copy can be replaced, but never the copy with the most items. The restore list shows which device each copy came from and marks this device's copies.
Automatic backup is optional, runs only when data has changed and platform scheduling/connectivity constraints permit, and uses a Wi-Fi-only preference that is on by default. On Android, roughly once a day (at most every 20 hours) the app also checks that this device's latest copy still exists and backs up again if it does not. Before a device's first backup, if the cloud already holds a copy with more items than the device, automatic backup pauses and Back up now asks you to Restore first or Back up anyway. Creating a cloud backup — Automatic backup or Back up now — requires Premium. Listing, restoring and deleting existing cloud backups does not require Premium, and restoring a cloud backup is never limited by the free 5-active-item limit. Restore downloads, decrypts and fully validates the backup before applying additive/non-destructive import behavior.
Deleting all cloud backups from the feature removes every stored backup copy and the escrowed recovery key from that cloud account (on iOS the copies are removed before the key), turns automatic backup off, and does not delete data already on the current device. On Android, a small deletion marker (holding only the deletion time and a format number) is left in the hidden app folder so that your other Android devices using that Google account stop automatic backup and forget their cached key instead of silently re-creating backups; automatic backup then stays off on each of those devices until you turn it on again, or tap Back up now, on that device. Simply uninstalling Expiry Pilot does not automatically delete cloud copies from Apple/Google.
CSV export
CSV creates a spreadsheet-friendly export of active and archived items with the columns Name, Category, Owner, Status, Date, Type, Repeats, Auto-renew, Issue date, Cost, Currency, Issuer, Reference, Location, Additional dates, Renewal (or account) link, Tags and Notes. The file is UTF-8 encoded; where your region writes decimals with a comma, fields are separated by semicolons and amounts use decimal commas, so spreadsheet apps open it directly; cells that could be read as formulas are escaped. Primary photos and supporting attachment binaries are not embedded in CSV.
Calendar (.ics) export
You can export one item (from its details) or all active items (Settings → Backup & export) as a standard .ics calendar file. Each tracked date becomes an all-day event containing the item's title, date type and notes (on Android, the main date's event also shows the category and related-date events name the item they belong to) and, when set, the renewal link; alarms mirror up to five of the item's reminders at your reminder time. The file is saved or shared only where you choose; once imported, your calendar app or provider controls those events. Expiry Pilot never uploads or reads back the file.
System/device backup
iOS/iPadOS: the private application container, including local database storage, private item photos/supporting files and app settings, can participate in Apple device/iCloud backup according to iOS and your Apple backup settings. AI-provider API keys use device-only Keychain storage and are not part of an Expiry Pilot export/backup.
Android: the declared Auto Backup and device-transfer rules are allow-list based and include only the non-sensitive expiry_settings.xml preference file. Tracked records, media, AI-assistant state/keys, cloud-backup state, pending encrypted uploads and the local cloud-key cache are excluded from Android system backup/device transfer.
9. App Lock and device authentication
App Lock is optional. It uses operating-system device-owner authentication and locks the main app again after a genuine background transition.
- iOS/iPadOS: LocalAuthentication can use Face ID, Touch ID or device passcode depending on device configuration.
- Android: AndroidX Biometric can use supported biometric authentication or device credential.
Expiry Pilot does not receive or store biometric templates. The OS performs biometric matching and returns an authentication result to the app. If compatible device-owner authentication is unavailable, App Lock cannot remain enabled.
App Lock also changes app-owned external surfaces. On both current platforms, the Next Due widget suppresses tracked-item content while App Lock is enabled. Android also removes tracked-item data from its context-sensitive Next Due launcher shortcut and Quick Settings tile while locked. Reminder notifications use private wording without item names while App Lock is on, whatever the notification-content preference says; on iOS, turning App Lock on also removes already-delivered detailed reminders. Record-changing notification actions are not allowed to bypass App Lock: iOS keeps a protected Snooze/Mark done action in a small on-device queue and applies it only after authentication, while Android omits/fails closed on direct mutation actions until the app can be opened through its authentication gate.
On Android 13 and later, the app's preview in the recent-apps screen is hidden while App Lock is on, and on Android the scanner camera, live analysis, AI capture and product lookups stop while the lock screen covers the app.
10. Premium purchases and subscriptions
Expiry Pilot offers two Premium choices. The free tier supports up to 5 active items (archived items do not count) and includes the optional AI scan assistant. Premium adds unlimited active tracking and the ability to create encrypted cloud backups, including Automatic backup and Back up now:
- Lifetime Premium: one-time/non-consumable entitlement.
- Annual Premium: one-year auto-renewing subscription until cancelled through the relevant store.
Existing cloud backups can still be listed, restored and deleted without Premium, and restoring a cloud backup is not limited by the 5-active-item limit. Importing a manual backup file on the free tier remains subject to that limit.
Apple: the iOS/iPadOS app uses StoreKit 2 products, verified transactions and current entitlements. Google: the Android app uses Google Play Billing product, purchase and entitlement information and acknowledges completed purchases. Both apps keep bounded local entitlement state to avoid a false free state while store verification starts; a successful store verification is authoritative and can clear stale/revoked entitlement state. The annual cached grace window is bounded to 72 hours.
The app receives store information needed to display the current localized price and determine Premium access. The developer does not receive your full payment-card details from the app. Refunds, revocations, renewals, cancellations and purchase history are managed by Apple or Google under their respective systems.
There is no Expiry Pilot cross-platform entitlement server. Apple and Google purchases do not transfer between ecosystems.
11. iOS / iPadOS-specific permissions and platform behavior
- Camera: requested only when you choose live camera scanning or explicitly take a primary photo for a tracked item.
- Existing photos/files/PDFs: selected through Apple system pickers/security-scoped access; the reviewed build does not request broad Photo Library permission.
- Notifications: used for enabled local reminders and for a generic cloud-backup attention notice. The app keeps a rolling priority queue of up to 60 future requests and rebuilds local scheduling as app data/settings change. The app has the Time Sensitive notifications capability, used only for alerts on an item's due day (including a snoozed reminder that lands on that day); you can turn it off in iOS Settings.
- Face ID / device authentication: used only if App Lock is enabled; LocalAuthentication can fall back to other device-owner authentication supported by iOS.
- Calendar: used only when you choose Add to Calendar. Expiry Pilot creates a draft all-day event in Apple's system EventKit editor so you can review, choose a calendar and save/cancel it. Calendar (
.ics) export does not use calendar access: the file is handed to the share sheet. - Contacts notice: Expiry Pilot does not read contacts directly. The iOS 16 system Calendar editor can use Contacts while you review an event, which is why the app includes the corresponding usage description.
- StoreKit: communicates with the App Store for Premium products/transactions and shows store-localized pricing.
- CloudKit: the app has an iCloud/CloudKit entitlement for optional encrypted cloud backup in your private iCloud database. Automatic backup can use a registered background-processing task, with increasing pauses after interrupted attempts; opening the Cloud backup screen can also contact iCloud. The random installation identifier that tells your devices' copies apart is kept in a device-only Keychain item and travels only inside the backup metadata.
- AI provider access: when you enable the AI scan assistant, requests go directly to the selected provider. Provider API keys are stored in a device-only, non-synchronizing Keychain item and are not included in Expiry Pilot backups/exports.
- Widgets / App Shortcuts: WidgetKit uses the private shared App Group for the Next Due snapshot; App Intents/Shortcuts provide local Add, Scan and Next due actions.
- Privacy manifests: the reviewed app and widget declare no tracking and no collected data types in their bundled Apple privacy manifests. They declare the required-reason APIs they use: user defaults (CA92.1 in the app for its own settings; 1C8F.1 in the app and widget for the App Group they share) and, in the app, system boot time (35F9.1, to measure elapsed time on the device). The optional user-directed cloud-backup, AI-provider and barcode-lookup transmissions are nevertheless described explicitly in this policy.
The reviewed iOS build has no developer analytics/tracking endpoint. External app-data transmissions are limited to the user/provider paths described here: optional encrypted iCloud backup, optional AI-provider requests, eligible retail-barcode lookup, store/platform services, and destinations you explicitly choose.
12. Android-specific permissions and platform behavior
The reviewed Android manifest declares the following app-facing permissions/capabilities:
- CAMERA for live scanning and the user-initiated Take photo action on a tracked item; camera hardware is optional.
- POST_NOTIFICATIONS on Android versions where runtime notification permission is required.
- RECEIVE_BOOT_COMPLETED so reminder schedules can be reconciled after reboot and related time/app changes.
- SCHEDULE_EXACT_ALARM for the optional precise final same-day alert when Android grants the user-controlled Alarms & reminders special access; WorkManager remains the duplicate-safe fallback.
- VIBRATE for enabled notification vibration.
- INTERNET for Google Play Billing/services, eligible-product-barcode lookup, optional Google Drive cloud backup and optional AI-provider requests.
The app does not request broad all-files/media-library or calendar permission in this build. Existing scan files/images/PDFs and item photos are chosen through Android system document/photo-picker flows. Add to Calendar opens an all-day event draft through Android's calendar insert intent, so the chosen calendar app receives the draft only after your action. Calendar (.ics) and CSV exports are written to a location you pick in the system file picker. Plain-text sharing similarly uses the Android share sheet. Primary item photos and supporting attachments are copied/normalized into private app storage; supporting files are shared out for viewing only through a scoped FileProvider URI.
Android Auto Backup/device transfer now includes only the non-sensitive expiry_settings.xml preference file. Tracked records, the Room database, photos/attachments, AI-assistant preferences and keys, cloud-backup account/state, pending uploads and local cloud-key cache are excluded. For optional cloud backup, Google Identity requests the narrow drive.appdata scope; the selected Google account email and a random installation identifier are stored locally to keep operations pinned to that account and to tell this device's copies apart (the identifier is also written to each copy's Drive metadata), while access tokens are used for an operation and are not persisted by the app. AI keys are encrypted with a non-exportable AES-256-GCM Android Keystore key and stored in the app's no-backup directory.
The app uses notification channels for reminders and a separate low-importance Backup status channel for cloud-backup attention notices; each can be adjusted or turned off in Android settings.
13. Public support-page and contact data
If you use the support form, it asks for your name, email, subject and message, with optional platform, app version and device/OS information. Those fields are sent by email to the developer so the request can be answered.
The support page uses a temporary first-party PHP session for CSRF protection, rate limiting and post/redirect feedback. It also uses a honeypot and minimum-form-time check to reduce automated abuse. The form does not intentionally create an Expiry Pilot support-message database and does not add advertising or analytics scripts.
The web server/mail infrastructure may create ordinary technical logs, such as timestamps, request metadata or IP information, according to the hosting/mail provider configuration even though the PHP support email body itself does not add the visitor's IP address.
Support emails are retained only as reasonably necessary to answer the request, maintain support history, prevent abuse, resolve disputes or meet legal obligations. You can request deletion of a support conversation by emailing lukegarrison.dev@gmail.com, subject to legally required retention.
14. Third-party/platform services
Expiry Pilot is intentionally light on third parties. Platform services involved in normal use can include Apple App Store/StoreKit, Google Play Billing, Apple/Android local notification services, device authentication frameworks, system share/calendar editors, system file/photo/document pickers and Apple system device backup. Optional cloud backup uses Apple iCloud/CloudKit on iOS/iPadOS or Google Identity/Google Drive on Android. User-selected share/export/calendar destinations can involve another app or cloud provider you choose.
Android uses Google ML Kit libraries for on-device recognition and iOS/iPadOS uses Apple Vision. Core OCR/date recognition stays on-device. For automatic eligible-barcode enrichment, an eligible checksum-valid retail barcode may be sent to Open Food Facts or Open Products Facts to request product metadata.
If you enable the AI scan assistant, scan evidence and your authentication request are sent directly to the selected OpenAI, Anthropic, xAI, Google Gemini, DeepSeek or OpenRouter API. OpenRouter passes each request on to the company that runs the chosen model, within the limits set by your OpenRouter privacy settings. Those providers may apply their own account requirements, billing/credit limits, safety policies, logging and retention. Expiry Pilot does not proxy those calls through a developer server and does not control provider-side processing.
A renewal or account link you save is opened by your browser only when you tap it; the website it points to operates under its own terms.
Each platform/provider or destination app operates under its own privacy terms for its independent service.
15. Data retention and deletion
- Tracked items/history: remain until you edit/delete them, clear app data (Android) or remove/uninstall the app. A deletion is written once its short Undo window ends (or earlier, as described in section 4). Manual/cloud restore merges into the local library and does not delete unrelated records.
- Private item photos/supporting attachments: removed after their item is successfully deleted; orphan cleanup may remove unreferenced private files after restore/sanitization.
- Manual exports: remain at the destination you chose until you delete them there.
- Expiry Pilot cloud backups: remain in your private iCloud/Google Drive app storage until the retention rules in section 8 replace them, the cloud-backup feature deletes them, or the cloud provider removes them under its rules. Delete cloud backups removes every Expiry Pilot cloud copy and the escrowed recovery key and turns automatic backup off without deleting local device data. Uninstalling the app alone does not guarantee cloud-copy deletion.
- Cloud-backup installation identifier: on Android it is removed with the app's data; on iOS the device-only Keychain item can remain after the app is deleted and is erased when the device is erased. Copies in the cloud keep the identifier in their metadata until they are deleted.
- AI-provider API keys: are device-local and are not in Expiry Pilot backups/exports. Data already sent to an AI provider is subject to that provider's retention/deletion terms.
- System/device backups: retention/deletion follows Apple/Google/device-backup settings and provider policies; Android Expiry Pilot system backup is settings-only in the reviewed build.
- Purchase history: maintained by Apple/Google and is not erased by deleting the app.
- Support communications: can be deleted on request where no legal retention requirement applies.
Because Expiry Pilot has no developer account or developer-hosted cloud database for app records, there is no separate Expiry Pilot server-side account deletion operation for tracked records.
16. Security and responsible use
Expiry Pilot uses private app storage, validated attachment/photo paths, bounded file sizes, integrity-checked portable backups, atomic/safe record persistence patterns and optional OS authentication. Optional cloud-backup payloads are encrypted on-device with AES-256-GCM before upload; the recovery key is kept in the same private cloud account to support restore on a replacement device. AI-provider API keys use platform secure storage and are excluded from Expiry Pilot backups/exports. No software or cloud arrangement can guarantee absolute security.
Protect your device, Apple/Google accounts and any AI-provider API keys, keep the OS updated, and maintain independent backups for information whose loss would be serious. Expiry Pilot is a reminder/organization tool, not an authoritative registry. Scanned/AI suggestions and notifications should not be the sole basis for legal, medical, travel, insurance, food-safety, contractual or financial decisions.
17. Children
Expiry Pilot is not designed to create a developer-held child profile and does not knowingly operate a developer cloud service collecting children's app records. If a parent/guardian uses local profiles to organize dates for a child, those profile labels and records remain subject to the same local-first processing described above.
Store purchases remain subject to Apple/Google age, family and parental-approval controls.
18. Changes to this policy
This policy is written to match Expiry Pilot iOS/iPadOS 1.0.0 build 204 and Android 1.0.0 versionCode 207. If data practices, permissions, third-party integrations or backup/purchase behavior materially change, this page should be updated before or together with the relevant release, and App Store Privacy / Google Play Data safety declarations should remain consistent.
19. Privacy questions and contact
Luke Garrison
lukegarrison.dev@gmail.com