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>
37 lines
1.3 KiB
Bash
Executable File
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."
|