Files
Paul HittandClaude Opus 5 df6248a9b2 The relay can tell which builds are still polling
The plan for 8.9c says to wait until the old clients are gone before dropping
the contact graph, and to read that from what is still polling. The relay could
not read it. iOS sent a hardcoded 1.0 that never changed and Android sent no
User-Agent at all, so the step everything else depends on rested on a
capability that did not exist.

One nullable column on devices, overwritten, no history. The marketing version
and nothing else: no build number, no device model, no OS. Parsed narrowly,
dotted digits up to sixteen characters, because a User-Agent is
attacker-controlled and this is read on the busiest authenticated path here.
Written on the hourly liveness update, plus once out of turn when the version
changes, since waiting an hour per device to notice an upgrade makes the answer
stale exactly when somebody is watching it.

Scripts/client-versions.sh is the operator's side of it, and says plainly that
a device reporting nothing counts as old, because that is the case that cannot
be ruled out.

Disclosed on /metadata rather than added quietly. The row says what it holds,
what it deliberately does not, and why the trade is worth taking: keeping one
version string is what allows the contact graph to go.

The migration goes BEFORE BackfillKeyLog, which loads Device through the model.
Fluent names every column in its SELECT, so a column added after a
model-reading migration fails with "no such column" on a fresh database. The
tests caught it. AddUserSuspendedAt was caught by the same trap.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 18:14:01 -04:00

37 lines
1.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# Which builds are still polling this relay.
#
# The question this answers: is every client new enough that a server-side
# feature can be removed. It is the check that has to pass before
# FN_ROSTER_ON_SERVER goes false, and again before DropBuddies is registered,
# because a client older than the roster guard reads its contact list from the
# relay and turning that off empties it.
#
# Scripts/client-versions.sh what is polling, last 30 days
# Scripts/client-versions.sh 7 a shorter window
#
# A device that has not polled inside the window is not counted. It may still
# exist; it is not currently reading anything, which is the thing being asked.
set -euo pipefail
days="${1:-30}"
container="${FN_DB_CONTAINER:-messenger-db-1}"
db="${FN_DB_NAME:-flamenet}"
user="${FN_DB_USER:-flamenet}"
docker exec "$container" psql -U "$user" -d "$db" -v ON_ERROR_STOP=1 -c "
select
coalesce(client_version, '(never reported)') as build,
count(*) as devices,
max(last_seen)::date as newest_poll
from devices
where last_seen > now() - interval '$days days'
group by 1
order by 1;
"
echo
echo "A build of '(never reported)' is a client older than the one that sends"
echo "its version, or a relay that has not seen it poll since this column was"
echo "added. Treat it as OLD: it is exactly the case that cannot be ruled out."