The address card shows the hello link (SPEC 6.34) with Copy and
Share, says what it does, and its QR code holds it.
flamenetmessenger://hi#<address> opens the app and fills in Home's add
screen; nothing is looked up or sent until the person chooses to. Both
scanners read a hello link, an older card or a bare address, and say
so when a code holds none. Parsing follows DESIGN 21: the known link
forms only, the fragment percent-decoded once, fnid: and 52 letters of
the FNID alphabet, a lowercased host with a dot.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Headlines, Markets ("Prices are delayed. Not advice."), Weather for
the cities picked on the phone, Site of the day with the file's own
note, the blog when there is a post, and the way into the directory.
The tab asks the relay when it opens and when the app comes back to
it; offline it shows the cached copy with "From <date>", and a 404
shows only the directory and "Today's page is not offered by this
relay." Links open in the browser on a tap, with the host shown.
The dev key (kid a9a94e74) is compiled into debug builds only, from
src/debug; release has its own empty copy, and TodayKeys.PRODUCTION is
empty until the release step. Eight tabs fit a 360dp phone with the
labels one size smaller.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
directory-v1.json (DESIGN 5) ships as an asset: 2,964 sites in 90
categories, browsed by category and searched on the phone with no
network. A site sent to a chat is an ordinary text through the 1:1
send path, the URL as the words and an lnk built from the entry: the
title, and desc present and empty (DESIGN 18). Nothing is fetched to
make it, and with link previews off the URL goes alone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TodayFile checks a Today envelope (SPEC 8.11) before reading a field
of it: size, version, a pinned kid, the Ed25519 signature over the body
bytes, then a body with a real date and a made time. Lists are cut to
their bounds, strings are cut on character boundaries, and a link that
is not http or https drops its entry.
TodayFeed asks the home relay for /today.json through the default
client, so a bridge carries it: no Authorization, cookie, id or query,
and If-None-Match only when it is the hash of the copy held (DESIGN
19). At most once an hour from the relay's last answer, five minutes
after a failed connection. Redirects are not followed. The last good
envelope is kept on disk and re-verified when read; a 404 clears it,
and an older signed file never replaces a newer one.
The signed sample flamenet.io's builder made is a test fixture, and
LiveTodayTest checks a relay on this machine when FN_LIVE_TODAY_RELAY
names one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Scanning a code on Home filled the add field with the literal text
${card.fnid}@${card.relay}: the line in 130edd7 was written with ${'$'}
escapes, so the template was never evaluated. Every scan on Home gave
the same unusable string.
homePrefill() turns a scanned code into the address, bound by the fnid
as the audit asked, and Home uses it. The test pins both the address
and the absence of the escaped template in MainActivity.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The messenger repositories move from gitlab.com/paulhitt to
gitlab.com/extant2000. Old web and git URLs redirect; the relay's container
image path does not, so docker-compose.yml names the new registry path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Peer's autoreply loop had no catch, so one failed poll or send to the relay
ended the process. The on-device reply case then waited out its 90 seconds
for an echo from a peer that was gone, which fits the one missed echo seen
in five full runs on 2026-09-28 (its log was lost, so not proven).
Each poll and each echo is now caught, logged to stderr with a timestamp
("poll failed, still answering: ..."), and the loop carries on. The
closing JSON adds an `errors` count. instrumented.sh already prints the
echo peer's log when a run fails, so a recurrence will say why.
Checked against a relay address that refuses connections: the old peer
exits 1 on the first poll with no result; the new one runs its full 8 s,
logs 4 failed polls and prints {"echoed":0,"errors":4}. Against relay1: 0
errors. Full on-device suite with the new peer: 10 of 10.
Harness only: interop/ is not shipped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The chat keeps its newest message in view when it opens and while the
keyboard slides, and both did it with `listState.scrollToItem`, which forces
a layout on the spot. This screen is composed inside the Scaffold's measure
pass, so when the effect ran straight away it asked for a layout in the
middle of one:
IllegalArgumentException: performMeasureAndLayout called during measure layout
at LayoutNode.forceRemeasure
at LazyListState.snapToItemIndexInternal
at ChatScreens.kt:1740 (the keyboard follow)
... LaunchedEffectImpl.onRemembered ... SubcomposeLayout ... Scaffold
It failed ChatFlowTest's send in every full on-device run since 2026-09-18.
The frames that ran the effect inline are the Compose test dispatcher's; the
shipped app posts effects to the next frame, so this has not been seen to
crash a real device. Forcing a synchronous layout from an effect is wrong
either way.
Both now use `requestScrollToItem` (foundation 1.7.6), which the next layout
applies, and the keyboard is what brings that layout. Still instant, still
not animated. The animated follow for new messages is unchanged: it waits
for animation frames before it moves.
TheKeyboardDoesNotHideTheMessagesTest read the source for
`listState.scrollToItem(`; it now requires `requestScrollToItem(` and refuses
`scrollToItem(` in the keyboard effect. Put the old line back and it fails.
Result on relay1: the full on-device suite passes, 10 of 10, and the crash
buffer has no performMeasureAndLayout. App unit tests: 358 debug, 323
release, all green.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
aGroupIsCreatedAndListed waited for a row matching `group-\d+`. The relay
mints group ids with randomHexToken(), so the tag is `group-` plus 64 hex
characters, and the pattern only matched when the id happened to start
with a digit: ten times in sixteen. The rest of the time the test failed
with "the group never appeared in the list after Create" while the dump
showed the group in the list ("Test Crew, peer96035270, You", tag
group-eea4ceeb...).
Now `group-[0-9a-f]{16,}`, which matches that tag and still not
`group-name` or `group-member-N`. Checked both patterns against the tag
from the failing run: old no match, new match. On device: passes.
The keyboard was ruled out first: with it fully open (top at y=1517) the
member row and Create sat at y=1201-1377, above it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The suites added `peerjvmone`, one standing account on relay1 whose only
device came online when somebody ran the interop peer by hand. Nobody did.
On relay1 it had no one-time prekeys left, classical or PQ, and 114
messages it would never collect, and every run added more strangers to it.
Adding it and delivering to it failed in some runs and not others.
scripts/instrumented.sh now creates two accounts with the interop peer
before the run and passes them as `contactPeer` and `echoPeer`: one for the
add-and-send cases, one running `autoreply` for the reply case alone, since
an autoreply peer answers the first new account that writes to it. The
peer runs from `:interop:installDist` with plain java, not a second Gradle
build, and the exit trap stops it and waits for it.
The suites no longer fall back to peerjvmone; without the arguments they
say to use the script. The reply case now fails when no echo arrives
instead of logging a warning and passing. FeatureUseTest waited for the
confirm button, which is on screen but disabled until the lookup resolves,
so it tapped a disabled button; it now waits for the lookup result like
the other two suites.
On relay1: 8 of 10. The reply round trip passes. Still red: the group case
("the group never appeared in the list after Create", not yet diagnosed)
and ChatFlowTest's send, which crashes in the app with "performMeasureAndLayout
called during measure layout" at ChatScreens.kt:1740, the keyboard-follow
scroll recorded on 2026-09-18.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One change since 0.30, android!154: the unsealed-send and changed-address
notices. versionCode and versionName move together, as the comment above
them requires.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The on-device sign-up helpers typed the relay and then tapped the
toggle-register button. Typing raises the keyboard, which slides in after
performScrollTo has measured the screen, so the tap landed on the keyboard
and the form stayed on sign-in. The failure read as a missing email field,
and was taken for a race with the relay probe.
Logged on the emulator: after the scroll the toggle sat at y=1878 while the
content ended at y=1517, the top of the keyboard. Every failure dump already
said "This server keeps its own accounts.", so the probe had answered.
Now the toggle is tapped first, while nothing is moving, and the email field
is waited for because the probe decides whether this relay asks for one.
Same change in FeatureUse, CrudSurface, ChatFlow, CrossPlatform and
LiveSignUpFleet. On relay1, every sign-up in the suite now gets through;
the remaining red is the shared peerjvmone fixture, not sign-up.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>