Verify it yourself · about two minutes
Don’t take our word for it. Check.
Local-first retirement education. Your numbers never leave your device.
This page shows you how to confirm that yourself in about two minutes, with tools already in your browser.
Test 1
Turn off the internet
- Open the app once so it saves itself to your browser. A note saying Ready to work offline appears when it’s done.
- Turn on airplane mode or disconnect Wi‑Fi.
- Reload the app and change any number — your annual spending, for example.
- Watch the plan recalculate. The verdict, the balances and the charts all update.
Why it works
There’s no server doing the math. The calculations run in your browser, on your device, and your plan is saved there too.
To be honest about it: the first visit needs a connection to download the app, and the optional Live Pricing refresh needs one too. This page and the home page aren’t saved for offline use; the app is.
This test works on a phone as well as a computer.
Test 2
Watch the network
- Open developer tools. Chrome, Edge or Firefox: press F12, or Cmd+Option+I on a Mac. Safari: first turn on Settings → Advanced → Show features for web developers, then press Cmd+Option+I.
- Click the Network tab.
- Reload the page.
- Build or edit a plan and keep an eye on the list.
What you’ll see
- Only files from www.outlastretirement.com
- After your first visit, most rows say (ServiceWorker): they came from the copy saved on your device, not the internet
What you won’t see
- Uploads of your plan
- Other companies’ domains
- Ad or tracking scripts
Two things appear only if you turn them on. Live Pricing shows requests to Google (script.google.com) when you press Refresh, carrying ticker symbols at most. Anonymous analytics, if you opt in, shows requests to /_vercel/insights on our own domain. Browser extensions can add rows of their own; a private window without extensions gives the cleanest list.
Developer tools need a computer. On a phone, the airplane-mode test (Test 1) is the one to try.
| Name | Status | Domain |
|---|---|---|
| app/ | 200 | www.outlastretirement.com |
| app-*.js | 200 | www.outlastretirement.com |
| app-*.css | 200 | www.outlastretirement.com |
| outfit-*.woff2 | 200 | www.outlastretirement.com |
| manifest.webmanifest | 200 | www.outlastretirement.com |
| sw.js | 200 | www.outlastretirement.com |
Test 3
The browser enforces it
Every page we send comes with a rule sheet called a Content-Security-Policy. It tells your browser which addresses this site is allowed to contact.
Ours allows only our own site, plus Google’s spreadsheet servers for the optional Live Pricing. If any code tried to send data anywhere else, your browser would block it — not us, your browser.
See it yourself
- In developer tools, open the Network tab.
- Click the first request in the list (
app/). - Find Response Headers.
- Look for
content-security-policy.
If the first request says (ServiceWorker), it came from the saved copy and shows no server headers. Open the Application tab, choose Service workers, tick Bypass for network and reload. Prefer not to open developer tools? Paste our address into a free header checker such as securityheaders.com.
- 'self'
- www.outlastretirement.com — our own site.
- connect-src
- The line that controls where the page’s code can send data. The Google addresses are only for the optional Live Pricing. The browser permits any Google Sheet at those addresses; the app contacts only the one you connect, and sends only ticker symbols.
- premium.outlastretirement.com
- Our Premium module server. The app sends it your Premium Access key and nothing else, and it answers with signed code that the app checks before running; the supporting library files it serves are checked against fingerprints inside that signed code. The server is listed only here, where the page fetches from; it is not allowed to run scripts on the page directly. See Premium Modules in the privacy policy.
- blob:
- Lets the app run code it has checked first: a Premium module, after its signature verifies, and the supporting library files a module loads (the PDF reader for statement import, the chart lookup), after each file’s fingerprint matches the one the signed module carries. Under
worker-srcit also lets the PDF reader start its background worker from a copy the page made of checked code. A blob is a file held only inside the page; no other site can supply one. - 'none'
- Nothing at all: no embedded pages or plug-ins, and no other site can frame this one.
More detail
What the lines cover. connect-src governs everything the page’s code actively sends: data requests, live connections and analytics beacons. The other lines close the side routes. img-src and font-src stop data being smuggled out by loading an image or font from someone else’s server; script-src stops outside code; form-action stops a form posting elsewhere; frame-src and object-src stop other sites being embedded.
The three Google addresses.
script.google.com: your own Apps Script. Refresh asks it for the price table, and it registers new ticker symbols.docs.google.com: your Sheet’s published CSV, used as a fallback.*.googleusercontent.com: where Google redirects both of those. The prices come back from here.
Two limits, stated plainly.
- The browser knows addresses, not owners, so it would allow any Google Sheet. What keeps the app to yours is the app itself: it contacts only the web addresses you paste into Pricing Source, and sends only ticker symbols. The Network tab (Test 2) lets you watch that.
- Links you click are not covered. Following a link to a source or a broker opens that site, as any link does. The page does not send your data along.
Test 4
Read the code
The three tests above show what the app does. The source shows why it cannot do otherwise.
The app’s source is published at github.com/jabone/outlastretirement under the GNU Affero General Public License. What you can check there:
- That the site runs the published code. Preferences shows the version the app is running (“Version 409”, say): the number of the newest entry in What’s New. The repository’s
src/config/changelogLatest.jscarries the same number. Since October 18, 2026 the site is built and deployed from that repository by its own public workflow (.github/workflows/ci.yml, thedeployjob), after its tests pass on the exact commit, so the site cannot run code the repository does not publish; the workflow’s runs are public under the repository’s Actions tab. If the site ever shows a higher number than the repository, something has gone wrong with that, and the Changelog page lists what the site has that the repository does not yet. - The policy the browser enforces (test 3) is
vercel.json, and a test,src/__tests__/cspHeader.test.js, fails the build if anyone widens it. - Where a request could be made. The open app has no
fetchof its own except the Premium module loader,src/premium/registry.js; the optional analytics (src/components/GatedAnalytics.jsx) and the update check (src/utils/pwa.js) are the other two things that touch the network, and all three asksrc/utils/localOnlyMode.jsfirst. Live Pricing’s URL rules aresrc/utils/pricingSource.js; its request is made by the Detailed Holdings module.src/__tests__/privacyTenets.test.jspins the privacy policy’s claims to the code. - What a Premium key carries. The loader sends it and nothing else;
src/utils/premiumJwt.jsis what reads it.
What is not there: the Premium modules themselves. They are a separate, closed work, fetched from our module server only when a key is present; what loads them, and the interface they are written against (PREMIUM_MODULE_CONTRACT.md), is in the open code. And the published source is the source of the site you are reading; we do not yet offer a way to prove byte-for-byte that the files your browser received were built from it, so treat the three tests above as the proof and the code as the explanation.
The full list
Everything that can touch the network
No fine print. This is every feature that can make a connection, what it sends, and where.
| Feature | Off by default? | What it sends | Where |
|---|---|---|---|
| Loading the app | Noit’s how the app reaches you | Requests for the app’s own files (code, not your data) | www.outlastretirement.com |
| Update check | Noskipped in Local-Only Mode, except when you reload | Asks whether a newer version exists. Nothing about your plan. | www.outlastretirement.com |
| Anonymous analytics | Yesoff unless you opt in | The tab you opened (its name only), the site you came from (usually just its domain), and what our host Vercel reads from the request itself: approximate country or region, device type, operating system and browser, and the time. No cookies, no link parameters, never anything from your plan. | www.outlastretirement.com, processed by Vercel, our host |
| Live Pricing prices Detailed Holdings | Yesoff unless you set it up | Ticker symbols only — never amounts, share counts or balances | Your own Google Sheet, through Google’s servers. The Sheet’s script then looks the symbol up with Google Finance and Yahoo Finance from Google’s servers; those services see the symbol, not you. |
| Premium modules Premium only | Yesnothing without a Premium key | Your Premium Access key, as a request header — never your plan | Our module server, premium.outlastretirement.com, which answers with signed code. Loaded once per area the first time you open it; Local-Only Mode stops it. |
| Contacting us | Only if you write | Whatever you put in the email | Your own email app, to hello@outlastretirement.com |
Never touch the network: backups (saved where you choose, encrypted if you set a password), your Premium key (checked on your device), and imports of PDFs, CSVs and bank exports (read in your browser).
No accounts. No logins. No bank linking.
What we don’t claim
- Loading the page does contact our host. That’s how the app gets to your browser in the first place. Our host, Vercel, receives your IP address and browser type, as any web server does; we don’t receive or keep them.
- Google sees Live Pricing requests when you use it, as any website sees a visit: your IP address and the ticker symbols, nothing else. The Sheet’s script then looks the symbol up with Google Finance and Yahoo Finance from Google’s servers; those services see the symbol, not you.
- We can’t see what’s on your device — so we can’t recover a lost plan. Keep backups.
Found something that doesn’t match this page?
Tell us: hello@outlastretirement.com
Open the demo plan and try the airplane-mode test