Skip to content

Action Mode: show relayed data for direct sharing - #9181

Merged
myleshorton merged 4 commits into
mainfrom
fisk/action-mode-direct-bytes
Oct 19, 2026
Merged

myleshorton merged 4 commits into
mainfrom
fisk/action-mode-direct-bytes

Conversation

@myleshorton

@myleshorton myleshorton commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

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:

Repo PR What
lantern-box getlantern/lantern-box#371 Counts each samizdat connection's bytes and hands the listener a peerconn.Counter
radiance getlantern/radiance#689 (on #688) 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.
    • Re-pinned radiance to model.setupComplete not set correctly #689's reviewed head (8c3aa4a).
  • Dismissed: none

🤖 Generated with Claude Code

https://claude.ai/code/session_01LQG2dRhaocR2UY5RPamm39

Summary by CodeRabbit

  • New Features
    • Advanced statistics now show current speed, session totals, and lifetime data in both sharing modes.
    • Direct peer-sharing traffic now contributes to relay totals and country statistics, with saved progress retained across sessions.
  • Bug Fixes
    • Statistics now remain accurate when switching sharing modes, resuming a session, or when relay data is temporarily unavailable.

Copilot AI balanced review requested due to automatic review settings October 19, 2026 18:45
@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0b9eb0fc-37a5-4496-a9c4-a94a95a7dfb1
📥 Commits

Reviewing files that changed from the base of the PR and between 79dc356 and 9e52ded.

⛔ Files ignored due to path filters: 1
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing: 11
  • assets/locales/en.po
  • go.mod
  • lantern-core/core.go
  • lantern-core/unbounded_snapshot.go
  • lantern-core/unbounded_snapshot_test.go
  • lib/core/models/app_setting.dart
  • lib/features/action_mode/advanced_stats_sheet.dart
  • lib/features/action_mode/provider/share_notifier.dart
  • lib/features/home/provider/app_setting_notifier.dart
  • test/features/action_mode/action_mode_widgets_test.dart
  • test/features/action_mode/snapshot_test.dart
+----------------------------+
| Try...catch me if you can. |
+----------------------------+
  \
   \     /)/)
        ( '')
        /  |
      o(___)
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Direct-mode outages leave stale speeds, and idle Unbounded snapshots bypass persistence throttling.

2 open issues
What changed in this PR

Extends Action Mode’s relay statistics to direct sharing, using the existing Unbounded accounting and display paths.

Changes:

  • Adds peer snapshot polling and independent counter tracking.
  • Shows direct-sharing speed, session totals, and country data.
  • Updates dependencies, translations, and regression tests.
File Description
test/​features/​action_mode/​snapshot_test.dart Tests direct-sharing accounting and speed isolation.
test/​features/​action_mode/​action_mode_widgets_test.dart Verifies direct-mode statistics display.
lib/​features/​action_mode/​provider/​share_notifier.dart Processes independent relay feeds.
lib/​features/​action_mode/​advanced_stats_sheet.dart Displays statistics for both sharing modes.
lantern-core/​unbounded_snapshot.go Generalizes polling and adds peer snapshots.
lantern-core/​unbounded_snapshot_test.go Tests peer polling failures.
lantern-core/​core.go Starts the peer snapshot poller.
go.sum Updates dependency checksums.
go.mod Updates relay-related dependencies.
assets/​locales/​en.po Removes the obsolete note and revises translator comments.

Review effort: Balanced

return;
}
if (event.eventType == 'unbounded-unavailable') {
if (!_changing && state.mode == ShareMode.unbounded) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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.

Comment thread lib/features/action_mode/provider/share_notifier.dart Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Cross-repository sharing lifecycle changes retain an unresolved counter-loss issue and need running-app validation.

2 open issues
1 resolved since last review

Review effort: Balanced

Comment thread lib/features/action_mode/provider/share_notifier.dart

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

An outage-ordering race remains, and the cross-repository integration still needs running-app validation.

1 open issue
1 previously missed issue

In code that hasn't changed since last review

Medium severity 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.

1 resolved since last review

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Cross-repository counter lifecycle changes and dependency updates still need running-app validation and human confirmation.

1 open issue

Review effort: Balanced

Base automatically changed from fisk/action-mode-mode-label to main October 19, 2026 23:15
Adam Fisk and others added 4 commits October 19, 2026 17:16
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
lantern-box v0.0.166 carries peerconn.Counter (getlantern/lantern-box#371);
radiance 79f0a5bd8 is main with the relay snapshot merged
(getlantern/radiance#688, #689) and already contains a0b72470 from #9182.
sing-box-minimal moves to the version lantern-box v0.0.166 requires.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KqE58aYaai3u8CFZe5nXdE

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Cross-repository backend changes and native event delivery need human review and running-app validation.

1 open issue

Review effort: Balanced

@myleshorton
myleshorton merged commit 3f49a57 into main Oct 19, 2026
19 of 20 checks passed
@myleshorton
myleshorton deleted the fisk/action-mode-direct-bytes branch October 19, 2026 23:23
@getlantern getlantern deleted a comment from teamlantern Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants