Privacy
What this site records, what it deliberately does not, and how retention and deletion actually work.
This page is about dev3d.net — this website and the plugin marketplace it serves — and not about the dev3d software you install on your own machine. The software you run reports nothing to this site: it has no address for it and no code that would contact it. What follows is a complete account of what this site stores in its database, why each field exists, how long it lives, and which parts of the answer are weaker than a privacy page usually admits.
The short version
- Two things measure traffic here, and only one of them is ours. A first-party beacon runs always, because it sends nothing to anybody else. A Matomo Tag Manager container hosted by a third party loads only if you press Allow. Until you do, no request leaves your browser for that host, and the rest of the list below describes the state you are in.
- No IP address is stored by our own analytics — neither in the page-view table nor in the download table. An address is hashed with a secret salt that rotates daily, so distinct visitors can be counted within a day and nothing links a visitor across days.
- A visit is grouped by a random cookie value, not by a fingerprint. It is one random token in an HTTP-only first-party cookie, and it identifies a browser, not a person.
- The user agent is bucketed at write time into a device and a browser. The raw string is stored on the row in truncated form, and no query on this site ever reads it — every chart reads the buckets.
- No country is recorded. The column exists and is honestly null, because a geo-IP lookup would mean shipping the visitor's address to somebody else.
- The console and file requests are never recorded, so operator activity and asset loads do not inflate the numbers.
- Retention is a button, not a schedule. This site has no background worker; the 90-day window is enforced when an operator presses prune.
- If you allow the tag manager, the honest answer changes, and the section below says exactly how rather than burying it.
The page-view beacon
Traffic figures come from one small first-party component and one endpoint. The component fires once per client-side navigation — so a route change is counted the way a full page load is — and posts exactly two things to /api/analytics/collect:
{
"path": "/docs/concepts/model-routing",
"referrer": "https://news.ycombinator.com/"
}
That is the entire payload. It is sent with navigator.sendBeacon, which survives the page being closed mid-flight, or a plain fetch with keepalive where a beacon is unavailable. The only other data involved arrives in the request itself: the User-Agent header, the Host header, and whatever the proxy put in x-forwarded-for. Everything the endpoint does with those is described in the next two sections.
Four deliberate limits on that endpoint:
- The path is truncated to 400 characters and rejected unless it starts with
/. The referrer is truncated to 500. - Anything under
/adminis dropped before a row is written. The console is not public traffic and a signed-in operator moving between screens would swamp the figures. - Anything that looks like a file request is dropped. A path ending in a short extension — an image, a font, a stylesheet — returns
{ "ok": true, "skipped": true }and stores nothing. - A failed beacon is invisible. The endpoint swallows its own errors and answers
200, so a blocked or broken request never surfaces to a visitor.
If JavaScript does not run on your visit — script disabled, a content blocker with the endpoint on its list, or a text-mode client — then nothing at all is recorded. There is no server-side fallback that logs a page view when the beacon does not arrive. The application also writes no access log of its own — there is no logging middleware — though a web server or hosting provider in front of the application may keep access logs, which is outside this code and outside what this page can promise about.
What is recorded, field by field
The table below is the whole of it. Anything not in this table is not stored.
| What | Why it is there | Retention |
|---|---|---|
path |
To know which documentation is read — the top-pages list and the traffic chart | Inside the retention window, then pruned by hand |
referrer and referrerKind |
To know what sent someone here. The kind is one of search, social, internal, direct or other, computed at write time |
The raw string and the bucket share the row's lifetime. The console lists referrer hosts only — the raw string never leaves the database in a rendered list. |
host |
To tell the site's own hostnames apart | Retention window |
visitorHash |
A truncated salted hash of the address and user agent, recomputed with a per-day salt. It is what "distinct visitors" counts. | Rotates at midnight: yesterday's hash cannot be joined to today's, and the hash cannot be turned back into an address in any case |
sessionKey |
The value of the dev3dnet_vid cookie — a random 16-byte token, created on first sight. It groups one browser's views into a visit rather than identifying a person. |
One year for the cookie; the row lives inside the retention window |
device and browser |
Bucketed at write time from the user agent: device is tablet, mobile, bot or desktop; browser is Edge, Opera, Firefox, Chrome, Safari or Other. These are what the charts read. |
Retention window |
userAgent |
Stored truncated to 400 characters on the row, and never selected by any analytics query on this site. See the honest note below. | Retention window |
country |
Always null. The column exists because the schema was written with it, and it is populated with nothing because a geo-IP lookup would mean sending the visitor's address to a third party. A rough country is not worth that. | n/a |
userId |
Set only when you are signed in, so a signed-in session can be told apart from anonymous traffic. Nulled if the account is deleted. | Retention window, then pruned; the account link is severed on deletion |
The raw user agent, stated plainly
The honest version is more specific than "we do not keep your user agent". The truncated user-agent string is on the row, because it arrives with the request and the endpoint stores the row in one insert. What is true — and what the design comment in the endpoint means — is that it is not retained for analysis: every analytics query on this site reads the device and browser columns, and none of them selects user_agent. The console's browser and device panels are built from the buckets, not from the string. So the string is present but unused, and it disappears with the row when the retention window is pruned.
The visitor hash is truncated to 32 hexadecimal characters. That is a deliberate reduction in precision on top of the daily salt: it is enough to count distinct visitors and not enough to be a durable identifier, even for someone who later obtains the database and the salt.
Plugin downloads
When a dev3d host fetches a bundle from /marketplace/bundles/…, the download is recorded so a publisher can see which dev3d versions their plugin is installed on. The row stores:
- The plugin and version — which bundle was fetched.
- The dev3d version and the operating system, parsed out of the user agent at write time. A host identifies itself as
dev3d/1.2.0, and the operating system is classified asWindows,macOS,Linuxor null. The charts read these two parsed fields, not the string. - The truncated user agent, up to 400 characters.
ipHash— the same treatment as a page view: a salted, truncated hash with a daily-rotating salt, so unique installers can be counted without an address ever being stored.- The referrer, truncated to 500 characters.
Two things are worth noting. First, attribution is explicitly best-effort: a failure to record a download is logged and the download still completes, so a publisher's numbers under-count rather than over-count. Second, there is no prune path for download rows at all — the retention button deletes page views and nothing else. That is a real gap, it is stated here rather than hidden, and it is the kind of thing this documentation exists to say out loud.
If you use the contact form
The contact form stores your name, email, company (optional), a topic, your message, and a spam-triage hash of the client address. That hash is a daily-rotating SHA-256 of the date and the address — enough to recognise the same sender across submissions on the same day, not enough to recover an address. The message itself has no retention schedule; deleting it is a manual action by an operator. Like the download rows, it is not covered by the analytics prune button.
If you sign in — and the one place an address is stored
Signing in creates rows that a page view does not. This section exists because a privacy page that only described anonymous traffic would be misleading.
- Account fields. Email, display name, optional handle, title, bio, avatar URL, a status, and a flag for whether the profile is public. Your handle and profile are separate switches: a handle alone does not publish a profile.
- Password. Never the password. A scrypt hash encoded as
scrypt$N$r$p$salt$hash, with a fresh 16-byte salt per account. It cannot be reversed, and it is not comparable between accounts. - Sessions. One row per signed-in session, holding a SHA-256 of the session cookie value — the cookie itself is never stored — its expiry, and the client address and the user agent of the session. This is the one place on this site where a raw address is kept, and it is kept so that the account page can show you where you are signed in and let you revoke a session you do not recognise. The session cookie
dev3dnet_sessionlasts 30 days; changing your password revokes every other session. - API tokens. For publishing from CI. Only a SHA-256 of the token is stored, with a short readable prefix kept so you can identify it in a list. The token itself is shown once, at creation.
- Audit entries. Administrative actions record the actor, the action and the target, and copy the actor's email in so the entry still reads correctly if the account is later deleted. Audit rows also carry the address of the request that made the change. The audit log is the answer to "who changed that", which is why it is deliberately more durable than the account it refers to.
What is deliberately not collected
Every claim below describes the site before you allow the tag manager, which is the state every visitor starts in. The one place the answer changes is called out immediately after, because a list that quietly stopped being true would be worse than no list.
- No advertising pixel, and no social embed that calls home. No share button here loads a third party's widget; sharing is a plain link.
- No cross-site identifier. Our own cookies are first-party and
httpOnly, so page scripts cannot read them. Nothing here follows you to another site, and nothing here receives a signal from one. - No fingerprinting. The visit identifier is a random token the server generated, not a digest of your screen size, fonts or canvas output.
- No IP address in the analytics or marketplace tables — only the daily hash, described above.
- No country, no city, no coordinates. The column is null by choice.
- No keystroke, scroll, mouse or session-replay recording. There is no code for any of it on this site.
- No email open or click tracking. The footer newsletter stores an address, a source label and a confirmation flag — and nothing about whether you opened a message, because nothing here sends tracking pixels.
- No prompts, workspaces or run data. This site has never seen them. They live on your machine, in the dev3d installation you run.
The Matomo tag manager, and what changes if you allow it
This section exists because it is the one part of this page that is not a clean answer.
The site loads a Matomo Tag Manager container from analytics.thecorehosting.net. It is a third-party script served by a host we do not operate, and it is not loaded unless you press Allow analytics in the banner. Declining is remembered for 180 days, in a first-party cookie, and you can change your mind at any time from Privacy choices in the footer.
| If you decline, or have not answered | If you allow | |
|---|---|---|
| Requests to a third-party host | None. No request to analytics.thecorehosting.net is made at all. |
The container script is fetched, and the tags inside it decide what it sends afterwards. |
| Your IP address | Not stored by our analytics, and not sent anywhere. | Visible to that host, as it is to any server your browser requests a file from. We cannot hash it away, because the request is not ours to make. |
| Cookies | Two first-party cookies only, both described on this page. | Matomo sets its own cookies. They are script-readable, unlike ours, and their names and lifetimes are set by the container, not by this site. |
| What is recorded there | Nothing. | Determined by the tags configured in that container. This page cannot enumerate them for you, and a promise that it could would be a promise we cannot keep — the container is edited outside this repository. Treat it as: pages you visit here, plus what Matomo normally records. |
| Withdrawing | — | Footer → Privacy choices → No thanks. The container stops loading on your next page load, and the request is not made again. |
Withdrawing stops future collection; it does not erase what was already sent. Removing data already recorded on that host is a request to the operator of that Matomo installation, not something this site can do for you. If that matters to you, decline — and note that declining is the default until you answer.
Two consequences worth stating plainly. First, a container is code we do not control: a tag added to it later is not reviewed here, which is the reason for a consent gate rather than an unconditional script. Second, with the tag manager allowed, the honest summary of this site's analytics is no longer "first-party only", and everything above that says so is scoped to the declined state.
Retention is a button, not a schedule
The site enforces a retention window for page views, and the way it enforces it is worth describing accurately because it is unusual.
- The window is 90 days by default. It is a site setting (
site.analytics_retention_days) and can be set anywhere from 1 to 3650 days in the console. - Pruning is manual. There is a button on the console's settings screen. Pressing it counts what is outside the window first, and if nothing is outside it, it says so and deletes nothing. Otherwise it deletes those rows, reports how many, and writes an
analytics.prunedentry to the audit log with the count and the window used. - There is no cron job, no scheduled task and no background worker. This is a deliberate trade, and the reason is in the code: a retention policy that silently did not run would be worse than one an operator has to run deliberately. A window that only shrinks when someone presses a button is honest about what it is; a window that quietly stopped being enforced is a lie with a timestamp.
- What this means in practice: if nobody presses the button, page views live longer than 90 days. The site does not claim otherwise, and this page is the place where that is written down.
- What is not covered at all: plugin download rows, contact inquiries and audit entries have no prune path. Only page views do.
Your choices
| If you want to… | Do this | What happens |
|---|---|---|
| Refuse, or withdraw, the third-party tag manager | Press No thanks in the banner, or Privacy choices in the footer | No request is made to that host, now or on later page loads. The decision is remembered for 180 days in the dev3dnet_consent cookie, and it is the default until you answer. Withdrawing does not erase what was already sent |
| Not be counted at all | Block /api/analytics/collect, or browse with JavaScript disabled |
No row is written, because the beacon never arrives and there is no server-side fallback |
| Break the link between visits | Clear cookies for this site | You get a new random visitor token on the next page view, and the consent banner is shown again because the decision is cleared with it |
| See or end your sessions | Open your account page | The list shows each session's last-seen time, its user agent and its address; you can revoke one, or all of them by changing your password |
| Stop appearing in a public profile | Turn the profile switch off in your account | The profile stops being public. A handle alone never published one. |
| Have your account deleted | Ask — there is no self-serve delete button | See the next section for exactly what goes and what stays |
| Have an inquiry removed | Ask and quote the message | An operator deletes the row; there is no automated path |
Deleting an account: what it removes, and what it does not
Account deletion is an administrative action, and it is a database delete with cascades and null-outs — which is precise, and worth spelling out rather than summarising as "we delete your data".
Removed outright: the account row; every session, so every signed-in browser is signed out; every API token; every role grant.
Retained, with the link to you severed:
- Documentation revisions you authored. The revision keeps its content, its note and its timestamp; its editor reference becomes null. An edit history that silently loses its authors is still an edit history, but one that has been quietly rewritten — so the edit stays and the attribution link goes.
- Page views and downloads that were attributed to you. The
user_idcolumn is nulled and the row continues to age out of the retention window like any other. - Plugin listings you published. The listing is not removed, because other people may depend on it and the catalog serves it. Its owner reference and the "published by" attribution become null, so the listing survives without naming you. If you want the listing taken down as well, that is a separate request.
- Audit entries. The actor reference is nulled, but the actor's email was copied into the entry when it was written, so the log still records who did what. That is deliberate: an audit log that forgets its actors on request is not an audit log.
Never possible: deleting the owner account, and deleting the account you are currently signed in with. Both are refused, the second with an explicit message.
What this page does not claim
Three limits, because a privacy page that only lists strengths is not worth reading:
- This is not anonymity in a strong sense. Within a single day, the same browser at the same address with the same user agent produces the same visitor hash, which is exactly the point — it is how visitors are counted. The protection is that the hash does not survive midnight and cannot be reversed.
- Infrastructure logs are outside this code. Whatever a reverse proxy, CDN or hosting provider in front of the application records is not described here, because this application does not control it and cannot vouch for it.
- Nothing here describes the dev3d software's own network behaviour. A dev3d installation talks to the model providers you configure, and to OpenRouter for benchmark data and a pooled quality index unless you turn those off. None of that traffic comes to this site. For that side of the picture, read the environment reference, which documents every variable that governs it.
Related reading: the terms, the marketplace policy, and — if you want to check any of the above against the code — src/lib/analytics.ts, src/app/api/analytics/collect/route.ts, src/lib/crypto.ts and src/db/schema.ts.
Linked from
Did this page answer your question?