Canonical statement. Published at https://ideas.trustvalidated.com/privacy.html, which carries the same text. If the two ever disagree, this file is correct.
Six demos argue, in one way or another, that a site should ask for the minimum it needs and say so plainly. Instrumenting them silently would have been the fastest way to lose that argument, so this page is written to the same standard as the code: every claim below is checkable, and where something is collected it is named rather than described in the abstract.
The short version. Each page reports that it was opened, and four of them report one further thing you did on purpose (started the agent, started a voice session, ran a scenario). That is all. There is no cookie anywhere, no identifier survives the page load, nothing you type or say is recorded, and there is no session replay anywhere. Send Global Privacy Control or Do Not Track and nothing is sent at all. On voice. there is also a notice bottom right with a button that turns the whole thing off; answering it stores one word in your browser so it stops asking, and that word is the only thing the analytics ever writes to your device. The choice is reversible from the footer at any time, including forgetting it entirely so the notice asks again: a consent control you cannot revisit is a click-through, not a choice.
Two tables, and there is nothing outside them.
Events. The complete allowlist. Each one is an explicit call at a line you can grep for; nothing is captured by observation.
| Event | Where | When |
|---|---|---|
page_view | all six sites | the page loaded |
agent_run_started | jobs. | you pressed the button that runs the agent |
voice_session_started | voice. | you started a session, with mode: live or scripted |
scenario_started | console., rail. | you pressed a scenario button, with which one |
agent_prompt_sent | kit. | you submitted a prompt |
Properties. Attached to every event, and this is the whole set.
| Property | Value | Why |
|---|---|---|
path | the URL path only, never the query string | which page |
utm_source … utm_term | if present in the link you followed, truncated to 120 characters | which campaign or post sent you |
referrer_host | the host only — news.ycombinator.com, never the full URL | where you came from. What you were reading there is not our business |
distinct_id | a random UUID generated fresh on every page load, held in memory | required by the API. It is not you; it is this page load |
$process_person_profile: false | always | tells PostHog to build no person profile behind the event |
The honest cost of that distinct_id: unique visitors cannot be counted here, and returning visitors cannot be recognised. Reload the page and you are a new row. We do not report visitor counts, because we do not have them. This was the trade we chose over setting a cookie.
Not "off by default" — absent from the code, and the code is fifty lines you can read.
sessionStorage. No fingerprint. Unconditional.localStorage key, and only about consent. voice. writes voice-application:analytics-choice when you answer the notice, holding one of two words so an opt-out survives a reload. It is not an identifier and cannot be used to recognise you. Nothing else about the analytics touches your device. One demo's own product feature, separately, does: see the section on voice.'s remembered answers.There are none. Not a strictly-necessary one, not an analytics one, not a third-party one.
That is why you are not being shown a cookie banner: consent under the ePrivacy cookie rules is required for storing or accessing information on your device, and this site does neither. You can confirm it in one place — devtools → Application → Storage — where Cookies, Local Storage and Session Storage are empty on every page.
One exception, and it is yours rather than ours: on jobs., voice. and kit. there is an optional live-model mode. If you choose to paste a Google Gemini API key, that key is held in your tab's sessionStorage so a reload does not lose it, and it is gone when you close the tab. The apps say so on screen next to the field. Nobody but you and Google ever sees it — see §6.
Browser signal, honoured automatically. If your browser sends Global Privacy Control (navigator.globalPrivacyControl) or Do Not Track (navigator.doNotTrack === '1'), the module returns before it builds a request. Nothing is sent. This is three lines near the top of src/lib/analytics.ts and it runs before anything else.
Block it. Any content blocker stops us.i.posthog.com. Nothing on any of these sites depends on analytics succeeding; the request is fire-and-forget with a .catch(() => {}) and the page never learns whether it worked.
Verify rather than trust. Open devtools → Network, filter on posthog, and use any demo. You will see at most one small POST per page load plus one per deliberate action, and you can read every byte of the body. Then read src/lib/analytics.ts, which is the entire implementation.
| Processor | PostHog, US Cloud region (us.i.posthog.com) |
| Purpose | Knowing which links and posts brought people to a hackathon submission, and whether they tried a demo or bounced |
| Legal basis | Legitimate interest in understanding traffic to a public demonstration, with no profiling, no device storage, and no persistent identifier |
| Retention | PostHog's project default; we set nothing longer, and there is no export or backup of it anywhere else |
| Where it is processed | The United States. The PostHog project behind these demos is on US Cloud |
One property was added on 2026-09-03. page says which of voice.'s two pages a row came from, and holds one of exactly two authored words: board or resilience. The resilience page is reached by a query parameter rather than a path, so before this the two were indistinguishable. Reporting the query string itself would have answered that by opening a hole for arbitrary parameters; two words cannot.
On that last row, said plainly rather than left in a table. If you are in the EU or the UK, the one thing collected — a page-view row with a path, which of a demo's pages it was, possibly a UTM tag, a referring host, and a per-page-load random number — is processed in the United States. PostHog offers an EU region and moving there is a one-line change in src/lib/analytics.ts; this project is on US Cloud because that is the account these demos were built under, and pretending otherwise in a document about honest data handling would be a strange way to make the point. There is no account, no profile, no persistent identifier and nothing you typed in that row, which is why we were comfortable shipping it — but the residency is a fact you are entitled to have in front of you rather than buried.
What we cannot make disappear, stated rather than glossed over: the POST carries an IP address, as every HTTP request on the internet does, and PostHog may derive a coarse location from it. We do not store the IP ourselves, we do not join it to anything, and there is no persistent identifier for it to be joined to. If that matters to you, §4 tells you how to stop the request.
The analytics is the small part. This is the part worth reading.
All six demos are synthetic. The applicants, employers, postings, cloud estate, incident, research archive, agents and prices are invented. Nothing you do reaches a real system, no account exists, and none of the demos has a backend.
Everything you enter stays in the tab. The job application, the voice form, the change plan, the console's estate — all of it is React state and disappears when you close the page. There is no database.
The one place data genuinely leaves, and it is your choice: jobs., voice. and kit. each offer an optional live-model mode. If you supply your own Gemini API key, then your prompts — and on voice., your microphone audio — go from your browser directly to generativelanguage.googleapis.com under Google's terms, with no server of ours in the path. The scripted mode is the default everywhere and needs no key, no network, and no microphone.
rail. publishes real Ed25519 private keys on purpose. src/data/agents.ts contains genuine private keys for three invented agents in a demo where no money exists, so that the signing side of the protocol can be demonstrated rather than mocked. They protect nothing and are useful to nobody. The file says so, and this is not an accident to report.
No accounts, no sign-up, no email, no payment. Nothing on any of these sites asks you for a real piece of personal information, and the one that looks like it does — a job application form — is the demo arguing that it should not.
These are hackathon demonstrations, not a service. If you want an event removed or have a question about anything above, the repository's issue tracker is the route, and the implementation is src/lib/analytics.ts in any of the five app directories (analytics.js for the ideas hub).