Finyvo Legal
Security
This page is about how Finyvo is built underneath. The privacy policy answers what data of yours is handled, what for, and with whom; this is the how: where each thing lives, what protects it, which decisions we made, and which ones we haven't made yet. No seals, no superlatives.
Table of contents
This page is about how Finyvo is built underneath. The privacy policy answers what data of yours is handled, what for, and with whom; this is the how: where each thing lives, what protects it, which decisions we made, and which ones we haven’t made yet. No seals, no superlatives.
The short version
| Question | Short answer |
|---|---|
| Where does your financial data live? | On your iPhone and, with sync enabled, in your private iCloud. Some features send data to the backend for processing. |
| Is there an account or password? | No. The app identifies itself with a cryptographic key that is born inside your iPhone’s Secure Enclave and never leaves it |
| What protects the traffic? | HTTPS with TLS 1.2 or later, on every connection |
| Where does the backend run? | On Cloudflare Workers, with D1 and KV as its only storage |
| Where are the providers’ keys? | Only on the backend, as Cloudflare secrets. None of them ships inside the app |
| Can you turn off what goes out to the internet? | iCloud and AI have separate controls. Converting Excel uploads the file even with AI off. |
| Any security certifications? | No. No SOC 2, no ISO 27001. The detail is in What isn’t there yet |
On your iPhone
The financial database is stored in SwiftData inside the iOS-protected app container. Calculations and export run on the device. Preferences use system settings; category memory belongs to your database.
Keychain stores the App Attest key identifier and installation identifier with WhenUnlockedThisDeviceOnly access, and the session token and expiry with AfterFirstUnlockThisDeviceOnly, allowing background reuse after the first unlock. They do not sync through iCloud Keychain and may survive uninstallation.
Apple Pay captures are first written as local evidence, then processed. Active connections belong to each device; financial and review records may sync through iCloud. Pausing a connection does not erase that evidence.
Automatic cleanup applies limits of seven scheduled backups and three pre-migration backups, while protecting the latest backup, manual and incident backups, and those currently in use. Private mode hides amounts in viewing surfaces but does not encrypt data or replace the iPhone lock.
Your iCloud copy
If you leave sync on — it is on by default — your data is replicated to the private database of your own iCloud account, inside the iCloud.com.moiszr.finyvo container. That is Apple’s infrastructure and the account is yours: Apple encrypts it in transit and at rest, and Finyvo cannot query it. It does not appear in any console of ours. If you have Apple’s Advanced Data Protection enabled, that copy is additionally end-to-end encrypted.
You can turn it off in Settings → Sync → iCloud. With the current sync system, the change takes effect while you use the app. If your installation still requires a restart, Settings shows that it will apply on the next launch. This does not delete the iCloud copy or disable the app’s other data flows.
Turning it off does not delete what was already uploaded. The App has no “delete my iCloud copy” button: to remove it you go to iOS Settings → [Your name] → iCloud → Manage Account Storage → Finyvo.
Exactly what gets replicated, and what happens with your display name, is in the privacy policy.
How the app authenticates
Finyvo has no accounts. There is no email, no password, no login for anyone to guess, leak, or steal from you. The backend checks something different: that the caller is the real app, running on a real device.
The key. On first launch, the App generates a cryptographic key inside the Secure Enclave. The private half never leaves that chip: the app can’t read it, iOS can’t, and neither can we. It can only be asked to sign.
The handshake, step by step:
- The app requests a challenge from
/auth/challenge. The backend returns a random number that is single-use, with a 5-minute lifetime. - The app asks the Secure Enclave for an attestation over that challenge and sends it to
/attest/verify. This is where Apple comes in: the attestation proves the key was born on a real device and that whoever is using it is a legitimate copy of Finyvo. - If it checks out, the backend burns the challenge — a used one is never accepted twice — and issues two things: an installation identifier and a session token that expires after 15 minutes.
- From then on, every call to
/fx/*,/brand/*,/ai/*, and/import/*travels with that token. No feature of the App uses a shared anonymous token. - When the token has a couple of minutes left, the app renews it on its own: new challenge, new Secure Enclave signature, new token. If a request crosses the expiry and comes back 401, it renews and retries once.
The key that is inside the binary. The three handshake requests cannot carry a session token yet — they exist to obtain one — so they identify themselves with an application key shipped inside the binary and therefore common to every copy of Finyvo. Anyone who extracts the binary can read it, which is why it is not the security boundary: it filters out background noise. The real boundary is the Secure Enclave attestation, which cannot be forged by copying a string. That key does not identify you either.
Installation identity. It is pseudonymous and can survive key renewal with valid proof of the previous session. Reinstalling does not guarantee a new identity. The backend uses it for the plan, quota, and abuse prevention.
In transit
Everything travels encrypted with HTTPS / TLS 1.2 or later: between the App and our backend, and between our backend and external providers. There is no plaintext path.
Our backend requires TLS and always responds with these headers:
| Header | Value | What it’s for |
|---|---|---|
Strict-Transport-Security |
max-age=31536000; includeSubDomains; preload |
A year of mandatory HTTPS, subdomains included |
Content-Security-Policy |
default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none' |
The API does not serve pages: it can’t load resources, be embedded, or submit forms |
X-Frame-Options |
DENY |
The same, for older browsers |
X-Content-Type-Options |
nosniff |
Nobody reinterprets a response’s type |
Referrer-Policy |
no-referrer |
No address of ours leaks outward |
Permissions-Policy |
geolocation=(), microphone=(), camera=() |
The API asks for no sensors, and it is written down |
Cross-Origin-Resource-Policy / Cross-Origin-Opener-Policy |
same-site / same-origin |
Isolates responses from other origins |
Cache-Control |
no-store |
Nothing is kept in intermediate caches. The exception is brand logos, which are public and are cached |
The finyvo.com site installs no cookies and loads no third-party resources: the fonts are hosted on our own domain.
How the backend is built
The backend uses Cloudflare Workers, D1, and KV for authentication, providers, caches, and metrics. D1 stores historical rates, installations, AI usage, and confirmed import formats. KV holds caches, challenges, counters, and temporary format tickets.
AI routes use OpenAI models selected by feature; they do not necessarily share the same model. Metrics retain request and response identifiers that allow correlation with OpenAI responses. The metrics table does not store full bodies; the import cache does retain format interpretations and mappings. Zero retention and inability to link data are not promised.
/import/grid converts a complete Excel file in memory without persisting the file or table. It does not use AI: turning AI off does not prevent this upload.
Standard Workers logs last 3 or 7 days depending on the Cloudflare plan. D1 has no automatic expiry deletion implemented for metrics and installations. ntfy alerts include operational metadata; the code does not send IP or financial content to that channel. See Privacy for details and sources.
Limits and anti-abuse
The backend applies IP and installation controls, monthly AI quota, and installation-level and global spending protection. Plus removes the monthly plan quota, not technical controls. A 429 response may indicate a limit; 503 may mean unavailability or global protection. The app may indicate when to retry and retains local alternatives. Quota lookups do not invoke models or consume an AI request.
The switches are yours
- iCloud: turn it off in Settings. The change takes effect while you use the app; if your installation still requires a restart, Settings tells you. This does not delete the existing copy or disable other network features. Your display name uses a separate iCloud store.
- AI: sharing content with OpenAI requires your explicit permission. The master switch withdraws it, and per-feature controls limit which features use it. For file import without upload, use CSV, TSV, or JSON with import AI off; Excel requires backend conversion.
- Apple Pay: pause or revoke the Finyvo connection and disable its Shortcuts automation. Earlier purchases and evidence are managed separately.
- Notifications: control permissions and previews in iOS. Some reminders contain amounts; capture notifications are generic. In-app success/error messages are not feedback sent to the team.
Export is generated locally without encryption, and private mode does not hide its amounts. Resetting the app does not delete exported files or backend records.
What isn’t there yet
We do not publish SOC 2 or ISO 27001 certifications, an external audit report, or a bounty program. Finyvo does not include a separate biometric lock. The data-export ZIP is not encrypted; portable recovery backups can be protected with a password. Private iCloud container deletion is managed in iOS. Technical credentials may survive uninstallation; use the privacy contact to request backend access or deletion.
Reporting a security issue
If you find something — in the app, in the backend, or on the website — write to [email protected] with Security as the subject.
Tell us what you found and how to reproduce it. If you can, include the app version and the approximate time: it helps locate the request in the logs, which have limited retention.
What we ask of you: give us time to fix it before publishing, and don’t access other people’s data or degrade the service while you investigate. There is no monetary reward; there is public credit here, if you want it.
We reply within 30 calendar days at most, the same commitment we apply to any privacy request.