Skip to content

fix(p2p stats): improve own-IP geolocation and world map accuracy#1138

Merged
tomcasaburi merged 2 commits into
masterfrom
codex/fix/p2p-stats-ui-tweaks
May 23, 2026
Merged

fix(p2p stats): improve own-IP geolocation and world map accuracy#1138
tomcasaburi merged 2 commits into
masterfrom
codex/fix/p2p-stats-ui-tweaks

Conversation

@tomcasaburi
Copy link
Copy Markdown
Member

@tomcasaburi tomcasaburi commented May 23, 2026

Summary

  • Resolve the browser node's public endpoint when libp2p only advertises private listen addresses, and show an accurate country flag for "Your IP" via a per-IP lookup (connected peers stay offline/approximate).
  • Snap world-map peer markers to country centroids and bump land-dot contrast for clearer geography.
  • Add a "want to seed?" link when leeching, reorder stats rows, and extend tests plus generation scripts for map/centroid data.

Test plan

  • yarn test for p2p-stats-settings, peer-world-map, and peer-geo
  • yarn build, yarn lint, yarn type-check
  • Open Settings → P2P stats and confirm "Your IP" shows public address + flag
  • Confirm leeching state shows seeder link; seeding state hides it
  • Confirm connected peers still render on world map without external peer lookups

Note

Medium Risk
Introduces new network lookups (to api.country.is/ipify) and caching to resolve the local node’s public IP/country, plus significant changes to P2P stats UI/map rendering; mistakes could affect privacy expectations or display correctness.

Overview
Improves browser P2P stats to show an accurate “Your IP” (IPv4/IPv6) and matching country flag by deriving a public endpoint from libp2p-observed addresses and falling back to api.country.is/ipify, with short-lived caching and abort-safe behavior.

Upgrades the peer world map backdrop and marker placement by replacing hardcoded continent polygons with a generated Natural Earth raster mask (WORLD_MAP_DOTS) and snapping peer markers near per-country centroids (COUNTRY_CENTROIDS) with small deterministic jitter.

Tweaks the stats panel UX: adds a “want to seed?” link when in leeching mode, reorders the “updated” row placement, and adjusts styling/contrast; expands/updates tests and adds generation scripts (yarn map:generate, yarn centroids:generate) for the new data files.

Reviewed by Cursor Bugbot for commit 278c330. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Shows your public IP with country flag in P2P stats.
    • Seeding prompt link appears when in leeching mode.
  • Improvements

    • Redesigned world map backdrop for crisper, faster rendering and more visible map dots.
    • Peer markers positioned more accurately by snapping to country centroids for tighter clustering.

Review Change Stack

Resolve the user's public endpoint when libp2p only advertises private listen addresses, look up an accurate country flag for "Your IP", snap peer markers to country centroids, and add leeching seeder link plus panel layout tweaks.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
5chan Ready Ready Preview, Comment May 23, 2026 3:28pm

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7d41375d-c74e-491b-bb64-3b7cd1d8be95

📥 Commits

Reviewing files that changed from the base of the PR and between ad3b6cb and 278c330.

📒 Files selected for processing (2)
  • src/lib/__tests__/peer-geo.test.ts
  • src/lib/peer-geo.ts

📝 Walkthrough

Walkthrough

This PR adds generators and generated geographic assets, extends peer geolocation and IP utilities, surfaces the node endpoint (IP + country flag) in P2P stats, replaces polygon-based map rendering with a precomputed bitmap path, and expands tests covering these flows.

Changes

Peer Geolocation and World Map Feature

