Privacy Policy -- Portfolio Manager by PluriFin
Version: 1.0.2 Last update: 2026-07-14 Original language: Italian (binding version for jurisdiction) Text hash (for audit trail): SHA-256 generated by scripts/legal/build_legal_html.ps1
1. Data controller
The data controller is:
- Name: Filippo Salemi
- Legal form: natural person (independent developer)
- Commercial brand: PluriFin
- Country: Italy
- Email for GDPR and privacy requests:
[email protected] - General contact email:
[email protected]
No Data Protection Officer (DPO) is appointed: the controller is a natural person and the activity does not fall within the cases listed in art. 37 of Regulation (EU) 2016/679 (GDPR).
2. Summary (TL;DR)
- All portfolio data (positions, transactions, goals, settings) is **stored
exclusively on the user's device** in encrypted form. We do not receive, store or transmit it to PluriFin servers.
- The app uses no active third-party analytics in this version (no
Facebook SDK, no AppsFlyer, no Sentry, no Google Analytics events generated by the app). A Google Analytics for Firebase (GA4) property is linked to the Firebase project portfolio-manager-f6f8d for future evolutions: version 1.0.0+10 does not bundle the firebase_analytics SDK, so no analytics event is generated by the app binary. See section 4 (sub-processors) for the full declaration.
- On Android, the app integrates an optional anonymous crash diagnostics via Firebase Crashlytics (Google Ireland Limited): off by default, enabled by the user from Settings > Privacy. Never portfolio data, API keys or AI content. See section 4 (sub-processors).
- The app shares no data with advertisers. There are no ads.
- Optional features exist that, if activated by the user, transmit data to
third-party processors: AI Gemini, live market prices (EODHD/FMP), subscription validation (Cloudflare Worker). Each requires explicit user action to be enabled.
- The user can export and delete all data at any time from the app's Settings,
without contacting the controller.
3. Categories of data processed
3.1 Data collected locally on the device
The following data is kept exclusively in encrypted local storage (Hive AES-256 with key in the OS keystore):
| Category | Examples | Legal basis | Retention | |---|---|---|---| | Portfolio data | position name, ticker, quantity, average price, broker, purchase date | art. 6.1.b GDPR (contract with user) | Until user deletes | | Financial goals | title, target amount, deadline, monthly contributions | art. 6.1.b GDPR | Until user deletes | | Preferences | language, currency, theme, notifications, demo mode | art. 6.1.b GDPR | Until user uninstalls | | Consent audit trail | timestamp, document version, decision (accept/decline), text hash | art. 6.1.c GDPR (legal obligation, proof of consent) | Until user deletes or uninstalls | | User API keys | keys for activated providers: Gemini, EODHD, FMP, Alpha Vantage, Twelve Data, Finnhub, Polygon.io, Marketstack, Tiingo, Nasdaq Data Link (manually entered by user) | art. 6.1.b GDPR | Until user removes |
All this information is encrypted with AES-256 + HMAC-SHA256, 32-byte key held in the system keystore (Android Keystore on Android, Keychain on iOS, DPAPI on Windows). API keys are saved in flutter_secure_storage, which uses the same OS keystore.
3.2 Data collected via Play Store (Google)
Installation, updates, ratings and any subscription purchase happen via Google Play Store. PluriFin receives from Google only the data strictly needed to validate the subscription:
| Data | Source | Purpose | |---|---|---| | purchaseToken | Google Play Billing | Subscription validation via Cloudflare Worker | | productId | Google Play Billing | Identification of purchased product (pluri_premium_yearly_099) | | device_id_hash (HMAC-SHA256 of Android ID + server salt) | Computed on device | Anonymous device identification for the 7-day trial |
PluriFin does not receive from Google: name, email, address, payment data, card number. Payment is fully handled by Google Play.
3.3 Data sent for subscription validation (Cloudflare Worker)
When the user starts the free trial or purchases, the app sends to the Cloudflare Worker:
device_id_hash(HMAC-SHA256 of Android ID with server-side salt; not
reversible client-side)
purchase_token+product_id(only in case of purchase)- Signed
JWT(for follow-up status requests)
The Worker replies with a JWT signed with Ed25519 (entitlement). No PII is ever transmitted. The device_id_hash is not a personal identifier: two different devices of the same user produce different hashes and are not linkable without the server salt.
3.4 Data Gemini receives (only if user enables the feature)
If the user:
- manually enters their Gemini API key in Settings;
- opens AI chat or requests portfolio analysis;
then the prompt (composed of: user question + portfolio structure in anonymous form -- ticker, quantity, average price, percent allocation) is sent directly from the app to Google Gemini.
PluriFin does not intermediate or store these prompts. Transmission is end-to-end between user device and the Gemini API. Google AI Studio / Google Gemini API Terms apply.
The user can revoke the key at any time in Settings; from that moment no AI request is sent.
3.5 Data market-data providers receive (only if user enables the feature)
The app supports 10 market-data providers that the user can configure independently by entering their own API key in Settings. When the user requests a price refresh (manual or scheduled), the app transmits the requested tickers directly to the chosen provider. PluriFin does not intermediate or store.
Supported providers and their privacy policies:
- EODHD Privacy Policy
- Financial Modeling Prep (FMP) Privacy Policy
- Alpha Vantage Privacy Policy
- Twelve Data Privacy Policy
- Finnhub Privacy Policy
- Polygon.io Privacy Policy
- Marketstack Privacy Policy
- Tiingo Privacy Policy
- Nasdaq Data Link Privacy Policy
- Stooq: public CSV download with no API key (the HTTP
request goes directly from the user's device to stooq.com)
For each provider, data transmission happens only if the user has:
- manually entered their API key in Settings (Stooq excluded, it is free);
- selected the provider as active for price refresh.
The user can remove the key or disable the provider at any time.
4. Sub-processors
Full list of sub-processors that can receive data (only if corresponding features are enabled by the user):
| Sub-processor | Purpose | Data sent | When | |---|---|---|---| | Google LLC -- Play Billing | Payment and subscription | purchaseToken, productId, payment details (handled by Google) | Only if user buys subscription | | Google LLC -- Gemini API | AI portfolio analysis | User prompt + anonymous portfolio structure | Only if user enters Gemini key and opens AI | | EODHD | Real-time and historical market prices | List of tickers | Only if user enters EODHD key and selects the provider | | Financial Modeling Prep (FMP) | Real-time prices, fundamentals | List of tickers | Only if user enters FMP key and selects the provider | | Alpha Vantage | Historical market prices | List of tickers | Only if user enters Alpha Vantage key and selects the provider | | Twelve Data | Real-time and historical prices | List of tickers | Only if user enters Twelve Data key and selects the provider | | Finnhub | Real-time market prices | List of tickers | Only if user enters Finnhub key and selects the provider | | Polygon.io | Historical market prices | List of tickers | Only if user enters Polygon.io key and selects the provider | | Marketstack | End-of-day market prices | List of tickers | Only if user enters Marketstack key and selects the provider | | Tiingo | Real-time and historical prices | List of tickers | Only if user enters Tiingo key and selects the provider | | Nasdaq Data Link | Historical market prices | List of tickers | Only if user enters Nasdaq Data Link key and selects the provider | | Stooq | Public CSV download of historical prices | List of tickers (no API key, public HTTP request) | Only if user selects Stooq as provider | | Google Ireland Limited -- Firebase Crashlytics | Anonymous crash diagnostics | Obfuscated stack trace, device model (e.g. "Pixel 7"), OS (e.g. "Android 14"), app version (e.g. "1.0.0+10"), anonymous Firebase Installation ID | Only if the user enables "Anonymous crash diagnostics" in Settings > Privacy. Off by default. | | Google LLC -- Google Analytics for Firebase (GA4) | Forward-compat for aggregated stability analytics (GA4 property linked to the Firebase project) | No data transmitted in version 1.0.0+10 (the firebase_analytics SDK is not in pubspec). When firebase_analytics is added in a future release, GA4 will receive auto-collected events (screen views, app_start/app_foreground, app_exception aggregated from Crashlytics). The user will be re-consented at that time | Inactive in version 1.0.0+10. Activated only after a future app update + user re-consent. | | Cloudflare, Inc. -- Worker | Trial and subscription validation | device_id_hash, purchase_token, productId, JWT | Always (required for subscription gating) | | Microsoft GitHub -- Pages | Hosting legal docs, public market data, web demo, marketing site | User IP at HTTP request time (Microsoft server logs) | Always, on every visit to the web app or legal document |
Cloudflare and GitHub Pages may collect technical access logs (IP, user-agent, timestamp) per their standards. See:
No financial user data is ever sent to Cloudflare or GitHub beyond what the respective service strictly requires.
5. Transfers outside the EU
Google (Gemini, Play Billing), Cloudflare and GitHub are US companies. Data transfers occur under:
- EU Standard Contractual Clauses 2021/914 where applicable
- EU-US Data Privacy Framework for Google (see
- For EODHD and FMP: their respective transfer policies apply
Users who do not want non-EU transfers can simply not enable optional features (AI Gemini, real-time prices): the app works fully in local mode without any transfer.
6. Data subject rights (GDPR arts. 15-22)
The user has the right to:
| Right | How to exercise | |---|---| | Access to personal data | In app: Settings > Privacy > Export my data (ZIP with portfolio + consents + log) | | Rectification of inaccurate data | Direct in-app edit | | Erasure ("right to be forgotten") | Settings > Privacy > Delete all data (irrevocable) or app uninstall | | Restriction | Disable optional features (AI, live prices, subscription) | | Portability | Settings > Privacy > Export my data (JSON + CSV in ZIP) | | Objection | Disable optional features (AI, live prices) | | Complaint to supervisory authority | Italian Data Protection Authority -- Garante or your country's national authority |
For requests not handled in-app (e.g. revoking consent for data already sent to a sub-processor):
- Email:
[email protected] - Response time: within 30 days from receipt (GDPR art. 12.3)
- Response language: same as request
Most requests can be fulfilled instantly by the user without contacting the controller, since the app is local-first.
7. Data retention
Local data on the device remains until the user:
- Manually deletes the entry (position, goal, etc.)
- Performs "Delete all data" from Settings
- Uninstalls the app
The consent audit trail (consent_box) has separate retention: it is kept until the user explicitly exports and clears it. This is needed to provide proof of consent under GDPR art. 7.1.
Sub-processor retention is governed by each provider's policies:
- Cloudflare Worker:
device_id_hashfor trial duration (8 days) + active
entitlement + grace period (3 days) + account hold (7 days) = max 1 year + ~ 3 weeks
- Google Play Billing: see Google terms
- Gemini, EODHD, FMP: no PluriFin-side retention (passthrough requests)
8. Security
Technical and organizational measures:
- Local storage: Hive AES-256 + HMAC-SHA256, 32-byte key in the OS keystore
- User API keys:
flutter_secure_storage(Android Keystore / iOS Keychain /
DPAPI)
- Network: HTTPS only (TLS 1.2+),
cleartextTrafficPermitted=falsein
Android Network Security Config, NSAppTransportSecurity strict on iOS
- Anti-screenshot:
FLAG_SECUREAndroid on sensitive pages (home, position
detail, AI chat, rebalance)
- Anti-backup:
android:allowBackup="false"to prevent encrypted data from
being included in Google Drive backups
- Anti-tamper: root/jailbreak detection with graceful degradation
- Code: Dart obfuscation always on for releases (`--obfuscate
--split-debug-info`)
- No persistent logging of personal data, neither in console nor on file
- No crash telemetry by default (Sentry disabled in v1.0)
9. Minors (GDPR art. 8)
The app is not intended for children under 18. The Play Store content rating is "18+" as recommended for financial apps. PluriFin does not knowingly collect data from minors. Parents or guardians who detect a minor's account can request deletion at [email protected].
10. Cookies and similar technologies
The mobile app does not use cookies (it is not a browser).
The web demo (lunapiena49.github.io/portfoliomanager-data/app/) uses:
- HTML5
localStorageto persist the 5 demo positions locally. No cookie is
set by the app.
- A technical anti-CSRF cookie set by GitHub Pages (managed by Microsoft
GitHub, not by PluriFin).
The marketing site (lunapiena49.github.io/portfoliomanager-data/) uses privacy-safe self-hosted GoatCounter analytics (no cookie, no fingerprinting, IP not stored). If cookie-based analytics are ever added, a consent banner will be shown per ePrivacy regulations.
11. Changes to this Privacy Policy
PluriFin reserves the right to update this Privacy Policy. Material changes will be notified:
- In app, with a re-consent screen on the first launch after the update
- On the marketing site, with a banner for 30 days
- Via newsletter email (if applicable)
The version number (top of this document) is bumped on each material change. Previous versions are available in the public repo lunapiena49/portfoliomanager-data, folder legal/, via Git history.
12. Contacts
| Request type | Email | |---|---| | GDPR (access, deletion, portability, complaint) | [email protected] | | Privacy & security | [email protected] | | General support | [email protected] | | Legal & disclaimer questions | [email protected] | | Public Play Store contact | [email protected] |
13. Jurisdiction and applicable law
This Privacy Policy is governed by Italian law and EU Regulation 2016/679 (GDPR). For disputes the consumer's forum applies under Italian Legislative Decree 206/2005 (Consumer Code).
For users outside the EU, Italian law and GDPR apply to personal data processing, except where stricter national consumer-protection rules apply.