Documentation
Privacy & your data
Guest portfolios use a temporary hosted session plus a backup in your browser. Signed-in portfolios are stored under your hosted account. Desktop portfolios are stored on your disk; cloud AI, tax calculations, optional sharing and hosted synchronization send the data described below. The desktop MCP bridge requires that hosted synchronization.
Using the app as a guest
The Free dashboard works without an account. Guests can import holdings, use portfolio analytics, backtest a trade plan and use the in-app assistant within Free limits: 3 accounts and 50 assets. Social requires a signed-in account; Taxes, external-agent MCP access and investor-group analytics require Pro. See the Free plan boundaries.
As a guest on the hosted app, your data lives in two places:
- A private server session. Your browser gets an anonymous session cookie, and everything you upload belongs to that session alone — visitors can never see each other's data. The session is ephemeral: after 12 hours of inactivity (on the deployed app) it is swept from the server, and a server restart clears it too.
- A local backup in your own browser. Your browser keeps a mirror (in IndexedDB) of what you've put in: the uploaded CSV files, your settings, manual assets and favourites. When you come back and the server session has meanwhile expired, the app notices the empty session and silently replays the backup — so it can restore your work after the temporary session expires. Keep your original files or export a backup: clearing site data, browser storage eviction or an incomplete recovery can still remove access to the browser copy. One deliberate exception: API keys are never mirrored to the browser — they live only encrypted on the server.
Practically, that means:
- Same browser, same device → your portfolio is there when you return, even after long breaks.
- Different device or browser (or after clearing site data) → it isn't. The backup lives in the browser you used; nothing durable on the server identifies you as a guest.
What creating an account changes
An optional email + password account (the Account tab in ⚙ Settings, hosted app only) moves the durable copy of your data to the server:
- Your sources, settings, manual assets, favourites and encrypted API keys are stored in the server database under your account — independent of any browser, surviving browser changes and following you to any device you sign in on.
- Your password is stored only as an argon2id hash. Signing in sets an HttpOnly cookie holding a server-issued random token — the server keeps only a hash of that token, so even a leaked database couldn't impersonate you. The login lasts 30 days by default. That cookie belongs to the app's own address and nothing else can read it.
- Signing in also sets one marker cookie,
pd_signedin=1, shared between the app and this website so the site's header can show your avatar instead of a "Sign in" button when you already have a session. Its entire value is the character1: no name, no email, no plan, nothing that identifies you, and it grants no access on its own — the app still checks the real login cookie for anything that matters. It is deleted when you sign out, when you delete your account, and whenever your login expires. It is not analytics and it is not shared with anyone. - While signed in, the browser mirror is not used — the server's copy is the source of truth on every device.
- Downloading your data — the Download my data row in the account panel, right above Delete — hands you one JSON file with every row the account owns: sources and their transactions, settings, manual assets, favourites and lists, tax lots, alerts, and what you share on the Social tab (your settings, friendships, current and prior relative snapshots, posts, reactions and replies). API keys are listed by provider only, their encrypted value redacted. The file is built on request, signed-in only, and nothing about it is kept.
- Deleting the account (password-confirmed, in the same tab) permanently removes every row it owns — the same table list the download walks: sources, settings, API keys, manual assets and favourites.
Starting as a guest, signing up later
You lose nothing by trying the app first: your first sign-in from a browser claims what you built there as a guest.
The story in order:
- You open the app as a guest, upload your exports, star favourites, tune settings.
- Later you create an account (or sign in for the first time) from that same browser. If the account is still empty and your guest session has data, everything the guest session owns — sources with their transactions, settings, API keys, manual assets, favourites — is moved to the account in one step, all-or-nothing.
- Any other device you sign in on now sees the same portfolio, served from the server.
Two edges worth knowing, exactly as implemented:
- The claim happens only into an empty account. If the account already has data of its own, the guest data stays with the guest session (and eventually expires) rather than being merged — nothing is overwritten.
- Signing out returns that browser to guest mode with a fresh, empty session. Your account's data stays on the server, untouched, and is there when you sign back in. The browser's own local backup from your guest days also remains in that browser and replays into the new guest session — so the machine you originally uploaded on keeps working even signed out.
API keys
On the hosted app, LLM API keys entered in Settings are encrypted at rest on the server and are never stored in the browser — not in localStorage, not in the IndexedDB backup. They belong to your session or account and are wiped with it. If you run the app yourself, keys can also come from the server's own .env or environment (OPENAI_API_KEY / OPENROUTER_API_KEY) — and only then. The hosted app deliberately ignores both, so no session can ever end up spending a key it was not given.
And if you'd rather not send portfolio context to any cloud model: use the desktop app with Ollama or LM Studio. Assistant inference then stays on your computer; the other outbound services below remain separate choices.
What reaches an LLM provider
Ordinary dashboard calculations do not need an LLM. Import parsing, portfolio values, charts, performance, risk and allocation are computed by the portfolio engine. Enabled assistant features — including reports, AI insights, smart search and proactive comments — can call the selected model without a newly typed chat question. Their relevant context follows the provider choice below.
Out of the box, the hosted app answers on our key. So that you are not sent off to obtain an API key before you can try anything, a new session starts on a small free budget against the deployment's own OpenAI key, on gpt-5.6-luna. Be clear about what that means: those calls reach OpenAI exactly like any other — the only difference is whose key pays for them. A quiet donut beside the chat shows how much of the budget is left, and when it runs out the assistant stops rather than quietly falling back to anything else. Enter your own key in ⚙ Settings → Assistant and it takes over at once and is never metered; use a local model on desktop to keep that model request on your computer. The desktop app has no free budget — it starts on a local model.
Whichever of those you are on, using an assistant feature (chat, the one-click reports, AI insights, smart search) sends your question plus the portfolio context the answer needs — holdings with weights and values, performance and risk figures, recent trades, your strategy text, and what's currently on your screen — to the provider behind the model you picked (OpenAI, Anthropic or OpenRouter for cloud models), under your key or, on the free budget, under ours. Your imported raw CSV files are not automatically sent as portfolio context. Attachments you deliberately send in chat are shared with the selected provider; a connected external agent can also access authorized attachments and Assistant context, as described below. What those providers do with request data is governed by their terms, which is exactly why the choice of provider — including a fully local one — is yours.
The desktop app
The native desktop app runs the portfolio engine on your computer and stores its data locally. Market-data requests, cloud AI and opt-in Social sharing have the boundaries described below. Pro users can also choose One synchronized portfolio in Settings → Assistant → External agents. This copies portfolio inputs to the hosted account and keeps web and desktop synchronized while the app is running; it is required for the desktop external-agent bridge. Review both datasets and choose which to keep before synchronization starts. Replaced inputs are backed up and conflicting edits pause sync for review.
Where your data lives
Everything the app knows sits in its own data folder on your disk: a SQLite database with your transactions, sources, settings, manual assets and favourites; the local encryption key protecting any API keys you enter; and the market-data caches. On macOS that folder is ~/Library/Application Support/Fortunest, on Windows %APPDATA%\Fortunest. Back it up and you've backed up the app; deleting it removes the local copy. If you enabled hosted synchronization, Social sharing or made a backup, those copies are separate.
What leaves your machine, and why
- Market data requests — tickers only. Prices, FX rates, fundamentals, news and search answers come from the Fortunest server's shared, licensed market-data cache (the server fetches from its providers and pools the result for everyone) and are cached again locally on your machine. Those requests carry ticker symbols, date ranges and your licence sign-in — a free account is enough — and nothing more: never quantities, values, or anything about your portfolio's composition beyond the symbols you look at. The server answers from its cache and stores nothing about you.
- The assistant — only where you point it. With LM Studio or Ollama the model runs on
localhostand that model request is processed on the same computer. This does not disable the other online services listed here. If you instead configure a cloud provider under your own key, the same rules as the hosted app apply — see What reaches an LLM provider. - Interest rates & macro data. The desktop build answers from a data snapshot bundled with the app — it makes no calls to external rate services.
- Social sharing — only if, and only while, you tick a section. When you tick a sharing box on the Social tab, the app uploads to the Fortunest server exactly the snapshot sections you ticked in ⚙ Settings → Social — relative values only (names, weights and percentages), never amounts — plus any posts you write there, so the friends who accepted your request can read them. Beyond the ticked sections and your posts a snapshot carries one more thing, and only when it applies: a
demo: trueflag saying the figures were computed over the built-in demo data (see Social & friends). With every portfolio-sharing box off — which is how every account starts — no portfolio snapshot is shared. Account identity, friendship requests, posts and presence are separate Social data; accepting a friend does not publish your holdings. - Optional Pro portfolio synchronization and external agents. Sync includes portfolio inputs, transactions, manual assets, favourites, settings, notes, memories, skills and tax inputs. Provider keys, login credentials and machine settings are excluded. The connected agent receives the data its permissions authorize; see agent access.
- Analytics and licence services. The desktop build ships with no analytics at all — no Umami, and the anonymous usage counters the hosted app keeps are written only to the local database, where they stay. The app refreshes its signed licence token when needed. Pro desktop tax calculations send tax inputs — transactions, rules, manual opening lots, transfer links and display currency — to the hosted computation service. The tax engine is not bundled in the desktop app; the service computes the response without saving those inputs. Tax calculations require connectivity.
What works offline
Essentially all of it: the dashboard, performance, allocation, risk, dividends, correlations, simulations, trade recommendations, Explore, and the assistant on a local model. Price history is cached for a day; when the network is away the app simply keeps serving the last cached prices (and quietly retries in the background), so charts show the world as of your last online session. An offline session cannot fetch fresh market data or price a symbol it has never fetched before — so a fresh install wants one online session to warm its cache, and after that cached analysis remains available. Cloud AI, Social, hosted synchronization/MCP and Pro tax calculations need connectivity. For a mode-by-mode comparison and a local-model check, see the private portfolio tracker guide.
External agents & your permissions — Pro
Connecting Codex, Claude Code or another MCP client gives that named client the permissions you approve at Fortunest sign-in. Authorized reads can include portfolio figures and Assistant context: conversations, attachments, notes, memories, skills and reports. Your chosen agent provider receives this information under its own terms. Fortunest provider API keys and login credentials are never exposed to the agent.
Connection consent sets the client’s permissions; ordinary authorized reads do not require a new approval for each call. Sensitive changes need action approval unless an active permission already covers them. Approve once, reject, allow matching bounded actions, or allow all actions within this connection’s existing permissions for 1 hour or 8 hours. Review activity and revoke connections or permissions in Settings → Assistant → External agents. Desktop-agent approvals and Undo are reviewed in the hosted app. Completed action details and Undo are retained for seven days, with minimal status history for 90 days. Interrupted actions require review instead of being retried automatically. See the connection guide and approval details.
Social & friends
Group portfolios keep the current relative snapshot and three previous publications. Current sharing toggles and friendship/block grants apply to all four at read time. Delete shared data and account deletion remove the history. Own comparison weights stay on your device.
The Social tab is the one place data of yours can become visible to other people — and only ever the people you approved, only ever the sections you ticked, and only ever relative figures. The rules, exactly as built:
- Available by default; sharing nothing by default. The tab works out of the box — you have an @handle, people can find you by it, and you can send and accept friend requests. Not one of the sharing checkboxes is ticked for you, so a new account publishes nothing at all: there is no snapshot on the server to read, and a friend you accept sees an empty profile until you decide otherwise. Being findable and being readable are two different things, and only the second one is ever on by your own hand.
- Consent is request + accept. Nobody sees anything of yours until you and they have both agreed: one side sends a friend request, the other approves it. Accepting a request is the consent to share; either side can unfriend at any time, which ends visibility in both directions immediately, and a block list is there for stronger cases.
- What friends can see: exactly the sections you ticked in ⚙ Settings → Social — each one off by default — as relative values: asset names with portfolio weights and gains in %, return curves, allocation and risk percentages, targets and drift. Plus the posts you write, your @handle and avatar, and (unless you turn its toggle off) an online dot with a last-seen time.
- One flag rides no toggle, because it is a disclaimer: a portfolio built (partly) from the built-in demo data publishes
demo: true, so friends see a "uses demo data" label beside your @handle. It qualifies the shared figures rather than adding to them, and names no file, source or amount. That flag and the ticked sections are the whole of a snapshot. - What friends can never see: amounts, currency values or quantities — no toggle shares an absolute number; your raw transactions, CSV files, account balances, API keys, chat history or settings. Sections you didn't tick are not just hidden — they are never uploaded in the first place: the shared snapshot is built on your device from the ticked sections only.
- Opting out is instant. Untick a section and friends stop seeing it immediately — every read is filtered by your current settings, not by what was last uploaded. Turning Social off entirely makes you unfindable and unreadable.
- Deletion is real. The "Delete shared data" button in ⚙ Settings → Social removes your published snapshot from the server on the spot; you can delete any post of yours (automatic trade events included). Deleting your account removes everything Social ever stored about you — profile, friendships, blocks, snapshot, posts, images and reactions.
Usage analytics & feedback
Recovering a failed app download. If a tab cannot download part of the app, it can reload once in five minutes to fetch the current version. A later failure can recover again after that pause. This uses one timestamp in the tab’s session storage. If that storage is blocked, the original error remains visible; you can reload the page through your browser. Chart range strips also defer resize measurements to the next frame and discard pending work when closed; browser error reporting remains enabled.
The hosted app records anonymous, cookieless usage events (which features are used, how large portfolios are as counts). Only generic values travel — never file names, tickers, amounts, queries, chat or strategy text, or emails. The same anonymous counts are mirrored into cookieless Umami page analytics; this website uses the same cookieless Umami counter and no analytics of any other kind. The only cookie this website ever reads is the pd_signedin marker described above — set by the app when you sign in, deleted when you sign out, carrying the character 1 and nothing else — and it is used for one thing: deciding whether the header should show your avatar. It is never sent anywhere, and if you have no account you have no cookie from us at all.
One visit counter of our own. Both this website and the hosted app send the server a single visit beacon once per browser tab session: a request to /api/visit whose entire content is two words — which surface you are on (site or app) and a device class (desktop, mobile or tablet, worked out in your browser from the screen width and whether it has a touch pointer). That is the whole payload. It carries no cookie (the request is sent without credentials and the server sets none in reply), no identifier of any kind, no page address and no user-agent string; the server does not store your IP address — it keeps only a salted, in-memory hash of it for about a minute to throttle abuse, then forgets it. What is stored is one row per day per surface per device class with a count, so the operator can see "how many visits came from phones this week" and nothing finer. It respects the same opt-out as the analytics: a browser that has opted out with ?notrack sends no beacon either. The desktop app sends none at all.
When an assistant answer fails, is refused, is cut short or comes back empty, the server notes how it ended — which AI provider and model, the reason it stopped, the provider’s own error text and its id for the request, token counts — in its event log, tied to the same anonymous session id, and in its error tracker, which sees only a salted hash of that id. Never your question and never the answer: error reports carry no request contents at all.
Server diagnostics. The hosted service also keeps a size-limited local error journal, separate from usage analytics, so faults can be investigated without the external error tracker. It records timestamps, severity, route patterns, software version identifiers, diagnostic messages and bounded stack locations. It does not collect request bodies, headers, cookies or stack-frame variable values. Common credentials, email addresses and URL queries are scrubbed from diagnostic text. Access is restricted to server operators; older records are overwritten within a 10 MiB storage budget, and excess reports are dropped during an error flood.
One exception exists, and you type it yourself: a message sent through the in-app Feedback button is stored as written (with the email field only if you fill it in), tied to nothing more than the same anonymous session id.
Asking about your data
Write to privacy@fortunest.ai for anything covered on this page — a copy of what is stored about you, a correction, or deletion. The app already does both without a letter — Download my data in the account panel is the copy, and deleting your account removes it; the address is there for the cases that need a person, and for the rights the GDPR gives you beyond the button.
For everything else: support@fortunest.ai for help with the app, security@fortunest.ai for a vulnerability report.