Cookie and Local Storage Policy

Last updated: 25 July 2026

1. What this document covers

This policy explains what Gigapop stores on your device, and what it reads back from your device, when you use https://gigapop.app (and www.gigapop.app, which serves the same site).

We deliberately do not call this a "cookie policy" only. The law here — Article 5(3) of the ePrivacy Directive 2002/58/EC, as transposed into the national law of each EU/EEA country — is not about cookies specifically. It covers any storing of information on your terminal equipment, and any gaining of access to information already stored there. That includes classic HTTP cookies, but also localStorage, sessionStorage, tracking pixels, and device fingerprinting. This document therefore covers everything Gigapop puts on your device, whatever the technical mechanism.

The separate question of what we then do with that data as personal data — the purposes, the legal bases under the GDPR, who receives it, how long we keep it, and your rights — is answered in our Privacy Policy. The two documents are meant to be read together. Where this one repeats something from the Privacy Policy, the Privacy Policy is the fuller statement.

2. Who is responsible

Gigapop is operated by Ivan Smetanin, an individual entrepreneur (sole trader, not a company), established in Kazakhstan. He is the data controller for the processing described here.

Contact for anything in this document, including questions about storage on your device: hi@gigapop.app

Note for the reviewer: Article 13(1)(a) GDPR requires the identity and the contact details of the controller, and a supervisory authority will expect a postal address for a trader selling to consumers, not an email address alone. No postal address is given here or in the Privacy Policy, and none has been supplied to the drafter, so none has been invented. It must be added before publication. The same applies to the statement required under Art. 13(1)(b) — either name a data protection officer or state that none is appointed and why. See §9.21.

We have no establishment in the European Union. Because we offer a paid service to consumers in the EU, in Russian and English, priced in euros, the GDPR applies to us under Article 3(2)(a), and the national ePrivacy rules of the Member States where our users are located apply to what we store on their devices. We are not attempting to argue otherwise.

Note for the reviewer: an EU representative under GDPR Art. 27 has not yet been appointed. This is required for this fact pattern and it must be named here and in the Privacy Policy before launch. See §9.13.

The site is hosted on servers operated by Hetzner in Finland, inside the EU/EEA.

Hosting inside the EEA is not the whole story, however, and we should not present it as if it were. The controller is established in Kazakhstan, which is not covered by an adequacy decision, and the site is administered — including the admin dashboard that displays the traffic figures described in §6 — by that controller rather than by anyone inside the EEA. To the extent that access happens from outside the EEA, it is a transfer under Chapter V of the GDPR and needs both a transfer mechanism and a statement here. Neither this document nor the Privacy Policy currently addresses it. See §9.20.

3. The rule we have to meet

Under Article 5(3) there are exactly two cases where we may store something on your device, or read something from it, without asking you first:

There is no third exemption. In particular there is no "legitimate interest" route for storing things on your device, and there is no general exemption for analytics — not even for analytics that we run ourselves, on our own domain, without any third party involved.

Everything that does not fit (a) or (b) requires your prior consent: freely given, specific, informed, unambiguous, and given by a clear affirmative action. Silence, a pre-ticked box, scrolling, or "by continuing to browse you agree" are not consent.

Below we mark each item honestly against that test. Where we think an item is not exempt and we are nevertheless setting it without valid consent, we say so rather than reclassifying it as "functional" or "essential" to make the table look better.

4. Cookies

All of these are first-party cookies, set on the gigapop.app domain, and your browser sends them back only to gigapop.app. None of them is a third-party or advertising-network cookie, and none is shared with an advertising network. One of them, gb_ref, nevertheless serves a marketing purpose (affiliate attribution) — being first-party does not change that, and it is marked accordingly in the table below.

Unless stated otherwise they are set by our server with the flags HttpOnly, Secure, SameSite=Lax, Path=/. In plain terms: HttpOnly means the page's JavaScript cannot read them; Secure means they are only sent over HTTPS; SameSite=Lax means they are not attached to most requests started from another site, though they are still attached when you follow an ordinary link from another site to us; Path=/ means they are sent on every page of our own site.

