Xenon

Privacy policy

The Xenon app runs on your own PC and stores its data there. This website is a separate thing with separate rules. This page keeps the two apart and says what each one sends and stores.

Last updated: 14 October 2026

Summary

  • 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 is sent when the app checks for updates and describes the install itself: its version, its system and 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 → General → Updates; 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.
  • The app is not fully offline. It checks for updates and fetches a short list of announcements on its own; weather, album artwork and the services you connect are contacted only by the features that use them. Those calls are 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.

Data controller and contact

Xenon is a personal project by one developer, Marcello Mastroeni, with its source on GitHub under a custom non-commercial licence, and I am the data controller for this website. For anything on this page, such as 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, and it is listed here so you know it is there. Why both: Analytics only counts visitors who accept the banner, so this is the full 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.

Cookie consent

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 button 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. The page header also asks the GitHub API for the project's star count, once per browser session, so GitHub sees that request too. The site's fonts are served from the site itself (/fonts/), so loading them contacts no one else. The one exception is the live demo, which runs the app's own dashboard page and loads that page's fonts from Google Fonts, which then sees the request. Community screenshots are served from Cloudflare R2 (assets.xenon-app.com).

The Xenon app

The app stores your data on your PC. Settings, notes, tasks, events, decks, AI memory and uploaded backgrounds all live in server/data/. The app does not upload that folder anywhere, and there is no analytics SDK, no crash reporter and no behavioural tracking in it. The two anonymous counters described below are the only reports the app makes 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 run on your PC: your search phrases, file names, the folder index and the disk-scan results are computed there, and the list of files you open from search (used to rank your own results better) lives in server/data/ like everything else. What you hand to Xenon AI is the exception. With a cloud AI provider, Smart search (Ctrl+I) sends the phrase you type to that provider, and asking the AI to find a file or explain your disk sends it the results it answers from, such as file names and folder sizes. The Full brain for AI search and cleanup switch (off by default) also sends the names of your installed apps, indexed folders, most-used folders and the files you recently opened from search, never their contents.

What the app does do is make network calls for features that need the network. Here is the 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. It runs without you turning anything on.
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, such as those running a particular widget or 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 nobody, me included, can ask what any particular PC answered. 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
(supporter count)
When you open Support in Settings, and in December for the once-a-year note Downloads two public numbers: how many people support Xenon right now and how many it takes to cover its costs. Nothing is sent: the request is the same from every PC, with no identifier, and your PC keeps the answer for an hour.
Supporter hub
(support buttons)
Only when you press a button to support Xenon: in Settings, in the Store, on a locked tile or on a supporter note The button opens Buy Me a Coffee through the hub, which adds 1 to a daily count for that button and plan (monthly or one-time) and then sends you on. No cookie, no install UUID, no account. The hub does not store your IP address. It keeps a one-way hash of it for up to a day, only to stop one address from pressing more than 120 times an hour.
Supporter hub
(Cloudflare Worker)
When you rate a catalog item, redeem a supporter code, or update something you unlocked 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. Updating something you unlocked sends that same hash, the id of the creation and the version you have, and no code: the hub answers only a PC it already knows as an owner.
ipwho.is, get.geojs.io, ipapi.co Only if weather auto-location is on Turns your IP into an approximate city so the weather widget knows where you are. Xenon asks these three services in this order and uses the first one that answers. Set the location manually and these calls never happen.
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.
lrclib.net While media is playing, if Song lyrics is on Looks up the lyrics of the current track by title, artist and length. No identifier. Turn it off in Settings.
Your AI provider
lyrics translation
Only if you turn on Translate lines in the Lyrics widget Sends the lines of the song playing to the AI provider you picked in Xenon AI, once per song, after 20 seconds of listening. With Ollama nothing leaves the PC. On Gemini's free tier Google may use what it receives to improve its products. Off by default.
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.
Your phone's push service
Apple, Google or Mozilla, depending on the phone's browser
Only after you turn on notifications on a paired phone or tablet Lets the PC wake a closed phone for something you set up and are waiting for, such as a countdown finishing. The message is end-to-end encrypted to that phone's browser, so the push service carries it without being able to read it; it does see that a message went to that phone, and when. Nothing is set up until you press the button on the phone, and removing the paired device stops it.
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, sent at most once a day to the same supporter hub as the rows above. No identifier of any kind. 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. Spoken replies work differently: with any provider other than Gemini or OpenAI, which have voices of their own, the text being read aloud is sent to Microsoft's online Edge voice service to be turned into speech. The app makes the AI provider your choice, and never sends a prompt 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, called Count this install, anonymously, that sends a few fields when the app checks for updates, at most once a day. This is the complete list:

That is the entire payload. 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 about which widgets you use, what you search for or what you open. The ping is one small request to the supporter hub, sent right after the update check the app already makes.

Weekly and monthly counts

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.

How the counts are stored

On the server side there are only counters, no rows about machines. 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” and “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.

Changes in v4.11.0

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.

One limit: 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.

Changes in v4.8.0

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 that is the Garante per la protezione dei dati personali.

Changes to this page

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