Trust
Security
This page is written to be checkable. Where something is not done, it says so — a security page that reads as pure reassurance is not useful to anyone deciding whether to install an app.
The permissions we ask for
Vitrine asks for read access to products and product listings, plus the unauthenticated Storefront permissions the mobile app needs to browse the catalogue, build a cart, start a checkout and sign a customer in.
It does not ask for access to your orders, your customer list, your finances or your staff accounts, and it has no write access to your products, prices or inventory. An install also verifies what Shopify actually granted and refuses to complete if a permission is missing, rather than continuing and failing later in a way that looks like a bug.
What we never hold
- Payment details. Checkout is Shopify's, from the first tap to the receipt. Card data never touches our servers.
- Shopper passwords or account credentials. Sign-in is Shopify's. Shopify issues a customer access token, the token stays on the device, and we forward it. We do not store it.
- Your customers' orders or profiles. Read live from Shopify with the shopper's own token and returned to their device. Nothing is copied into our database.
Authentication
The builder embedded in your Shopify admin has no password and no login screen. Every request carries a session token that Shopify's admin frame issues, valid for about a minute, and the server verifies it before doing anything: the signature, the algorithm (pinned, so a token claiming to need no signature is rejected), the audience, the expiry and not-before times, and that the two claims naming your shop agree with each other. A token stitched together from two sources fails all three of the last checks.
Because those tokens are short-lived and refreshed per request, there is nothing long-lived in your browser for a script on the page to steal. Who on your team can open Vitrine is controlled by your Shopify staff permissions, not by a separate user list we would have to keep in step.
The older standalone dashboard, which signs in with an email and password, hashes passwords with bcrypt and never stores them in a reversible form.
The install handshake
Three checks stand between an install request and your store:
-
The shop domain is matched against an anchored pattern. The domain
in an install link decides who we redirect you to, and later which host receives a
secret. A value like
shop.myshopify.com.example.compasses a naive "ends with" test, which is exactly why the check is a full-anchor match instead. - Every request from Shopify is signature-verified, using a constant-time comparison so that a wrong signature does not leak the right one a byte at a time.
- An install is tied to a single-use nonce, stored server-side, bound to one shop and expiring on its own. Reusing one, or presenting one issued for a different shop, fails.
The Admin API token we then obtain is an expiring one, acquired by exchanging the session token the app already holds. It is used only while a merchant is present in the app — reading your shop, creating the Storefront token, subscribing webhooks. Nothing runs against your store in the background.
Webhooks
Shopify signs each webhook over the raw request body, and we verify that signature against the exact bytes received before parsing anything. An unsigned or wrongly signed webhook is rejected and logged. Correctly signed topics we do not act on still return success, because failing them would make Shopify retry for days and eventually drop the subscription.
The uninstall webhook clears both access tokens as soon as it arrives — dead credentials sitting at rest buy nothing.
Keeping stores apart
Each store has a public store key that the mobile app sends to select which store it belongs to. That key is deliberately not a secret: it identifies a tenant the same way a public Storefront token does, and it grants no privileges by itself. Anything privileged needs either your merchant session or a shopper's own Shopify-issued token.
Your Storefront access token stays server-side. It is never compiled into the app your customers download, so it cannot be extracted from a binary.
Infrastructure
- All traffic is served over HTTPS, with TLS terminated at our edge.
- The database and cache are reachable only from the application on a private network. Neither is exposed to the internet.
- The application runs as an unprivileged user in a container that cannot rewrite its own code.
- Interactive API documentation is disabled in production, and error text returned by Shopify is logged rather than passed back to a client, so store internals do not leak through an error message.
- Access tokens are never written to logs. Every change is gated on an automated test suite, a linter, and a migration that must apply and roll back cleanly.
Validation and safe failure
A published theme is rendered by phones we cannot redeploy, so a theme is validated when it is saved, not when it is read: unknown fields are rejected outright, and a malformed document becomes an error in the builder rather than a crash on a customer's device. Conversely, an older app build that meets a section type it has never heard of skips it instead of failing. Published versions are append-only — a rollback copies an old document forward rather than resurrecting it — so a device can never be served stale content under a reused cache tag.
What we have not finished
These are known and tracked. We would rather you read them here than discover them.
- Access tokens are stored without field-level encryption. They sit in a database that is not reachable from the internet, on infrastructure we control, but they are not individually encrypted at rest. Envelope encryption is planned before Vitrine is generally available.
- There is no denylist for our own dashboard tokens. The standalone sign-in issues stateless tokens, so one cannot be revoked before it expires. This does not affect the embedded builder, which uses Shopify's short-lived tokens.
- Rate limiting is Shopify's, not ours. We forward a shopper's IP so Shopify limits per shopper rather than per server, but our own gateway does not yet impose limits of its own.
- No third-party certification. We hold no ISO 27001 or SOC 2 report, and no penetration test has been commissioned yet.
- We are a small team. Access to production is limited to the people who operate the service, but there is no separate on-call security function.
Reporting a vulnerability
If you find a security problem, please tell us before telling anyone else. Email [email protected] with enough detail to reproduce it. We will acknowledge within [response window, e.g. 3 business days], keep you updated, and credit you when a fix ships if you would like us to.
Please do not run automated scanners against production, do not access or modify data belonging to a store that is not yours, and do not test denial of service. We will not pursue legal action over research that follows those limits and gives us a reasonable chance to fix the issue.