Name What it stores What it is for Duration Strictly necessary?
gb_user A signed token containing your Gigapop account ID and an expiry timestamp, protected by an HMAC signature. No name, no email. Keeps you signed in, and proves that the songs, orders and jam contributions in your account are yours. 30 days from sign-in Yes as to authentication itself — Art. 5(3)(b). But it is a persistent login cookie that survives closing the browser, and you are never offered a "keep me signed in" choice. See §9.16.
gb_guest A random token (about 24 characters) with no meaning outside our database. Two things, and this matters (see the note below): (1) it ties a song, a jam contribution or a "like" to you before you create an account, so that you can come back to a song you started; (2) it is used as the unique-visitor key for our own traffic statistics. 90 days from when it is first issued. The code does not extend it on later visits, so it expires 90 days after your first visit unless it has been deleted and re-issued. Partly. Use (1) is strictly necessary. Use (2) — analytics — is not exempt and needs consent. See §9.2.
gb_partner A signed expiry timestamp plus an HMAC signature. No identifier. Keeps a jam host / partner signed in to the partner area at /partner/. 30 days Yes as to authentication — Art. 5(3)(b). The persistence point in §9.16 applies to this cookie too.
gb_auth A signed token for a staff login, containing the staff user name and an expiry, protected by an HMAC signature. Keeps an operator signed in to the internal admin area (/admin/, /history/). Ordinary users never receive this cookie. 30 days Yes. Authentication — Art. 5(3)(b). Staff only.
gb_ref A short partner referral code taken from the ?ref= parameter in the URL you arrived on. Referral attribution: if you later create an account, it credits the partner who sent you. Written by JavaScript in the page rather than by our server, so it is not HttpOnly and — in the current code — carries no Secure flag. Being a cookie, it is nevertheless sent to our server with every request to gigapop.app, not only at sign-up. 90 days, first-touch (not overwritten by a later ?ref=) No. This is marketing attribution. It is not necessary to deliver the service you asked for, so it requires consent. See §9.3.

About gb_guest — the honest version. One cookie is doing two jobs. Holding your unsaved work against a random token is genuinely necessary: without it, a guest who writes a brief and then refreshes the page loses it, and a jam guest cannot be prevented from voting five times. Counting unique visitors with the same token is not necessary — it is measurement, it benefits us and not you, and Article 5(3) does not exempt it. The fact that the two purposes currently share one cookie does not make the second purpose exempt. Our intended fix is described in §9.2.

Payments. When you pay, you are redirected to a checkout page hosted by Dodo Payments, which is the merchant of record for the purchase — Dodo is the seller and contracting party, collects and remits EU VAT and issues your invoice. That checkout page is on Dodo's own domain, and any cookies set there are set by Dodo under Dodo's own cookie and privacy notices, not ours. We do not embed Dodo's checkout in our pages and we do not place any payment-related cookie on your device ourselves.

5. Local storage (localStorage)

localStorage is browser storage on your own device. Unlike a cookie, your browser does not attach it automatically to every request — the page reads it locally to remember how you left things. That is not the same as saying it can never reach us: the page's own JavaScript can read a key and send it, and in one case below (jam_notify_<jam code>) it deliberately does, because we cannot email you a song without the address. Where that happens the table says so.

localStorage also has no expiry: a key stays until you clear your browser's site data, or until we remove it.

It is still fully covered by Article 5(3), so we list it here.

