You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Action Mode: show relayed data for direct sharing - #9181
Advanced stats showed current speed, data relayed this session, and per-country data only for Unbounded. Direct sharing (Share My Connection) got "Data stats are available only when sharing via Unbounded." This finishes the work to give direct mode the same stats, across three repos:
Adds those up per session and per IP, and serves them at /peer/snapshot in the shape of unbounded.Snapshot
lantern
this PR
Polls the snapshot, folds it with the existing relay code, and shows the figures
What changes
lantern-core: the 1s Unbounded poller is now a generic pollSnapshots[T]. A second instance polls PeerRelaySnapshot and emits peer-snapshot. It sends no "unavailable" event, because the Unbounded poller already reports when the backend is unreachable.
ShareNotifier:
Each snapshot source has its own _RelayFeed, a counter tracker plus a sample clock. Both sources report every second with separate epochs, and one shared tracker would read each alternation as a restart and reset the counts.
Peer snapshots are folded in every mode, so bytes a session relays as it stops still count. They only drive the speed while direct mode is active.
An idle Unbounded snapshot no longer zeroes direct mode's speed. Only the feed that owns the speed sets it, along with available.
People still come from peer-connection events, so arrivals in peer snapshots are not counted a second time. Bytes go to countries through the same _attributeToCountry path Unbounded uses.
Advanced stats: direct mode shows current speed and data relayed this session. The "only via Unbounded" note and its string are gone, and the translator comments no longer say "over Unbounded".
sequenceDiagram
autonumber
participant SB as samizdat inbound<br/>lantern-box#371
participant P as peer client<br/>radiance#689
participant LC as poller<br/>unbounded_snapshot.go
participant N as ShareNotifier<br/>share_notifier.dart
participant UI as Advanced stats
SB->>P: accept/close with *Counter
loop every 1s
LC->>P: GET /peer/snapshot
LC->>N: peer-snapshot {epoch, bytesDown, peerBytes}
Note over N: _peerFeed.counters.apply → deltas ⚠️
N->>N: session/lifetime += delta,<br/>speed (SmC only), countries += bytes
end
LC->>N: unbounded-snapshot (idle)
Note over N: _unboundedFeed, updateSpeed=false in SmC
N-->>UI: speed, session, all time, countries
Loading
Dependency bumps
lantern-box is pinned to v0.0.166 (the release with getlantern/lantern-box#371), and radiance to 79f0a5bd8, the main merge of getlantern/radiance#688 and #689. The sing-box-minimal replace matches what lantern-box v0.0.166 requires. go mod tidy was run, and go.mod and go.sum are committed together.
Not covered
Nothing has been checked in the running app yet. All verification so far is unit and widget tests.
Testing
SmC relay snapshots drive speed, data, and countries: a direct-mode session's snapshots produce session and lifetime bytes and a speed, and add bytes to the US row without counting the person again. An idle Unbounded snapshot leaves the speed alone.
advanced stats in SmC shows the same figures as WebRTC replaces the old note test.
TestPeerSnapshotsSkipFailedReads covers the generic poller with no unavailable event.
flutter test test/features/action_mode: 79 pass. go test ./lantern-core/: passes.
Pre-PR review (local Codex gate)
Rounds: 2 completed. Round 3 could not run: Codex reported "workspace is out of credits" on the attempt and on its retry. Round 2's fixes below are verified by tests but were not re-reviewed by Codex.
Last completed: CODEX GATE: verdict=findings critical=0 important=0 minor=2 reviewed=21 (round 2; both findings fixed)
Exit: none. The gate did not reach a clean round.
Fixed:
share_notifier.dart:745 (r1, important): direct mode's watermark wasn't persisted, so bytes relayed while the app was closed never reached "all time". peerRelay* is now saved and restored like Unbounded's, with a restart test that fails on the old code.
unbounded_snapshot.go:27 (r2, minor): when the backend went unreachable during direct sharing, the speed stayed at its last value. The outage now zeroes it in direct mode.
share_notifier.dart:605 (r2, minor): a forced save on every idle Unbounded snapshot bypassed the 30s throttle once direct-mode snapshots began marking stats dirty. Now forced only when Unbounded's counters move, with a test that fails on the old code.
The reason will be displayed to describe this comment to others. Learn more.
Partly fixed in 419e0ce: on unbounded-unavailable in direct mode the speed is now zeroed (covered in SmC relay snapshots…), so Advanced stats no longer shows the last figure as current traffic.
I'm deliberately not clearing direct mode's live peers or activeCount. In Unbounded mode clearing is safe, because the next full snapshot rebuilds the peer set when the backend comes back. Direct mode has no such rebuild: its peers come only from peer-connection accept/close events, and the backend keeps serving through a UI-side outage. Clearing would under-count, and drop the globe arcs of, everyone still connected until they reconnect, which is worse than showing the last known count during the outage. Leaving this open in case a human prefers the other trade-off.
Peer poller restores SmC speed during ongoing Unbounded outage
lantern-core/unbounded_snapshot.go:27
The two pollers run independently: an in-flight peer read can succeed after unbounded-unavailable clears SmC speed, then set it above zero again. If subsequent reads fail, Unbounded suppresses repeat outage notifications and the peer poller emits none, so that speed remains displayed throughout the outage. Emit a peer-specific unavailable event and handle it by zeroing SmC speed without clearing its peers. Add a regression test for an Unbounded outage notification followed by a successful peer snapshot and then failed peer reads.
Advanced stats showed speed, session data and per-country bytes only for
Unbounded; direct sharing (SmC) got a note saying data was unavailable.
radiance now serves a direct-sharing relay snapshot at /peer/snapshot in
the shape of Unbounded's, so lantern-core polls it every second as
peer-snapshot and the share notifier folds it with the same relay code.
Each snapshot source gets its own counter tracker and sample clock: both
arrive every second with separate epochs, and a shared tracker would treat
every alternation as a restart. Peer snapshots are folded in every mode, so
bytes a session relays as it stops still count, but drive the speed only
while SmC is active, and an idle Unbounded snapshot no longer zeroes SmC's
speed. People still come from peer-connection events, so the snapshot's
arrivals are not counted again.
Bumps radiance to the snapshot commit (getlantern/radiance#689), with
lantern-box and sing-box-minimal to match.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQG2dRhaocR2UY5RPamm39
Without it, a restarted app's first SmC snapshot only set a baseline, so
bytes relayed while the app was closed never reached the all-time total.
The peer feed now saves and restores its own epoch and counters alongside
Unbounded's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQG2dRhaocR2UY5RPamm39
When the backend is unreachable peer snapshots stop, but only Unbounded's
outage reset the speed, so direct sharing kept showing its last figure.
And the SmC branch of the Unbounded snapshot forced a save every second,
which with SmC snapshots now marking the stats dirty bypassed the 30s
throttle; it now forces only when Unbounded's counters moved.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQG2dRhaocR2UY5RPamm39
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
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.
Why
Advanced stats showed current speed, data relayed this session, and per-country data only for Unbounded. Direct sharing (Share My Connection) got "Data stats are available only when sharing via Unbounded." This finishes the work to give direct mode the same stats, across three repos:
peerconn.Counter/peer/snapshotin the shape ofunbounded.SnapshotWhat changes
pollSnapshots[T]. A second instance pollsPeerRelaySnapshotand emitspeer-snapshot. It sends no "unavailable" event, because the Unbounded poller already reports when the backend is unreachable._RelayFeed, a counter tracker plus a sample clock. Both sources report every second with separate epochs, and one shared tracker would read each alternation as a restart and reset the counts.available.peer-connectionevents, so arrivals in peer snapshots are not counted a second time. Bytes go to countries through the same_attributeToCountrypath Unbounded uses.sequenceDiagram autonumber participant SB as samizdat inbound<br/>lantern-box#371 participant P as peer client<br/>radiance#689 participant LC as poller<br/>unbounded_snapshot.go participant N as ShareNotifier<br/>share_notifier.dart participant UI as Advanced stats SB->>P: accept/close with *Counter loop every 1s LC->>P: GET /peer/snapshot LC->>N: peer-snapshot {epoch, bytesDown, peerBytes} Note over N: _peerFeed.counters.apply → deltas ⚠️ N->>N: session/lifetime += delta,<br/>speed (SmC only), countries += bytes end LC->>N: unbounded-snapshot (idle) Note over N: _unboundedFeed, updateSpeed=false in SmC N-->>UI: speed, session, all time, countriesDependency bumps
lantern-box is pinned to v0.0.166 (the release with getlantern/lantern-box#371), and radiance to
79f0a5bd8, themainmerge of getlantern/radiance#688 and #689. Thesing-box-minimalreplace matches what lantern-box v0.0.166 requires.go mod tidywas run, and go.mod and go.sum are committed together.Not covered
Testing
SmC relay snapshots drive speed, data, and countries: a direct-mode session's snapshots produce session and lifetime bytes and a speed, and add bytes to the US row without counting the person again. An idle Unbounded snapshot leaves the speed alone.advanced stats in SmC shows the same figures as WebRTCreplaces the old note test.TestPeerSnapshotsSkipFailedReadscovers the generic poller with no unavailable event.flutter test test/features/action_mode: 79 pass.go test ./lantern-core/: passes.Pre-PR review (local Codex gate)
CODEX GATE: verdict=findings critical=0 important=0 minor=2 reviewed=21(round 2; both findings fixed)peerRelay*is now saved and restored like Unbounded's, with a restart test that fails on the old code.🤖 Generated with Claude Code
https://claude.ai/code/session_01LQG2dRhaocR2UY5RPamm39
Summary by CodeRabbit