Embedding
Security model
What the chart can do on your page, what we can see, and why the answer to both is smaller than you would expect. Written so you can check it rather than take it on faith.
Why an iframe
The chart runs in an iframe served from our origin, not as a script in your page. That single decision is the whole model, and it is worth being explicit about what it buys you, because the alternative is what most embed products ship.
| Script tag | This embed | |
|---|---|---|
| Runs in your page's realm | yes | no |
| Can read your DOM, forms and cookies | yes | no |
| Can read your localStorage | yes | no |
| Needs entries in your CSP | script-src, plus whatever the bundle fetches | frame-src, and nothing else |
| Its crashes can break your page | yes | no |
| Its bundle is your page weight | yes | no, a separate document |
None of those “no”s are promises we make. They are enforced by the browser’s same-origin policy, which means they hold whatever we ship, whatever a bug of ours does, and whatever a future version changes.
If you cannot load a third-party script at all
Common, and correct, on any page that also renders balances, positions or an order form: a remote <script src> is arbitrary code execution in your origin, changeable by us at any time, without your review. There is no version of that we would ask you to accept.
You do not have to. embed.js is a convenience wrapper over a documented, versioned postMessage protocol, it is not a dependency. A bridge you write and bundle yourself is about thirty lines, supplies the chart’s data and drives it exactly the same way, and leaves your CSP addition at one frame-src entry.
The whole surface
Exactly three things cross the boundary, and you can watch all of them in your own devtools.
The loader (embed.js) is the one piece of our code that runs in your page. It builds iframes, listens for two message types, and claims one global. It sets no cookie, makes no request of its own, and reads nothing about your page beyond the attributes on your own placeholders. You can drop it entirely and use the plain iframe form.
Reader sign-in requires HTTPS
The chart draws on a plain-HTTP page exactly as it does anywhere else. What is not offered there is the optional reader sign-in, the control simply does not appear, and the browser console says so by name.
The reason is worth stating rather than hiding behind a policy. A grant is bound to your site, and a site deliberately ignores the scheme, so http://example.com and https://example.com are one embedder and one consent. That is right for identity and wrong for trust: over plain HTTP anybody on the network path a café router, a hotel, a hostile ISP, can serve their own page under your hostname, and the consent screen would honestly read “example.com” because the site genuinely is example.com. There is nothing we could put on that screen to tell the reader apart from you. Consent that cannot mean what it says must not be asked for.
http://192.168.1.10 is still a network somebody can be on, and is refused like any other plain-HTTP origin.What we can see
Almost nothing, and that is a consequence of the data model rather than a promise. A chart on your site draws from your datafeed that is required, not optional, so it makes no market-data request of ours at all. Which symbols your readers look at, at which intervals, and how often: none of it reaches us, because none of it is ever asked of us.
What is left is the request for the frame document itself:
- The referring page, when the browser sends one, the same header any image or font request carries.
- Ordinary request metadata: IP, user agent, timestamps.
Not your page’s content, not its other requests, not your readers’ identities. There is no analytics in the embed and no tracking pixel.
Cookies and storage
The embed sets no cookie for an anonymous reader. Browsers partition storage for third-party frames in any case, so anything the chart does keep, a reader’s preference for hiding news marks, say, is scoped to the pair of (your site, our origin). It is invisible to us on other sites and to every other embedder.
SameSite=Lax cookies from third-party contexts. That is precisely why the optional sign-in uses a popup and a scoped token instead of a cookie.The optional reader sign-in
A reader may connect their own VerticalProp account to run indicators they have installed. It is optional, it asks nothing of your page, and it grants one permission: reading their indicator library.
- Consent happens in a popup on our domain. No credential field ever renders inside an embedded frame, that screen refuses to be framed at all.
- The credential is bound to one permission, to YOUR site, and to ten minutes. It is not a session and cannot be used as one.
- A grant is per site: allowing your site has not allowed anybody else’s.
- They can withdraw it at any time from their account page.
Your page cannot read that token, the frame is cross-origin to you, and we cannot spend it on anything but the indicator list it names.
Community indicators
Indicators are code other people wrote. They execute in a second sandboxed frame on a different domain, with default-src 'none': no network, no storage, no cookies, and their own process. They cannot reach your page, ours, or anything else.
Checking this yourself
Three things you can verify in a browser in about a minute:
- In the console,
document.querySelector('.vp-chart iframe').contentWindow.documentthrows. That is the boundary, and it is the browser saying so rather than us. - In the network tab, every request the frame makes is listed under its own document, and none of them carry your cookies.
- Delete the
<script>and use the plain<iframe>form. The chart still works and only the automatic height goes away, nothing of ours needs to run in your page at all.
Reporting a problem
If something here is not true, we would rather hear it from you than from your readers. Write to security@verticalprop.com with what you did and what happened.