Layer / File(s) Summary
Geographic data generation infrastructure
package.json, scripts/generate-country-centroids.mjs, scripts/generate-world-map-dots.mjs, src/data/country-centroids.ts, src/data/world-map-dots.ts
Two generation scripts fetch geographic sources (country centroid CSV, Natural Earth GeoJSON), parse and rasterize them (CSV parsing + coordinate rounding; polygon rasterization → 1-bit bitmap), and emit TypeScript modules. package.json scripts added to run generators.
Geolocation core logic and IP utilities
src/lib/peer-geo.ts, src/lib/__tests__/peer-geo.test.ts
Adds IP extraction (IPv4/IPv6), public-address detection, selection of first public IP, local-node endpoint fetching with short-lived caching and IPv4 fallback, and centroid-based peer positioning with reduced jitter. Tests validate extraction, centroid snapping, selection logic, and caching/abort behavior.
Node endpoint resolution and stats gathering
src/components/settings-modal/p2p-stats-settings/p2p-stats-settings.tsx (types, helpers, stats fetching)
Collects addresses from the browser libp2p address manager and connections, resolves the node's public endpoint (countryCode + ip) via geolocation or fallback, and adds NodeEndpointStatRow to the stats shape.
Node endpoint UI rendering and table layout
src/components/settings-modal/p2p-stats-settings/p2p-stats-settings.tsx (dispatcher, component rendering)
Dispatcher passes AbortSignal into browser stats fetching, introduces StatValueCell and NodeEndpointValue to render IP+flag and a seeder link when leeching, and reorders row rendering to accommodate nodeEndpoint rows.
Map visualization with bitmap rendering
src/components/settings-modal/p2p-stats-settings/peer-world-map.tsx
Replaces runtime polygon-based rect rasterization with a single SVG <path> built from the precomputed WORLD_MAP_DOTS 1-bit bitmap for the land backdrop.
UI styling for node endpoint and map
src/components/settings-modal/p2p-stats-settings/p2p-stats-settings.module.css
Adds .statValue a link styles, .nodeEndpoint/.nodeIp layout rules, adjusts .peerFlag quoting, and increases .landDot opacity from 0.18 → 0.28.
P2P stats UI and PeerWorldMap tests
src/components/settings-modal/p2p-stats-settings/__tests__/p2p-stats-settings.test.tsx, src/components/settings-modal/p2p-stats-settings/__tests__/peer-world-map.test.tsx
Tests stub global fetch for deterministic geolocation, adapt Helia multiaddr mocks to deterministic IPs, verify "Your IP" display and icon, assert seeder link presence/absence and row ordering, validate fallback endpoint resolution, and assert PeerWorldMap DOM rendering.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I nibble cents and trace each dot,
Centroids guide where peers are brought,
A bitmap path draws seas and land,
Your IP waves with flag in hand,
Tiny hops unite the lot!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: improving own-IP geolocation (new public endpoint detection and country lookup) and world map accuracy (switching to generated bitmap-based land rendering). It is concise, specific, and directly reflects the PR's core objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix/p2p-stats-ui-tweaks

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Comment thread src/lib/peer-geo.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/lib/__tests__/peer-geo.test.ts (1)

99-130: 💤 Low value

Module-level cache state leaks between tests.

fetchOwnPublicEndpoint and fetchOwnIpCountryCode populate module-level caches (cachedOwnPublicEndpoint, ownIpCountryCache) that persist across test runs. If test order changes or tests run in parallel, cached values from one test can affect another, causing flaky behavior.

Use vi.resetModules() with dynamic import in a beforeEach to get a fresh module instance, or export cache-reset helpers for testing.