Key What it stores What it is for Duration Strictly necessary?
gb-theme light or dark-midnight The colour theme you picked with the theme switch. If you never touch the switch, nothing is written and we follow your operating system setting. Until cleared Yes, on the "UI customisation explicitly chosen by the user" reading of Art. 5(3)(b). Only written when you actually press the switch.
gb-lang ru or en The interface language you picked. If you never pick one, nothing is written and we use your browser's language, defaulting to Russian. Until cleared Yes, same reasoning as gb-theme.
gb-cookies-ack 1 Records that you dismissed the notice at the bottom of the screen, so it is not shown to you again. Until cleared Yes — storing the record of your own choice is exempt. But see §9.1: what this currently records is a dismissal, not a consent.
gb-adult-ok 1 Records that you confirmed the explicit-content warning before playing a track that is flagged as explicit, so we do not ask again. Until cleared Yes. You asked to play the track; the confirmation gate is part of delivering that.
gb_brief_likes A list of numeric IDs of example briefs you gave a thumbs-up to Re-highlights your own likes when you come back to the page. The like itself is also stored on our server against gb_guest or your account. Until cleared Yes — persistence of your own input in a feature you used.
gb_fb_<song id> 1, one key per song Records that you already left feedback on that song, so we stop asking. Until cleared Yes — persistence of your own input.
jam_notify_<jam code> telegram, an email address, or a "skipped" marker — one key per jam you joined Remembers, on your own device, how you asked to be told when the collective song is ready, so the jam page does not ask you again. If you enter an email here it stays in your browser and is also sent to our server so we can actually notify you. The jam page shows a "cancel / change" control that deletes this key from your device; deleting it does not by itself delete the address held on our server. Until cleared Yes — persistence of your own input in a feature you explicitly used.
gb_notes_sound 1 or 0 Sound on/off in the small note-catching game shown while your song is generating. Until cleared Yes — a setting you chose in a feature you opened.
gb_notes_best A number Your best score in that game. Until cleared Yes — the state of a feature you asked for.
gb-testmode 1 or 0 An internal development toggle. The button that writes it is rendered only for accounts the server reports as staff (/api/whoami), and the server ignores the flag for everyone else, so an ordinary user cannot turn test mode on. The key is nevertheless read on every load of /create/, whoever you are. Until cleared Not written for ordinary users. The read is covered by Art. 5(3) but is harmless; see §9.15 for what should still change.

We do not use sessionStorage, we do not use IndexedDB, we do not register a service worker, we do not use tracking pixels, and we do not fingerprint your device.

Every row above says "until cleared", and that is a statement about our design rather than a formality — §9.17 explains why indefinite is the wrong answer for several of these keys. One further mechanical point: a small script in the <head> of nearly every page reads gb-theme before the page paints, so the read happens even on pages that write nothing at all. Reading is as much within Article 5(3) as writing; we are satisfied this read is exempt under (b), but we would rather say it than let you discover it.

6. Our own traffic statistics

We run a small first-party visit counter. There is no Google Analytics, no Meta pixel, no advertising tag, no session recording, no heatmap, and no third-party analytics vendor of any kind on this site.

When you open a page, the page sends our server:

That last item is the most important line in this section, so we will not bury it. Because the account id is stored with each visit, these records are not anonymous statistics and not a sealed-off measurement standing apart from the rest of your data. For a signed-in user they amount to a page-by-page history of what you looked at and for how long, attached to your account and therefore capable of being read together with your orders, your songs and your email address. See §9.18.

Pages under /admin/ and /jam-admin/ are not counted at all. The page path is recorded without its query string, so the song identifiers and referral codes that travel in query strings do not reach the visit table by that route.

Separately, and independently of any of this, our web server (nginx) writes ordinary server logs, which include your IP address, the request and your browser's user-agent string. That is server-side logging, not storage on your device, so Article 5(3) does not apply to it — but it is personal data and it is covered in the Privacy Policy.

We must be straight about the legal position of the visit counter. Being first-party, being self-hosted, and involving no third party does not exempt analytics from the consent requirement. Article 5(3) asks what the storage is for, not who owns the domain.

There is a narrow national exception in France: under Art. 82 of the loi Informatique et Libertés, the CNIL treats strictly limited audience measurement as exempt. Its published criteria are that the tracker is used for audience measurement alone and for the exclusive account of the publisher, that it produces anonymous statistics only, that it does not lead to the data being cross-referenced with other processing or passed to third parties, and that it does not permit tracking across different sites; the CNIL further recommends a tracker lifetime of no more than 13 months, not automatically extended on new visits, and retention of the collected data for no more than 25 months, both reviewed periodically.

We would fail that test today, and not on a technicality. Our cookie lifetime (90 days, not extended on later visits) is comfortably inside the recommendation, but our visit rows carry the account id of signed-in users, which is precisely the cross-referencing with other processing that the exemption excludes and which stops the statistics being anonymous. We also have no defined retention period for the visit table. So the French route is not open to us as the counter is currently built, and it would only ever have covered our French users in any event. Across the rest of the EU the default is simply: this needs consent, and we are not currently obtaining it. See §9.2 and §9.18.

Note for the reviewer: the CNIL criteria above are taken from the CNIL's own page "Cookies : solutions pour les outils de mesure d'audience" (page dated 4 July 2025), which also publishes a self-assessment tool addressed to providers of measurement solutions. An earlier draft of this document asserted a change of regime "since 1 January 2026" and an express opt-out requirement; neither could be substantiated against the CNIL's published text and both have been removed rather than left in. Please confirm the current French position before any version of this paragraph is relied on.

7. Third-party content loaded by our pages

These are not storage on your device by us, but they cause your browser to contact a third party, which unavoidably discloses your IP address to that party. We list them so the picture is complete.

What Where What it means
Google Fonts (fonts.googleapis.com, fonts.gstatic.com) Almost every public page — around 40 page templates reference it, including the sign-in, creation, song and payment pages Fonts are fetched from Google's servers, so your browser discloses your IP address and user-agent to Google LLC (US). Google Fonts does not set a cookie, so this is not an Article 5(3) issue — but it is a GDPR issue, and a German court (LG München I, 20 January 2022, 3 O 17493/20) awarded damages against a site operator for embedding Google Fonts this way without a basis. We intend to self-host these fonts. See §9.9.
Telegram Web App script (telegram.org/js/telegram-web-app.js) The /play/ mini-game page only Loaded so the mini-game works when it is opened inside Telegram. It is a third-party script and can access browser storage on that page. See §9.10.
Dodo Payments hosted checkout Only after you choose to pay, on Dodo's own domain Governed by Dodo's own cookie and privacy notices.

We do not embed YouTube, social media plugins, share widgets, chat widgets, maps, or advertising tags.

8. What our banner does today — and what it does not

We are describing the current implementation, not an ideal one.

What it is. A small strip appears at the bottom of the screen on your first visit to one of the pages that carries it. It says that cookies are used "for sign-in", with a link to our Privacy Policy, and it has one button, labelled "ок" / "OK". Pressing it dismisses the strip and writes gb-cookies-ack = 1 to localStorage so it does not come back.

Where it actually appears. This is the correction that matters most in this document. The banner is injected by prototype/script.js, and that file is loaded on only about a dozen page templates. It is not loaded on the core product pages — the song-creation page (/create/), the song page (/song/), the payment page (/buy/), sign-in (/signin/), the account page (/me/) or the gift page (/gift/). The tracker and the gb_ref writer, by contrast, live in prototype/theme.js, which is loaded on essentially every page.

The practical consequence: a visitor who arrives from an advertisement or a Telegram link straight onto /create/, writes a brief, pays on /buy/ and listens on /song/ is counted, given a gb_guest cookie, and — on a ?ref= link — given a gb_ref cookie, without ever being shown any cookie notice at all. On the pages where the banner is suppressed by an explicit rule (/privacy/, /terms/) the tracker still runs, which §9.11 already recorded; what was missed is that on most of the funnel there is nothing to suppress in the first place.

Language. The banner's text is translated and the translation engine now runs on every page of the site that carries the banner, so it follows the language you have chosen. The legal pages themselves — this one included — are published in English only; see §9.12.

What it does not do, and we are not going to pretend otherwise:

What consent we are therefore relying on. For the strictly necessary items in §4 and §5 — the sign-in cookies, and the local storage that remembers your own choices — we rely on the Article 5(3)(b) exemption and no consent is needed, so the banner is not the operative thing there.

For the two non-exempt items — the analytics use of gb_guest, and gb_ref — we are currently relying on nothing that meets the legal standard for consent. A single "OK" button offers no genuine choice; the storage happens before the button is pressed in any event; and on much of the site the button is never shown at all. We are not going to describe that as "implied consent" or "consent by continued use", because those are not valid bases, and we are not going to call it "notice" either, because a notice that most users never see is not one. It is a gap, it is listed in §9, and it is being fixed.

9. Known gaps (to be fixed before, or immediately after, EU launch)

This section exists because an accurate cookie policy is worth more than a flattering one. It is written for our own team and our lawyer; a public version of this document may summarise it, but it should not silently drop it.

  1. The banner is notice-only. One "OK" button, no refusal option on the first layer, no granularity. Consent collected this way is not freely given, not specific and not unambiguous. It cannot lawfully support anything that is not exempt.
  2. The visit counter fires before any consent, and writes a cookie while doing it. The tracker in prototype/theme.js posts to /api/track on page load, and that endpoint issues the gb_guest cookie. This breaks the "prior" in prior consent. Two things must change: (a) the analytics call must be gated behind an affirmative opt-in; (b) gb_guest should be split so that the strictly-necessary ownership token is not the same value as the analytics visitor key — otherwise refusing analytics either breaks guest ownership or does not actually stop the measurement.
  3. gb_ref is written on first load with no consent. Referral attribution is a marketing purpose. It must be gated behind consent, or the referral programme must be re-designed to work without device storage (for example, by carrying the code through the sign-up URL). Separately, it is written by client-side JS without the Secure attribute — that should be fixed regardless of the consent question.
  4. No per-purpose granularity. Analytics and referral attribution are distinct purposes and need independently toggleable controls, defaulting to OFF.
  5. No withdrawal mechanism. GDPR Art. 7(3) requires withdrawal to be as easy as giving consent. Today there is no persistent control — no footer link, no floating icon — to reopen preferences. gb-cookies-ack actively prevents the banner from reappearing.
  6. Consent state is stored in localStorage, not a cookie, and only records dismissal. It carries no record of what was consented to, no timestamp, and no version of the notice shown. There is no server-side consent record at all, so we could not demonstrate consent if asked to.
  7. The banner text is inaccurate. "Cookies — for sign-in" is true of gb_user, gb_auth and gb_partner, but the site also sets an analytics visitor token and, on referral links, a marketing attribution cookie. An inaccurate consent notice is not only a transparency failure, it is arguably a misleading commercial practice.
  8. No retention limit on the visits table. Rows are inserted and never purged; there is no scheduled cleanup job in the code. GDPR Art. 5(1)(e) requires a defined period. A concrete figure has to be chosen (a 14-month raw-row retention with aggregate roll-up is the usual shape) and implemented as an actual job, not a policy sentence.
  9. Google Fonts is loaded from Google's CDN on public pages. Not an ePrivacy issue, but an unnecessary disclosure of every visitor's IP to a US recipient with no clear basis. The fix is trivial — self-host the fonts — and should be done before launch. Note that it is nine families across the site (Onest, Inter, Manrope, Space Grotesk, JetBrains Mono, Unbounded, Russo One, Bowlby One, Bagel Fat One), not the four an earlier draft assumed; check the whole set, not just the ones on the home page.
  10. The Telegram script on /play/. Assess whether the mini-game is in scope for the EU deployment at all. If it is, the third-party script should be loaded only when the page is actually running inside Telegram, not unconditionally.
  11. The banner is suppressed on /privacy/ and /terms/, but the tracker still runs there. That is exactly backwards — and it is the smaller half of the problem. The larger half is that the banner is not present on the main funnel at all (/create/, /song/, /buy/, /signin/, /me/, /gift/), because the file that injects it is not loaded on those pages, while the file that runs the tracker and writes gb_ref is. See §8.
  12. Language. The banner strip is translated and follows the language you have selected on every page that shows it. The legal pages themselves are currently published in English only and carry no translation markup; a Russian version is still outstanding. Our EU audience is served in Russian and English and the rest of the UI is localised; GDPR Art. 12(1) requires the information to be intelligible to the people it is aimed at. Both language versions must ship together, and they must say the same thing.
  13. No Art. 27 EU representative appointed. Not a cookie issue as such, but this document and the Privacy Policy both have to name the representative and its contact details, and failing to do so is itself a transparency breach. (The Dutch DPA fined a controller EUR 525,000 for non-designation alone.)
  14. No maintained inventory. There is no automated check that the table in §4/§5 still matches what the site actually writes. Adding a key in a page template today would silently make this document wrong. A pre-release DevTools check, or a small automated audit, should be part of the release routine.
  15. gb-testmode ships in the production bundle on /create/ and /song/. It is properly gated — the toggle renders only for staff and the server ignores the flag for anyone else — so this is a hygiene item, not a live exposure, and an earlier draft of this document overstated it. What remains is that every visitor's browser reads the key on /create/ and downloads the code for a feature that is not theirs; it belongs behind a build flag.
  16. Persistent login cookies with no "keep me signed in" choice. gb_user and gb_partner last 30 days and survive closing the browser. The Article 29 Working Party's Opinion 04/2012 on the cookie consent exemption treats session authentication cookies as exempt but expressly says that persistent login cookies which carry an authentication token across browser sessions are not covered by that exemption, and recommends offering the user a "remember me" control next to the sign-in form. Either add that control (unticked) and make the cookie a session cookie when it is not ticked, or be prepared to argue the point. This is cheap to fix and it removes an easy finding against us.
  17. Nothing in localStorage ever expires. Every key in §5 lives until the user clears their site data. For genuinely exempt items the exemption is not open-ended: Opinion 04/2012 scopes the "user input" exemption to the session (a few hours) and treats UI-customisation storage as exempt for a limited period tied to the choice the user made. Indefinite retention of gb_brief_likes, gb_fb_<song id> and jam_notify_<jam code> goes beyond that shape even if the underlying purpose is unobjectionable. Give each key a written expiry, enforce it in code, and record the chosen periods here.
  18. The visit table is joined to the account. visits.web_user_id is populated for every signed-in pageview, so the "traffic counter" is in substance a per-user browsing history. This is what the first draft failed to disclose and it changes the analysis rather than decorating it: it removes any argument that the measurement is anonymous or severable, it closes off the French exemption discussed in §6, and it raises a purpose-limitation question under Art. 5(1)(b) that a banner cannot answer. Decide deliberately whether the counter needs the account id at all — the honest answer is probably no — and if it does not, stop writing it.
  19. The published Privacy Policy contradicts this document. The live /privacy/ page lists only five storage items (gb_auth, gb_user, gb_guest, gb-theme, gb-cookies-ack), describes gb_guest only as a jam-guest cookie with no mention of the visit counter, and omits gb_partner, gb_ref and eight of the ten localStorage keys in §5. It then says that "analytics (Google Analytics, Yandex.Metrica, Facebook Pixel and the like) are not connected" and that there are no advertising trackers. Read strictly, the parenthetical limits that sentence to third-party tools and it is true. Read the way an ordinary consumer will read it, it says the site does not do analytics — and the site does. Volunteering a reassurance that is only saved by a narrow reading of its own parenthesis is exactly the shape of statement that draws a misleading-practice finding under the Unfair Commercial Practices Directive, on top of the Art. 13 transparency problem. Rewrite it to say plainly that we run our own first-party counter and no third-party analytics. This must land in the same release as this document, not after it.
  20. Access from outside the EEA is not addressed. Hosting is in Finland, but the controller is established in Kazakhstan, which has no adequacy decision, and the admin dashboard — which displays exactly the traffic data described in §6 — is operated by him. Where that access happens from a third country it is a Chapter V transfer. Both a transfer mechanism (standard contractual clauses do not fit a controller transferring to himself, so this likely turns on Art. 49 or on restructuring the access) and a disclosure under Art. 13(1)(f) are missing. Confirm the factual position first — from where, and by whom, the admin interface is actually used — then pick the mechanism.
  21. Controller identification is incomplete. No postal address is published for the controller, and there is no statement about a data protection officer. Both are ordinary Art. 13 content and their absence is the first thing an authority checks.

