Repository navigation
Daily ping: one anonymous ping per tool per day - #2543
Merged
Merged
Conversation
Sends {"version":"X.Y.Z","age":N} (Fedora countme age bucket) to
ping.plannotator.ai once a day, after a one-time notice (CLI stderr line,
Inbox toast) and never on the notice day. Off with NO_ANON_PING, the shared
do-not-track convention, CI, dev and test builds, config.json telemetry, or
the synced Settings switch. Checks run in the background from the CLI's
sessions, the Inbox (start, hourly), Pi's servers and OpenCode's in-process
servers. Restores apps/telemetry as a cut-down Worker (POST /ping, GET
/totals, D1 daily(day, version, age, n), no IP, logs off). Rewrites the
privacy page and README, and fixes the stale update-check opt-out text.
Owner decisions applied:
- Each tool (inbox, plan, review, annotate, last) sends its own
once-a-day ping: {"version","age_week","tool","first"}. `first` is 1
only on the computer's first ping of the UTC day, so the server
counts daily users without an ID. At most five pings a day.
- On by default with no in-app notice and no wait.
- No Settings switch and no config key: NO_ANON_PING is the only off
switch (the do-not-track convention and CI still turn it off,
quietly). The README is back to main.
- Inbox Settings > About gets a plain "Privacy" part with the
approved text.
- The Worker stores daily(day, version, age_week, tool, n) and
daily_total(day, version, age_week, n).
- Privacy page, env var docs and agent guides updated.
- P1: only a build release CI marks may reach the live host. The binaries are compiled with --define __PLANNOTATOR_RELEASE__=true (release.yml only). OpenCode needs the dist/release.json release CI writes. Pi counts only outside this checkout. A test that spawns the CLI with a release version and a replaced env is a dev build. The test preload and the e2e global setup set NO_ANON_PING=1. Each replaced-env spawn sets it too. tests/daily-ping-live-guard.test.ts guards all three. - P2: GET /totals is served from caches.default for 5 minutes and reads only the last 90 days. - P3: Pi's archive browser sends nothing. age_week counts from the data folder's oldest time. The lock holds a random token: a process whose lock was taken over writes and removes nothing. The Worker refuses a large body unread (Content-Length, capped read). Doc fix. bun.lock is main's plus only the telemetry workspace. The CLI and OpenCode ping calls have loopback tests.
- Pi counts only with the release.json release CI writes beside its package.json (shipped by files) naming the package's own version; a local pack or copied folder is a dev build. - release.yml writes the Pi mark before "Pack @plannotator/pi-extension". - The live guard parses release.yml: every CLI compile defines __PLANNOTATOR_RELEASE__=true, each release.json comes after the build and before its pack, no other workflow marks a build, Pi files ships release.json; scripts and workflows that run under env -i set NO_ANON_PING=1. - smoke-update-prepare.sh smoke_env sets NO_ANON_PING=1. - firstUseWeek uses lstatSync.
# Conflicts: # tests/dom-test-list.txt
…ng off #2547's spec spawns the Inbox with a release version and a replaced env, which drops global-setup's NO_ANON_PING. Set it in the spawned env, as inbox-list-intro.spec.ts does, so daily-ping-live-guard passes.
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.
Owner decisions in this PR
NO_ANON_PINGis the only off switch, and the only variable that user-facing text names. Any non-empty value other than0turns it off. There is no Settings switch and no config key.The Worker is deployed at
ping.plannotator.ai(see below), and the owner has approved the privacy page and release-note wording.What it sends
Once a UTC day, each tool a computer uses sends one ping. The tools are inbox, plan, review, annotate and last.
version: the release (X.Y.Z only).age_week: the install-age bucket, Fedora countme style: 1 the first week, 2 weeks 2-4, 3 weeks 5-24, 4 older.tool: inbox, plan, review, annotate or last.first: 1 only on the computer's first ping of the UTC day from any tool, else 0. The server counts daily users from it, with no ID.A computer sends at most 5 pings a day. Pings carry no ID, no OS and nothing else.
When it checks
last). Archive and goal setup send nothing.last.Every check runs in the background and is never awaited.
Only a release CI marks can send
--define __PLANNOTATOR_RELEASE__=true, in release.yml only.release.json({"version":"X.Y.Z"}) into each package that pings:dist/release.jsonfor OpenCode, andrelease.jsonbeside Pi's package.json (Pi'sfilesships it).bun pm pack, or a copied folder has no mark and is a dev build.Everything else has no live endpoint: a source run, a local build, or a test that spawns the CLI with any
__CLI_VERSION__and a replaced env. On top of that:NO_ANON_PING=1.tests/daily-ping-live-guard.test.tsguards all of it.How a check works
ping.jsonin the data dir. Stop if this tool already sent today.ping.lock(O_EXCL, holds a random token, waits up to 5 s, stale after 60 s), then read the file again. A new file'sfirstWeekis the week of the oldest time in the data folder, so an existing user's first ping isn't counted as a new install.Nothing is written while the ping is off.
Worker (apps/telemetry, deployed)
POST /pingvalidates the exact four keys and upsertsdaily(day, version, age_week, tool, n). Whenfirstis 1 it also upsertsdaily_total(day, version, age_week, n). Both upserts usen = n + 1. Returns 204.GET /totalsreturns public JSON of the last 90 days' sums: users, per age bucket, per version, per tool. It is served fromcaches.defaultfor 5 minutes, so repeat GETs don't touch D1.ping.plannotator.ai(workers_dev = false).database_idis the deployed D1 databaseplannotator-ping(not a secret). The deploy steps are inapps/telemetry/README.md.Inbox Settings > About
A plain "Privacy" part below the version and updates, with the owner-approved text. It shows on the computer's own page and on a tailnet guest's page.
Tests
All tests use a temp data dir and a loopback receiver only.
packages/shared/daily-ping.test.ts:firstonly on the day's first ping;first.tests/daily-ping-live-guard.test.ts: a child with a release version and a bare env has no live endpoint; no test makes a marked build; every test that spawns with a release version turns the ping off or sandboxes it; every script or workflow that runs underenv -isetsNO_ANON_PING=1; the preload sets the off switch. It also parses release.yml: every CLI compile defines the mark, eachrelease.jsonis written after the build and before its pack, no other workflow marks a build, and Pi'sfilesshipsrelease.json.apps/hook/server/daily-ping-cli.test.ts:plannotator annotateas a real process sends its ping to a loopback receiver.apps/opencode-plugin/daily-ping.test.ts: plan, review, annotate and last each send their ping; a local build is dev.packages/server/inbox-daily-ping.test.ts: an Inbox start sendstool: "inbox", and NO_ANON_PING and DO_NOT_TRACK stop it.apps/pi-extension/daily-ping.test.ts: Pi parity for all four tools; a checkout run and an installed package without a matchingrelease.jsoncount as dev builds; the off switches work.apps/telemetry/test/handler.test.ts: exact keys, both upserts, no IP, header or cf data reaching D1 (fake D1), and the /totals shape.packages/inbox/aboutPrivacy.test.tsx(DOM): Privacy shows on both pages, with the env var as code and the link opening in a new tab.46 guarded behaviors were each reverted on their own, and the matching test failed every time.