Example fix using resetModules
describe('fetchOwnPublicEndpoint', () => {
  beforeEach(() => {
    vi.resetModules();
  });

  it('caches the fetched public endpoint', async () => {
    const fetchMock = vi.fn().mockResolvedValue({
      ok: true,
      json: async () => ({ country: 'US', ip: '2001:4860:4860::8888' }),
    });
    vi.stubGlobal('fetch', fetchMock);

    // Dynamic import to get fresh module with cleared cache
    const { fetchOwnPublicEndpoint } = await import('../peer-geo');

    await expect(fetchOwnPublicEndpoint()).resolves.toEqual({ countryCode: 'us', ip: '2001:4860:4860::8888' });
    await expect(fetchOwnPublicEndpoint()).resolves.toEqual({ countryCode: 'us', ip: '2001:4860:4860::8888' });
    expect(fetchMock).toHaveBeenCalledTimes(1);

    vi.unstubAllGlobals();
  });
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/__tests__/peer-geo.test.ts` around lines 99 - 130, Tests leak
module-level cache (cachedOwnPublicEndpoint, ownIpCountryCache) causing
flakiness; fix by resetting the module before each test and importing a fresh
instance of the functions under test: call vi.resetModules() in a beforeEach and
then dynamically import fetchOwnPublicEndpoint and fetchOwnIpCountryCode from
'../peer-geo' so each test sees a clean cachedOwnPublicEndpoint and
ownIpCountryCache; alternatively add and call exported test-only cache reset
helpers from the module to clear cachedOwnPublicEndpoint and ownIpCountryCache
between tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@package.json`:
- Around line 55-60: Package.json was modified so before merging, run corepack
yarn install to sync yarn.lock and then run corepack yarn blotter:check to
validate changelog/version rules; ensure you execute these in the repo root and
commit any updated yarn.lock or blotter-fix outputs so the changes to the
"scripts" block (e.g.,
"generate:assets","llms:generate","map:generate","centroids:generate","release:manifest")
are accompanied by an updated lockfile and passing blotter check.

In `@scripts/generate-country-centroids.mjs`:
- Around line 17-21: The CSV source in CSV_URL
(scripts/generate-country-centroids.mjs) is stale and includes retired codes
like "an" while missing modern ISO alpha-2 codes (e.g., "cw", "sx", "bq");
update CSV_URL to point to a maintained centroid source or, if you prefer to
keep the current CSV, add an override map (e.g., COUNTRY_REPLACEMENTS or
centroidOverrides) in generate-country-centroids.mjs that remaps retired codes
to current ones and adds entries for missing modern codes before the code that
parses/writes the module (ensure the override is applied in the parsing function
that consumes CSV_URL and before the module writer function).

---

Nitpick comments:
In `@src/lib/__tests__/peer-geo.test.ts`:
- Around line 99-130: Tests leak module-level cache (cachedOwnPublicEndpoint,
ownIpCountryCache) causing flakiness; fix by resetting the module before each
test and importing a fresh instance of the functions under test: call
vi.resetModules() in a beforeEach and then dynamically import
fetchOwnPublicEndpoint and fetchOwnIpCountryCode from '../peer-geo' so each test
sees a clean cachedOwnPublicEndpoint and ownIpCountryCache; alternatively add
and call exported test-only cache reset helpers from the module to clear
cachedOwnPublicEndpoint and ownIpCountryCache between tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1563dc0f-7c4f-42a9-8031-5c718f4d6538

📥 Commits

Reviewing files that changed from the base of the PR and between 9c4bb28 and ad3b6cb.

📒 Files selected for processing (12)
  • package.json
  • scripts/generate-country-centroids.mjs
  • scripts/generate-world-map-dots.mjs
  • src/components/settings-modal/p2p-stats-settings/__tests__/p2p-stats-settings.test.tsx
  • src/components/settings-modal/p2p-stats-settings/__tests__/peer-world-map.test.tsx
  • src/components/settings-modal/p2p-stats-settings/p2p-stats-settings.module.css
  • src/components/settings-modal/p2p-stats-settings/p2p-stats-settings.tsx
  • src/components/settings-modal/p2p-stats-settings/peer-world-map.tsx
  • src/data/country-centroids.ts
  • src/data/world-map-dots.ts
  • src/lib/__tests__/peer-geo.test.ts
  • src/lib/peer-geo.ts

Comment thread package.json
Comment on lines 55 to 60
"scripts": {
"generate:assets": "node scripts/generate-asset-manifest.js",
"llms:generate": "node scripts/generate-llms-files.mjs",
"map:generate": "node scripts/generate-world-map-dots.mjs",
"centroids:generate": "node scripts/generate-country-centroids.mjs",
"release:manifest": "node scripts/generate-release-manifest.mjs",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Run required post-edit package checks before merge.

Because package.json changed, please run and confirm:

  • corepack yarn install (sync yarn.lock)
  • corepack yarn blotter:check

As per coding guidelines, "Run corepack yarn install to keep yarn.lock in sync when package.json changes." and "{CHANGELOG.md,package.json}: If CHANGELOG.md or package version changes, run yarn blotter:check."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` around lines 55 - 60, Package.json was modified so before
merging, run corepack yarn install to sync yarn.lock and then run corepack yarn
blotter:check to validate changelog/version rules; ensure you execute these in
the repo root and commit any updated yarn.lock or blotter-fix outputs so the
changes to the "scripts" block (e.g.,
"generate:assets","llms:generate","map:generate","centroids:generate","release:manifest")
are accompanied by an updated lockfile and passing blotter check.

Comment on lines +17 to +21
// Public-domain ISO country centroids (country,latitude,longitude,name).
// Override with COUNTRY_CENTROIDS_CSV_URL.
const CSV_URL =
process.env.COUNTRY_CENTROIDS_CSV_URL ?? 'https://raw.githubusercontent.com/google/dspl/master/samples/google/canonical/countries.csv';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Centroid source is stale for current ISO alpha-2 coverage.

The generated dataset includes retired code an (Netherlands Antilles) and misses modern split codes like cw, sx, and bq, which can cause centroid snapping gaps for valid country lookups. Please switch to a maintained ISO centroid source or add an explicit override map for modern replacements before writing the module.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/generate-country-centroids.mjs` around lines 17 - 21, The CSV source
in CSV_URL (scripts/generate-country-centroids.mjs) is stale and includes
retired codes like "an" while missing modern ISO alpha-2 codes (e.g., "cw",
"sx", "bq"); update CSV_URL to point to a maintained centroid source or, if you
prefer to keep the current CSV, add an override map (e.g., COUNTRY_REPLACEMENTS
or centroidOverrides) in generate-country-centroids.mjs that remaps retired
codes to current ones and adds entries for missing modern codes before the code
that parses/writes the module (ensure the override is applied in the parsing
function that consumes CSV_URL and before the module writer function).

When the P2P stats panel unmounts mid-request, its AbortSignal cancels
the in-flight fetchOwnPublicEndpoint / fetchOwnIpCountryCode calls. Those
empty results were still cached for 30-60s, so reopening the panel within
that window showed "Your IP" as unavailable or without a country flag even
though nothing had actually failed. Skip caching when the signal aborted so
a later open retries. Addresses Cursor Bugbot finding.
@tomcasaburi
Copy link
Copy Markdown
Member Author

Triaged review feedback and pushed one fix (278c330):

Fixed

  • Cursor Bugbot — "Caches lookup after abort" (Medium): fetchOwnPublicEndpoint / fetchOwnIpCountryCode no longer cache empty results when the request was cancelled by the panel's AbortSignal, so closing the settings modal mid-request and reopening within 30–60s no longer leaves "Your IP" stuck as unavailable / flagless. Added a regression test.

Declined

  • CodeRabbit — module-level test cache leak (flagged low-value): the caching tests are deterministic today (each stubs/unstubs fetch and uses distinct inputs), so adding vi.resetModules() plumbing isn't worth it.

Verified (process item from CodeRabbit)

  • corepack yarn installyarn.lock already in sync (no changes); yarn blotter:check passes (no version/CHANGELOG change).
  • Local: yarn build, yarn lint, yarn type-check, and yarn test (25 passing) all green.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 278c330. Configure here.

<ConnectedPeersValue row={row} />
</td>
</tr>
</Fragment>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Updated time hidden on failure

Low Severity

The last-updated row is rendered only inside the connected-peers branch, so when a refresh fails and the reducer clears rows, updatedAt is still set but the timestamp never appears in the table.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 278c330. Configure here.

@tomcasaburi
Copy link
Copy Markdown
Member Author

Triaging the two findings that landed after the previous comment:

Declined — CodeRabbit "Centroid source is stale" (rated Major): no functional impact here. COUNTRY_CENTROIDS is only ever indexed by getApproximateCountryCode(...), whose output is restricted to a fixed ~37-country pool (REGION_COUNTRIES) — none of which are cw/sx/bq/an. The retired/missing codes are never looked up, and any miss falls back to the continent centroid. Adding an override map for codes that can't be produced would be dead code.

Deferred — Cursor Bugbot "Updated time hidden on failure" (Low): the timestamp row only renders alongside the connected-peers row, so on a failed refresh (rows cleared, error shown) it doesn't appear. This is debatable as a bug — surfacing "Updated HH:MM" next to an error arguably implies the errored view is fresh. It only affects the error path and the normal/success path is correct, so it's a non-blocking follow-up rather than a merge blocker.

@tomcasaburi tomcasaburi merged commit f503928 into master May 23, 2026
9 of 10 checks passed
@tomcasaburi tomcasaburi deleted the codex/fix/p2p-stats-ui-tweaks branch May 23, 2026 15:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant