← Back to the site

Privacy

Xenon is built to run on your machine, and the app holds to that. This website is a separate thing with separate rules. This page keeps the two apart and tells you exactly what each one does.

Last updated: 30 July 2026

The short version

  • The Xenon app does not track how you use it. There is no account, no profile and no usage log leaving your PC. Two counters exist: one rides the update check and describes the INSTALL — its version, its system, roughly how old it is; the other adds 1 to a creation’s install count when you install it from the Store. Neither says anything about which widgets you open, what you search for or what you do with the dashboard. On a new install both start switched on, and you can turn either off in Settings → Aggiornamenti; if you were already running Xenon before the version that added one, it stayed off for you. Neither carries an identifier, so neither can be tied back to you or joined to the other.
  • It is not fully offline either, and pretending otherwise would be the dishonest version. It checks for updates, and it contacts a service for weather or a music API only when you turn those features on. Every one of those calls is listed below.
  • This website uses Google Analytics to see which pages and downloads people reach. In the EEA, UK and Switzerland nothing is stored until you accept the banner.
  • It also counts pageviews with Cloudflare Web Analytics, which sets no cookie and builds no profile — it only tells me how many people reached a page.
  • Pressing a download button adds 1 to a counter on my own server, with no cookie and no identifier. The download link still points straight at GitHub, so that counter can never get between you and the file.

Who is responsible

Xenon is a personal open-source project by Marcello Mastroeni, and I am the data controller for this website. For anything on this page — a question, a correction, or a request about your data — write to marcellomastroeni4@gmail.com or open an issue on GitHub.

This website (xenon-app.com)

The site uses Google Analytics 4 (measurement id G-LQ8Y8WZPQF) to understand which pages get read, which downloads get clicked, and where visitors come from. It is aggregate reporting about the site — it is not connected to your copy of the app, and there is no way for me to link a visit here to an installation on your PC.

What Analytics receives

Cloudflare Web Analytics

Alongside Analytics the site loads Cloudflare Web Analytics, a cookieless pageview counter. It sets no cookie, stores no identifier and cannot follow you between sites or between visits; it reports the page URL, referrer, country, device type and page-load timing in aggregate. Because it identifies nobody it runs without the consent banner — but it is listed here rather than left unmentioned. Why both: Analytics only counts visitors who accept the banner, so this is the honest total those numbers are read against.

The download counter

When you press a download button on this site, the page also sends a one-line note to my own server (the same supporter hub the app talks to) saying that a download button was pressed, and which one — hero, footer, and so on. That is the whole message: no cookie, no identifier, no IP kept, and nothing that could be joined to anything else. It adds 1 to a counter for (day, button) and the request is discarded.

It exists because that single step was invisible to me. Cloudflare knows the page was viewed and GitHub knows a download finished, and neither can see the press in between — which is the one thing that says whether a download page actually works. Google Analytics could report it, but it only counts people who accepted the banner and a fair share of this audience blocks it entirely, so the number would be wrong in a direction I could not measure.

The download link itself does not go through my server. It points straight at GitHub, exactly as before; the counter is a separate, fire-and-forget note that the page does not wait for. If my server is down, or you block it, the download works normally and the press simply is not counted.

Your choice, and how it works

The site uses Google Consent Mode. If you are in the EEA, the UK or Switzerland, every storage and advertising signal starts as denied: until you press Accept, no cookie is written and Analytics receives only cookieless, non-identifying pings. Outside those regions the default is granted, and pressing Reject switches it off. Rejecting is one click, in the same place, at the same size as accepting.

You can change your mind at any time — this clears the stored choice and shows the banner again:

Your choice is remembered in your browser's local storage under xenon.site.consent. That value never leaves your device. Clearing your browser data resets it.

Hosting and fonts

The site is served by GitHub Pages, which processes the request and your IP address to deliver the page and keeps short-lived security logs — this is technically necessary and happens before any consent decision. Web fonts are loaded from Google Fonts, which likewise sees the request. Community screenshots are served from Cloudflare R2 (assets.xenon-app.com).

The Xenon app

The app keeps your data on your machine. Settings, notes, tasks, events, decks, AI memory and uploaded backgrounds all live in server/data/ on your PC. Nothing in that folder is uploaded anywhere, and there is no analytics SDK, no crash reporter and no behavioural tracking in the app. The two anonymous counters described below are the only things the app ever reports about itself, and neither carries an identifier of any kind: what the server keeps is counters, never rows about a machine.

The local file search and the disk-space widget are fully local by construction: your search phrases, file names, the folder index and the disk-scan results are computed on your PC and never sent anywhere. The list of which files you open from search (used to rank your own results better) lives in server/data/ like everything else.

What the app does do is make network calls for features that inherently need the network. Here is the complete list, and when each one happens.

What it contactsWhenWhy, and what it sends
api.github.com Automatic, at launch (cached 24h) Checks whether a newer release exists. It sends no identifier — but as with any HTTP request, GitHub sees your IP address. This is the one call that happens without you enabling anything.
xenon-app.com When you open the community catalog or Store Downloads the public catalog of community themes, widgets and packs. Read-only, no identifier.
xenon-app.com
(announcements)
Automatic, at most once a day Downloads the same short list of announcements that everyone gets: a new release, something new in the Store, the occasional note. Read-only, no identifier. Some announcements are meant for a subset of people — those running a particular widget, or a particular version — and that filtering happens on your PC: your dashboard downloads the whole list and decides for itself which ones apply. Nothing about what you have installed is ever sent to decide what you are shown, so I can neither target you nor tell who saw what. You can turn announcements off from any of them.
Supporter hub
(poll answers)
Only if you tap an answer on a poll shown in an announcement Sends which option you chose, and nothing else: no install UUID, no IP kept, no account, and not even a time. The server adds 1 to that option’s counter and discards the request, so there is no way to ask what any particular PC answered — including by me. Tapping nothing sends nothing, and turning announcements off stops it entirely.
Supporter hub
(install counts)
When you install something from the Store, and only if that switch is on Sends the id of the creation you installed, and nothing else: no install UUID, no IP kept, no account. The server adds 1 to that creation's counter and discards the request. Your dashboard remembers on your own PC which ones it has already counted, so it reports each creation at most once — which is why nothing identifying has to be sent to avoid double-counting. The consequence is that the number means "installs reported", not "how many people own it", and it is labelled that way wherever it is shown.
Supporter hub
(Cloudflare Worker)
When you rate a catalog item or redeem a supporter code Your PC keeps a random UUID (install-id.json) so a vote counts once and a code cannot be reused endlessly. That UUID never leaves your machine: what is sent is a one-way hash of it, and rating something and redeeming a code use different hashes of the same UUID. That is deliberate — with one shared value, a supporter code (which can be linked to a Discord account) and a star rating would sit in the database under the same identifier, and I would be able to see which creations a named supporter rated. With two, there is nothing to match them on. Neither hash is tied to your hardware or your name; delete the file and a new UUID is generated.
ipwho.is Only if weather auto-location is on Turns your IP into an approximate city so the weather widget knows where you are. Set the location manually and this call never happens.
Weather and air-quality APIs
open-meteo, met.no, wttr.in
Only with a weather widget on your dashboard Sends the coordinates being forecast. No identifier.
itunes.apple.com While media is playing Looks up album artwork by track title. No identifier.
Services you connect yourself
Spotify, Twitch, YouTube, Discord, Home Assistant, OBS, AI providers, stock/news/football feeds
Only after you configure that integration Each talks to its own provider under that provider's privacy policy, using credentials you supply. They stay dormant until set up.
Anonymous version ping
supporter hub
On by default on installs from v4.8.0; off on earlier installs unless you switched it on Version, system, how old the install is, and a first-of-the-week flag — no identifier of any kind, and no new host contacted. The complete list is in the section below.

Local AI features (Ollama, Whisper) run entirely on your machine. Cloud AI providers do not: if you choose Gemini, OpenAI or Anthropic, your prompts go to that provider under their terms. The app makes that choice yours, and never sends anything to a cloud model you did not select.

The version ping

I have no idea how many people actually run Xenon. Download counts are noise, and I would rather know whether people are stuck on an old release than guess. So there is a switch in Settings → General → Updates that adds a few fields to the update check the app already makes. Here is all of it, with nothing left out:

That is the entire payload, and it is worth saying what is not in it: no install UUID — that identifier stays reserved for ratings and supporter codes, and mixing it in here would let me correlate the two, which is exactly what I do not want to be able to do — no IP kept, no hardware detail, no timezone, and nothing whatsoever about which widgets you use, what you search for or what you open. There is no extra network request either: the ping rides the update check that already runs, so enabling it does not make the app talk to anything new.

Why two flags, instead of counting you

The one number I could not get without identifying people is “how many different installs were active this week”. Normally that needs an id, so the server can tell two pings from one PC apart from two pings from two PCs. Here your own PC does that job: it remembers, on your disk, the last week and month it reported, and simply says “first time this week”, once. The server adds 1 and knows nothing else. The weekly and monthly counts come out exact, and there is still nothing to identify you with.

Stored in pieces, on purpose

On the server side there are no rows about machines — only counters. And the fields above are counted in separate tables, one per field, never together in one row. That is the part that matters. A single row holding version and system and age and birth month and language could easily describe one single person; and because most of those stay the same from day to day, consecutive days could be matched into one machine’s history — an identifier built out of the data, after going to the trouble of not sending one. Counting each field on its own means that combination simply does not exist in the database: not hidden, not access-controlled, absent. What I can read is “N installs ran 4.11.0 today”, “N were born in July”, “N are older than 90 days” — never which N, and never the overlap.

Two places store a pair side by side, because there the pair is the question: how many installs moved from one version to another, and how many of a given birth month were still active in a later month. Neither carries anything else at all, and any combination seen fewer than five times is folded into “other” — including in the console only I can see.

Everything is deleted after 400 days. No per-request rows are kept, so there is no dataset that could be handed over, subpoenaed or leaked.

What changed in v4.11.0, and the one honest caveat

The switch itself did not change state for anybody: if it was off it stayed off, if it was on it stayed on. What changed is what an on ping carries, which is why this page changed in the same release — that is the commitment at the bottom of the table above, and this is it being kept.

The caveat: installs that already existed before this release report their age and birth month as unknown, and will keep doing so forever. The app never recorded when they first ran, and I would rather carry a visible gap than a guessed number. The blind spot is counted and shown as its own bucket, so I can always see how much of the picture is missing.

What changed in v4.8.0, and who it affects

Up to v4.7.1 this switch was off until you turned it on. From v4.8.0 it is on by default on new installs, because almost nobody turns a switch like this on and the count was therefore useless. You can turn it off at any time in Settings, and turning it off takes effect immediately.

If you already had Xenon installed, nothing changed for you. The new default is read only when there is no settings file yet, which means a fresh install. If you installed before v4.8.0 and left the switch alone, it stays off — you were told it was off unless you chose otherwise, and an update is not a place to quietly revisit that. If you had turned it on, it stays on. Either way the app keeps whatever your settings already say.

Your rights

Under the GDPR you can ask for access to, correction of, or erasure of your personal data, and you can object to processing. For this website, the practical routes are: press Reject (or the button above) to stop Analytics, use Google's opt-out add-on, or clear your browser data. For the app, uninstalling stops every call described above, and deleting server/data/ removes everything the app stored, including the install UUID.

You also have the right to lodge a complaint with a supervisory authority — in Italy, the Garante per la protezione dei dati personali.

Changes to this page

If the app starts making a network call that is not on the table above, that table gets updated in the same release — that is the commitment, and the release notes will say so. The date at the top tells you when this page last changed.