What a dashboard can't do here.
Every dashboard we host runs in a locked frame, on a domain that holds no logins. The rules are response headers, not promises, so you can check them yourself.
Content-Security-Policy:
sandbox allow-scripts
default-src 'none'
script-src 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com
style-src 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com https://fonts.googleapis.com
font-src https://fonts.gstatic.com data:
img-src data: blob:
connect-src 'none'
form-action 'none'
frame-ancestors https://larssoni.com
base-uri 'none'
X-Content-Type-Options: nosniff
Referrer-Policy: no-referrer
X-Robots-Tag: noindex, nofollow
Cache-Control: no-store
sandbox allow-scripts — Scripts may run. Nothing else: no cookies, no storage, no pop-ups, no reach into the page around it.
connect-src 'none' — It cannot call any server. Your numbers cannot be sent anywhere.
form-action 'none' — It cannot submit a form, so it cannot ask anyone for a password.
img-src data: blob: — Images come from inside the file. No tracking pixel can load.
frame-ancestors — Only our own viewer page may frame it.
Printed from the code that serves every dashboard, free or paid. To see it live: open any dashboard, open your browser's network panel, and look at the response headers of the frame.
1 · Where a dashboard runs
Dashboards are served from larssonipages.com or larssoni.co, never from larssoni.com.
The domain that holds your login never serves a dashboard, and the domains that serve dashboards set no cookies.
Free dashboards live on larssoni.co; paid and locked ones on larssonipages.com.
If someone abuses the free plan and a filter blocks that domain, business dashboards are not on it.
sandbox="allow-scripts", and nothing else.
No access to the page around it, no pop-ups. It is set twice: on the frame, and as a response header on the dashboard itself.
connect-src 'none'.
A dashboard cannot fetch, post or open a socket. It draws what it was given.
form-action 'none'.
It cannot submit a form anywhere, which is what a phishing page needs.
Its own inline code, plus libraries from jsDelivr, cdnjs and unpkg.
So charts render. No other host can serve it a script.
Images only from inside the file. Fonts from Google Fonts.
There is no network source for images, so no tracking pixel can load.
Thumbnails and PDF exports run under the same policy.
Our own render service blocks every outside request the policy would block in a browser.
2 · Who can open it
Anyone with the link. Only the email addresses you list. Only addresses at your company's domain.
Locking a link is on Pro and Team. There are no shared passwords anywhere in the product.
The viewer types their email and we send a six-digit code. It works once, for ten minutes.
No account, and no password for your people to reuse. We store a hash of the code, never the code.
Nothing.
The page answers the same way whether or not the address is on the list. A code is only sent if it is.
If that email has access, a code is on its way. Enter it below.
The gate's one reply, whether or not the address is on the list.
Five tries per address in ten minutes, with a separate limit per network address.
When a limit is hit the gate refuses. It never fails open.
A pass in that browser, for seven days.
HttpOnly, Secure, SameSite=Lax. Take someone off the list and their pass ends with that save.
Set-Cookie: __viewer=…; Path=/; Max-Age=604800; HttpOnly; Secure; SameSite=Lax
Seven days, readable by no script.
Each load uses a signed address that expires after two minutes.
Copying the frame's address out of the browser gets you nothing a moment later.
How many people opened it. Never who.
On the Team plan. A viewer's email is reduced to a keyed hash, separate for every dashboard, and is never shown to the owner.
3 · Your account
An emailed link, or Google. There are no passwords to steal.
The link works once, for fifteen minutes.
Thirty days, in a cookie marked HttpOnly, Secure and SameSite=Lax.
We store only a hash of the session token.
Set-Cookie: __session=…; Path=/; Max-Age=2592000; HttpOnly; Secure; SameSite=Lax
Thirty days, readable by no script.
The request is tied to the browser that started it.
That stops someone finishing a sign-in they began elsewhere.
A key for publishing from an AI tool or a script is shown once, stored as a hash, and can be revoked at any time.
Only the workspace owner can make one.
On Team: an owner, who handles billing, invitations and deletion; members, who make and share dashboards.
4 · What we keep, and for how long
| What | Where it lives | How long |
|---|---|---|
| Your dashboard files, every version | Cloudflare R2 | until you delete them; then 30 days to undo |
| The values in a connected Google Sheet | not stored — read when an update runs | a fingerprint for 30 days, to notice a changed sheet |
| A CSV file you upload | stored, so it can be read again | until you delete the dashboard |
| Column names and the update recipe | our database | while updates are switched on |
| A viewer's email on a locked link | used to send the code, then kept only as a keyed hash | until the dashboard is deleted |
| View counts | a hash of network address and browser that changes every day; never the address itself | with the dashboard |
| The change log | each entry carries a hash of the one before, so edits to history show | with the workspace, and in your export |
5 · Your spreadsheet, and what the AI sees
The one tab you point us at, through Google's own link export.
Only Google's hosts are contacted. A redirect anywhere else is refused.
At set-up, and each time a scheduled update runs. Nothing in between.
Column names, made-up rows, and your numbers with every digit masked: £46,200 becomes £##,###.
Anthropic's model, once, at set-up. There is no setting that sends it real values.
No AI at all.
Each update is a plain substitution, checked against the shape the dashboard expects. If the check fails, viewers keep the last good version, with its date.
Not yet: the sheet has to be viewable by link.
We wrote the Google sign-in route and never linked it in: the permission Google offers covers every sheet in the account, and we won't ask for that.
Your spreadsheet
It stays in your Google account. Only Google’s hosts are contacted.
Larssoni
Checks the shape, swaps the numbers, publishes a new version. No AI in this step.
Set-up only: column names and masked digits
Anthropic
The locked frame
Draws the dashboard. Cannot call out, submit a form or open a window.
Your viewers
No account, nothing installed.
6 · Abuse: what we block
We refuse files that register a service worker, redirect on load, or embed outside pages. A dashboard can be up to 6 MB.
If a pasted file contains a live key or password, publishing stops until its owner says otherwise.
The override is written to the change log.
Every file is scored for brand names, password fields, urgency and QR codes. Above the line, a person looks before it goes live.
On the free plan any password field waits for a person.
Every dashboard page has a report link.
Three separate reports take a free dashboard offline at once. A paid one goes to the front of our queue and stays up until someone has looked.
Disabling a dashboard takes effect on its next request.
7 · Where it runs, and who else touches your data
Workers, R2 storage, the D1 database, KV and Queues.
There are no servers of ours to patch.
The database is provisioned in Western Europe.
| Who | What for |
|---|---|
| Cloudflare | hosting, storage, database |
| Stripe Payments Europe | payments; card details never reach us |
| Postmark (ActiveCampaign) | |
| Anthropic | the set-up step only, masked values |
Google is not on the list: when you sign in with Google or link a sheet, Google gives data to us at your instruction.
Every email we send comes from hello@larssoni.com, through Postmark.
8 · Leaving
One file: every dashboard, the list of them, the change log, the view counts and where each dashboard came from.
The workspace owner can ask for it once a day.
a list of your dashboards (JSON)
audit-log.json
views.csv
provenance.json
dashboards/<each-dashboard>.html
What the export contains.
Its link stops at once. You have 30 days to undo it; then the files are erased.
The workspace closes at once and any subscription is cancelled. The files are erased after 30 days.
9 · Not yet
Not yet. Sign-in is an emailed link or Google.
Not yet. One region, Western Europe.
Not yet. See 5.5.
If one of these is what stands between you and yes, tell us. It moves up the list.
What to allowlist.
Three names for browsers, one for mail. A dashboard cannot contact anything else: the frame does not allow it.
For the people opening dashboards
larssoni.comthe viewer pagelarssonipages.comdashboards on paid planslarssoni.codashboards on the free plan
Only if a dashboard uses them:
cdn.jsdelivr.netcdnjs.cloudflare.comunpkg.comfonts.googleapis.comfonts.gstatic.com
For your mail filter
hello@larssoni.comsign-in links and the codes that open locked dashboards, sent through Postmark.
Paperwork, and a person.
Data processing agreement
Article 28, and it takes effect with our terms, so there is nothing to sign and chase. Read it
Breach notice
Within 48 hours, in the agreement.
New sub-processors
30 days' notice and a right to object.
Reporting a vulnerability
Send the questionnaire as it is.
Spreadsheet, PDF or portal link. The person who built the product fills it in.