Legal
Privacy Policy
Last updated: 2026-05-20
1. Who we are (the data controller)
The data controller for the personal data described below is:
- Alve Landgren, operating AstroEquip as a sole trader in Sweden.
- Contact: info@astroequip.eu
- Storefronts: astroequip.store, astroequip.eu, astroequip.se
- App API:
astro-matcher.vercel.app
We have not appointed a Data Protection Officer because the scale and sensitivity of processing does not require one under GDPR Art. 37.
2. What we collect, why, and on what lawful basis
The table below is exhaustive. If a category is not listed, we do not collect it. "Identifier" categories never include name, email or address for anonymous visitors.
| Category | What exactly | Purpose | Lawful basis (Art. 6) |
|---|---|---|---|
| Anonymous device ID | A random string in browser localStorage (key af_device_v1), e.g. dev_1715…_a9f3. Never linked to your real identity. |
So the Gear Finder remembers your saved setups across sessions on the same browser. | Art. 6(1)(b), performance of the service you requested. Strictly necessary, no consent banner required. |
| Wizard state (anonymous) | Your in-progress and saved setups in localStorage (keys af_setup_v1, af_setups_v1, af_build_v1, af_customs_v1). Lives on your device only. |
Resume the wizard, switch between named setups, keep your custom-gear list. | Art. 6(1)(b), service performance. |
| Saved gear setups (signed in) | When you are signed in to a Shopify customer account on our storefronts, your setup JSON is also stored against your Shopify customer ID as a customer metafield, and a copy is logged in our own database (table customer_setups) with your numeric Shopify customer ID. |
Cross-device sync of your saved setup. | Art. 6(1)(b), service performance. |
| "My Picks" candidate list (signed in) | The Picks feature is a flat list of candidate product handles you are comparing (distinct from a saved setup). When signed in, the list is stored against your Shopify customer ID as a customer metafield (astro.picks) and mirrored to our own database (table customer_picks) with your numeric Shopify customer ID and the array of handles. No prices, no email. |
Cross-device sync of your Picks list; scoring the bundle against your saved setup. | Art. 6(1)(b), service performance. |
| Approximate region & currency (for the Buy link) | When a product shows a Buy link, we read the approximate country and currency Shopify infers for your visit (via Shopify Markets), plus the storefront domain. This is used only at the moment the page renders, to choose which partner retailer's Buy link to show. It is never stored, logged, or linked to you — it forms no part of any record we keep, and never reaches our database. | Show a Buy link for a retailer that can actually ship to you, in your currency, so you avoid surprise customs or exchange-rate costs. It never affects product ranking or which products you see. | Art. 6(1)(f), legitimate interest in a useful, region-appropriate Buy link. Balancing test: the signal is ephemeral, never stored, and builds no profile, so the impact on you is negligible. Also gated behind the optional "Region & currency" cookie category (Shopify Customer Privacy preferences): decline it and we route by storefront domain only. |
| Anonymous match analytics | Every time you click Find my matches, an anonymous row is written to our database tables submissions and match_events: random session ID, your gear specs, Bortle scale, owned filters, the matched products, score breakdowns and rules fired. No email, no IP, no customer ID is stored in these tables. |
Improving the matching engine, spotting categories where users see zero matches, prioritising catalog expansion. | Art. 6(1)(f), legitimate interest in improving a free service. Balancing test: the data is genuinely anonymous and discarded after 90 days, so the impact on you is negligible. |
Result-click engagement (result_clicks) |
When you click "Add to build" on a recommended product, click "Swap" on a slot in My Setup, interact with a bundle, or click a recommended product's tagged "(affiliate) Buy" link, we write one row containing the same random session ID issued for your match, plus the product ID, its category, the action you took, the engine's score for it, and its rank in the result list at click time. No email, no IP, no customer ID. | Validating whether the matching engine actually surfaces the gear you want; spotting cases where the top pick is regularly ignored in favour of a lower-ranked one (the engine has a bug or a weight is off). | Art. 6(1)(a), consent — recorded only if you have accepted the Analytics cookie category (Shopify Customer Privacy). Pseudonymous via the same per-match UUID; withdraw at any time by declining or clearing Analytics consent. |
Catalog-gap signal (catalog_misses) |
When you type a brand or model into a wizard autocomplete field and the dropdown returns no match, we write one fully-anonymous row containing the typed query (trimmed, max 64 chars), which field it was typed into, its length, and the wizard locale. No session ID, no IP, no cookie, no customer ID. Per-page-load dedup so the same typed query is logged at most once per session. | Catalog roadmap input. If hundreds of users type a brand we don't carry, we should add it. | Art. 6(1)(f), legitimate interest in improving the catalogue. The query is unlinkable to any individual. |
| Webhook bookkeeping | If you place an order on a storefront, Shopify forwards orders/paid and inventory webhooks to us; we record only the Shopify order ID (table processed_webhooks) to avoid processing the same event twice. |
Idempotency of webhook processing. | Art. 6(1)(f) legitimate interest in reliable service; Art. 6(1)(c) for GDPR compliance webhooks (customers/data_request, customers/redact, shop/redact). |
| Operational metadata | HTTP request metadata (timestamps, status codes, user agent, the path requested) and rate-limit counters keyed by an HMAC-SHA-256 hash of your IP address, truncated to 16 hex characters and salted with a server-side secret. The raw IP is never persisted by our application. | Security, rate limiting, debugging, abuse prevention. | Art. 6(1)(f), legitimate interest in service security. |
| GDPR audit log | When Shopify sends a customers/data_request, customers/redact or shop/redact compliance webhook, we write one row to gdpr_audit_log recording the action, the customer ID it concerned, the timestamp, and per-table counts of rows touched. |
Demonstrate compliance with Art. 12 / 17 / 28(3)(h). | Art. 6(1)(c), legal obligation. |
3. What we explicitly do NOT collect
- Payment data. We do not process payments and have no card-handling infrastructure.
- Geolocation server-side. The Telescope Simulator may ask your browser for coordinates so it can centre the sky map on your location, but that is opt-in inside the browser, processed locally, and never sent to our servers.
- Browser fingerprints, canvas fingerprints, or device fingerprints.
- Marketing identifiers, advertising IDs, cross-site cookies, or behavioural-advertising data.
- Affiliate-click cookies or identity. When you click a tagged
(affiliate)“Buy” button we set no cookie of our own and never learn who you are; if you have accepted the Analytics category we record only the anonymous engagement event described in the table above (random session ID, product, action — no email, IP, or customer ID). You then leave for the retailer's own site (AstroShop, First Light Optics, or Amazon); the retailer may set its own first-party cookie on its domain to attribute a possible sale to us, governed by the retailer's privacy policy, not ours. We receive only aggregate commission totals, never your identity. See our Affiliate Disclosure. - Email or address fields for anonymous visitors. The Gear Finder does not ask for them.
- Special-category data under GDPR Art. 9 (health, ethnicity, religion, biometrics, etc.). There is no point in our service where that would be relevant.
- Data on children. The service is not directed at children under 16.
4. Cookies and similar storage
We only use strictly-necessary first-party storage; no consent banner is required under the ePrivacy Directive (Art. 5(3)).
Our storefronts display Shopify's customer-privacy cookie banner (Required, Personalization, Marketing, Analytics categories). The banner is part of Shopify's platform, not ours. Our anonymous match-time analytics is processed under the legitimate-interest basis in Section 2, not under consent, so the banner choice does not legally gate that processing.
Where Shopify's Customer Privacy API is loaded by the storefront's theme, the Gear Finder also reads it at runtime and treats an explicit decline of the Analytics category as a GDPR Art. 21 objection: it drops result-click events, catalog-gap events, and the match-time analytics row before any network call is made. On themes that do not expose this API, the banner is informational only; in either case you can object at any time by emailing us and we will stop processing your data. The Marketing category is unused by AstroEquip regardless: we do not run advertising or marketing cookies.
| Storage | Where it lives | Purpose | Lifetime |
|---|---|---|---|
af_device_v1 |
localStorage | Anonymous device ID | Until you clear browser storage |
af_setup_v1 |
localStorage | Current wizard state | Until you clear browser storage |
af_setups_v1 |
localStorage | List of named saved setups | Until you clear browser storage |
af_build_v1 |
localStorage | Your currently assembled "My Setup" | Until you clear browser storage |
af_customs_v1 |
localStorage | Custom gear you added that isn't in our catalog | Until you clear browser storage |
astro.picks |
localStorage | Your "My Picks" candidate list (handles + optional notes) | Until you clear browser storage |
astro.picks-build |
localStorage | The kit-builder state on the Picks page (slot assignments) | Until you clear browser storage |
| Tutorial-seen flag | sessionStorage | Suppress the simulator tutorial on re-entry | Until tab is closed |
cust_token (request body) |
In-memory only | Single-use HMAC token so the simulator iframe can load your saved setups | 5 minutes; consumed on first use |
We do not use third-party tracking cookies, advertising pixels, or cross-site identifiers. There is no Google Analytics, no Meta pixel, no Hotjar, no session replay.
5. Sub-processors and where data lives
Our database is self-hosted. Since 22 July 2026 every server-side record described in section 2 lives in a PostgreSQL database that we run ourselves, on our own hardware in Sweden. It is not rented from a database company, so for the data itself there is no third-party processor and no transfer out of the EU. Before that date the database was hosted by Neon, Inc. in Frankfurt; that provider no longer holds our production data.
We engage the following processors, each under a written Data Processing Agreement that meets the standards of GDPR Art. 28.
| Processor | What they do for us | Region | Transfer mechanism |
|---|---|---|---|
| Shopify International Ltd. | Hosts the storefront, customer accounts, and the customer metafield where your saved setup is stored. Forwards mandatory compliance webhooks to us. | EU / Ireland (with global infrastructure) | Adequacy + Shopify's DPA + Standard Contractual Clauses where applicable. |
| Cloudflare, Inc. | Two roles. (1) Our production database is reached only through a Cloudflare Tunnel protected by Cloudflare Access, so Cloudflare carries the encrypted connection between our API and our database. (2) Cloudflare R2 stores our nightly database backups. Backups are encrypted on our own hardware before they are uploaded, and our backup job refuses to upload an unencrypted copy, so Cloudflare holds only ciphertext it cannot read. | United States company with global infrastructure; the R2 bucket and the tunnel edge serve from Cloudflare's network. | Cloudflare's DPA and Standard Contractual Clauses (Art. 46(2)(c)). Backups are additionally encrypted end-to-end by us, so no readable personal data is exposed to the processor. |
| Vercel Inc. | Serverless hosting of our API endpoints and operational logs. | Functions execute in the EU; logs retained for 30 days in EU regions. PII is scrubbed before it reaches the log stream. | Vercel's DPA, SCC where any cross-border processing is required. |
| Centre de Données astronomiques de Strasbourg (CDS) | Provides the public Aladin Lite and Simbad APIs the Telescope Simulator queries for sky-survey tiles and object catalogues. Queries are anonymous and contain no PII. | France (EU). | Intra-EU. |
| Ko-fi Labs Ltd. (optional donations only) | Processes voluntary donations made via the "Support development" button. Collects donor name + email + payment data on Ko-fi's own platform; we never see card details. Used only if you click through and complete a donation. | United Kingdom (post-Brexit adequacy decision). | Adequacy decision; Ko-fi's published DPA. |
We do not sell, lease, or share personal data with any party outside this list, and none of the parties listed above are advertising networks.
6. Retention
We keep data only for as long as it is needed for the purpose it was collected. Retention windows are enforced by an automated daily cleanup job at 03:00 UTC.
| Data | Kept for |
|---|---|
| Saved gear setups (customer_setups) | Until you delete it; erased within 30 days of an erasure request. |
| "My Picks" candidate list (customer_picks) | Until you clear the list or your account is erased; erased within 30 days of an erasure request. |
Anonymous device records (device_setups, dev_* only) |
90 days of inactivity, then automatically erased. |
| Match analytics (submissions, match_events) | 90 days, then automatically erased. |
Result-click engagement (result_clicks) |
90 days, then automatically erased. |
Catalog-gap signal (catalog_misses) |
90 days, then automatically erased. |
| Processed-webhook bookkeeping | 60 days, then erased. |
| Rate-limit counters | 1 hour. |
| Single-use API tokens (token_nonces) | 1 day past expiry. |
| GDPR audit log | Indefinite; required for compliance evidence under Art. 28(3)(h). |
| Vercel operational logs | 30 days (Vercel platform default). |
7. Your rights under the GDPR
Under Articles 15–22 of the GDPR you have the right to:
- Access the personal data we hold about you (Art. 15).
- Rectify inaccurate data (Art. 16).
- Erase your data (Art. 17, the "right to be forgotten").
- Restrict processing while a dispute is resolved (Art. 18).
- Port your data to another controller in a machine-readable format (Art. 20).
- Object to processing based on legitimate interest (Art. 21).
- Withdraw consent at any time, for any consent-based processing (Art. 7(3)). Withdrawal is as easy as giving it. One click in the email footer or one email to us.
- Not be subject to fully automated decisions with legal effect (Art. 22). Our gear-matching engine produces recommendations only; nothing it returns has legal consequences for you.
How to exercise these rights
-
Through Shopify (signed-in customers). Use Shopify's
built-in "Request my data" or "Delete my account" flow on any of our
storefronts. Shopify dispatches a
customers/data_requestorcustomers/redactwebhook to us, we respond within 30 days, and erasure is completed within 10 days of a deletion request. - Directly by email. Write to info@astroequip.eu and include either your Shopify customer ID or, for anonymous visitors, the device ID visible in your saved-setups panel. We will reply within 30 days (Art. 12(3)).
8. Merchants installing the AstroEquip app
If a Shopify merchant installs our app on their own store and later
uninstalls it, Shopify dispatches a shop/redact webhook
48 hours after uninstall. On receipt we permanently erase all data
tied to that shop from our database and write a single audit row
recording the action.
9. International transfers
Our database is self-hosted in Sweden and our remaining processors operate largely in the EU (Vercel in EU regions, CDS in France). Three processors involve transfer to, or a company established in, a third country:
- Cloudflare (United States) carries the encrypted connection to our database and stores our nightly backups in R2. The backups are encrypted by us before upload and Cloudflare never holds the key, so it cannot read them. We rely on Cloudflare's DPA and the Standard Contractual Clauses (Art. 46(2)(c)).
- Shopify's own infrastructure is global; we rely on Shopify's published DPA and its SCCs. You can read Shopify's privacy statement for their position.
- Ko-fi (United Kingdom) operates under the EU's adequacy decision for the UK and its own DPA. Used only for voluntary donations you initiate yourself.
You may request a copy of the safeguards in place by emailing us.
10. Security
The technical and organisational measures we use include:
- TLS 1.2+ for every connection between your browser, our API and our processors.
- Database backups are encrypted with a public-key scheme (
age) on our own hardware before any copy leaves it. Our backup job refuses to upload an unencrypted dump, so off-site storage only ever holds ciphertext. - The database is not exposed to the public internet. It is reachable only through a Cloudflare Tunnel gated by Cloudflare Access service tokens, and the application connects with a non-superuser role.
- HMAC-signed requests on every Shopify App Proxy endpoint, including timing-safe verification, anti-replay windows, and shop-domain pinning.
- Single-use, time-limited HMAC tokens for the simulator-iframe handshake.
- Per-customer and per-IP rate limits on every write endpoint.
- Strict log scrubbing: email addresses, names, phone numbers, addresses, and postal codes are redacted from logs before they leave our application.
- Principle of least privilege: the Shopify API token requested by the app holds only
read_products, read_inventory, read_orders, read_locations, write_customers. - Daily automated cleanup of expired rows in line with the retention table above.
11. Children
The service is not directed at children under 16. We do not knowingly collect personal data from anyone under that age. If you believe a child has provided personal data to us, contact us and we will erase it.
12. Automated decision-making and profiling
Our matching engine ranks products by compatibility with the gear you describe. This is automated processing but does not produce legal or similarly significant effects (Art. 22). The output is a list of suggestions; you decide what, if anything, to do with it. The full scoring approach is documented in the How the engine matches guide.
13. Complaints
If you believe we have mishandled your data, you have the right to lodge a complaint with a supervisory authority:
- Sweden (lead authority): Integritetsskyddsmyndigheten (IMY).
- United Kingdom: Information Commissioner's Office (ICO) for users protected by the UK GDPR.
- Other EU/EEA countries: the data protection authority in your country of residence.
Swedish residents may also use Allmänna reklamationsnämnden (ARN) for out-of-court consumer dispute resolution.
14. Changes to this policy
Material changes are signalled by updating the "Last updated" date at the top of this page. Substantive revisions will be announced on the storefronts at least 14 days before they take effect, with a brief summary of what changed.
15. Contact
Email info@astroequip.eu for any privacy question, data-subject request, or to request a postal address for written correspondence.