One more point that cuts the other way. If — after fixing the above — Gigapop ends up setting only strictly necessary storage, then no consent and no consent banner are required at all, and a plain informational notice like this document is the correct and sufficient answer. Adding a full consent banner to a site that does not need one creates pointless friction and, if it implies consent is being collected for exempt cookies, is itself misleading. The decision in §9.2 — keep the visit counter behind consent, or drop it in favour of aggregate server-log analysis with no device storage — determines which of the two shapes this site should have. That decision should be made deliberately, not by default.

10. Open questions for the reviewer

11. How to control storage yourself

Regardless of what we do, you can:

Two rights that belong here rather than only in the Privacy Policy. You may object to, and withdraw any consent for, the non-essential storage described above at any time by writing to hi@gigapop.app. Withdrawal has to be as easy as giving consent; we do not yet offer an in-page control that meets that standard, so until we do, that email address is the mechanism and we will act on it. And you may complain to a data protection authority — the supervisory authority of the EU/EEA country where you live, where you work, or where you think the problem occurred. You do not have to contact us first.

12. Changes to this policy

We will update this document whenever we add, remove or change what we store on your device, and we will move the "Last updated" date at the top. Where a change affects something you previously consented to, we will ask again rather than assume the old answer still applies.

Questions: hi@gigapop.app