Conversation
… by every Worker A new dormouse_metrics_daily table holds (day, event, label, count) and nothing else; labels come from fixed allowlists, unknown ones count as other. Logins by method and new accounts, checkouts by plan and ref (the ref carried to the webhook's completion), ended subscriptions as refunded or canceled, voice outcomes and the day's cap, enrollments, Pocket sign-ins, push deliveries, and Hosted page visits by ref. Every write rides waitUntil and only logs a failure. The admin's /api/admin/metrics reads them, gated on the verified ADMIN_EMAIL. The app's links to the plans each carry an allowlisted ref. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Hosted page takes the ref off the address bar, forwards it on the cohort read it already makes (counted as a visit, unknown refs as other) and, when allowlisted, on its checkout links; the account's checkout page keeps it with the pending plan and sends it with the checkout it starts. The homepage, the Pocket playground, and both READMEs name their links; public-docs-lint accepts a link to a page that carries a query. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Founding seats per cohort, the funnel by ref (Hosted page visits, checkouts started, completed) and by plan (with refunds and cancellations), and every event's last 30 days as CSS bars with its all-time total: plain tables on the product theme tokens, no chart library. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…person analytics hosted.md gains "Metrics": the events and their labels, no identifier in a row, unknown refs as other, best-effort writes, rows kept indefinitely, and the admin-only view. pricing.md: the Hosted page forwards a visit's ref and nothing else. security-hosted.md: an audited FAIL IF on what a metrics row may hold and who the admin view answers. The privacy policy says what is counted and that no count records a person. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e session read The labels and the admin route live in metric-labels.ts, which the admin view imports instead of copying; login detection moves beside the login gate and shares its get-session read; the account checkout page passes its ref through App instead of a module variable; the Hosted page sends the visit's ref as-is (the server counts unknown ones as other) and the cards take it as a prop; countBilling counts inline through planOfPrice; every ref link uses HOSTED_REF_PARAM. The voice test counts the metrics' own waitUntil calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…erver-dated admin view An unknown visit ref reaches checkout as other, so a visit and its checkout count under one label; an expired incomplete subscription is no longer read as a refund; the admin view's days come from the Worker's UTC date and /admin/metrics/ resolves; the cron sweeps refs beside the resync; the spec names METRIC_LABELS' owners instead of their values; security.md lists the unauthenticated visit count as a known gap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deploying mouseterm with
|
| Latest commit: |
96441f1
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://620b4fe1.mouseterm.pages.dev |
| Branch Preview URL: | https://hosted-metrics.mouseterm.pages.dev |
nedtwigg
requested a deployment
to
hosted-preview
October 4, 2026 08:44 — with
GitHub Actions
Waiting
dormouse-bot
reviewed
Oct 4, 2026
dormouse-bot
left a comment
Collaborator
There was a problem hiding this comment.
The Privacy page's new sentence leaves out some of the counts this PR adds; the suggestion is inline.
| { id: "use", title: "How we use it", body: <> | ||
| <p>We use this information to create and recognize your account, verify sign-in, connect methods you explicitly select, send requested sign-in codes, prevent abuse, troubleshoot failures, and answer support requests. We discard provider access, refresh, and identity tokens after identity verification rather than storing them in your account.</p> | ||
| <p>We do not sell Hosted account information, use it for targeted advertising, or use it to train general-purpose AI models. Signing in does not subscribe you to a newsletter. The account site uses necessary authentication and security cookies and does not load marketing analytics.</p> | ||
| <p>We measure use of the service only as aggregate daily counts: sign-ins by method, checkouts and subscriptions by plan, spoken alarms and push notifications sent, and which Dormouse link a visit to the Hosted page came from, which those links name in their address. No count records an account, email address, IP address, or browser information, so there are no per-person analytics, and we keep the daily counts indefinitely.</p> |
Collaborator
There was a problem hiding this comment.
This sentence says counts are kept only for the listed things, but the PR also counts account.created, enroll.approved, and burrow.enrolled (METRIC_LABELS in hosted/server/metric-labels.ts). None of them identifies anyone, but a privacy page that says "only" should name every kind of count.
Suggested change
| <p>We measure use of the service only as aggregate daily counts: sign-ins by method, checkouts and subscriptions by plan, spoken alarms and push notifications sent, and which Dormouse link a visit to the Hosted page came from, which those links name in their address. No count records an account, email address, IP address, or browser information, so there are no per-person analytics, and we keep the daily counts indefinitely.</p> | |
| <p>We measure use of the service only as aggregate daily counts: new accounts, sign-ins by method, computers signed in to Hosted, checkouts and subscriptions by plan, spoken alarms and push notifications sent, and which Dormouse link a visit to the Hosted page came from, which those links name in their address. No count records an account, email address, IP address, or browser information, so there are no per-person analytics, and we keep the daily counts indefinitely.</p> |
3 of 5 tasks
This branch is waiting to be deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #1001. Basic launch analytics: first-party, server-side, and aggregate only. The desktop sends no telemetry, and no page loads an analytics script or sets a cookie.
Counters
Migration
007_metrics.sqladdsdormouse_metrics_daily (day, event, label, count). CHECK constraints reject any label shaped like an email, IP or id, and labels come from fixed allowlists. A count is an upsert that runs after the response, throughwaitUntil; a failure is only logged.loginaccount.createdcheckout.startedplan:refcheckout.completedplan:refsubscription.canceledsubscription.refundedREFUND_DAYSof startingenroll.approved,burrow.enrolledvoice.speakvoice.fallback-cappush.sent,pocket.signinhosted_page.refWhere buyers come from
HOSTED_REFScoverssettings-voice,settings-push,settings-remote,upsell-voice,upsell-push,tutorial,home,pocket-playgroundandreadme.reffrom the address bar, sends it on the cohort read it already makes (which counts the visit), and puts it on its checkout links.dormouse_checkout_refs).other./admin/metricsAn
ADMIN_EMAIL-only page; everyone else gets 404. It shows founding seats left, funnels by ref and by plan, and 30-day bars per event. Plain CSS, no chart library.Specs
hosted.md: a new "Metrics" section.pricing.md: the ref-forwarding rule.security-hosted.md: a FAIL IF that metrics rows hold no identifier, and on the admin gate.security.md: a known gap — visit counts are unauthenticated and can be inflated, and each cohort read now costs one database write.Decisions to check
tutorialis in the allowlist, but no tutorial link to/hosteduses it.account.createdis a heuristic and costs one extraget-sessioncall per login. A pgstenciluser.createhook would be cleaner.planOfand the refund rule.Private (ediso, not committed)
dormouse-private/scripts/launch-stats.mjstakes a daily snapshot of GitHub stars, forks and release downloads, Marketplace installs, and Open VSX downloads, appended to a CSV. WithDORMOUSE_HOSTED_COOKIEset, it also pulls/api/admin/metrics.Test plan
pnpm test: lib 5677, standalone 280, website 349, vscode-ext 191tscfor lib, standalone, hosted and website/admin/metricsend to end on StripeDev in the dev loop🤖 Generated with Claude Code