docs: correct geohash publisher relay guide

Describe the empty-directory fallback to default relays, clarify that
sparse cells are about stale directory rows (not geographic distance),
and point publishers at the canonical main-branch CSV the app refreshes.
This commit is contained in:
Taksh 2026-07-29 11:44:41 +03:00
parent 8fddb49146
commit 0495c44be2

View File

@ -15,7 +15,7 @@ Location chat messages are ephemeral Nostr events:
| `kind` | `20000` (chat) or `20001` (presence heartbeat) |
| `tags` | Must include `["g", "<geohash>"]`. Chat may also carry `["n", "<nickname>"]`. |
| `content` | Message text for kind `20000`; empty for kind `20001` |
| `pubkey` | Ephemeral identity derived for that geohash (not the user's long-term key) |
| `pubkey` | Ephemeral identity derived for that geohash (not the person's long-term key) |
Clients subscribe with filters equivalent to:
@ -32,25 +32,49 @@ On subscribe and publish, BitChat asks `GeoRelayDirectory` for the closest
relays to the geohash center:
1. Decode the geohash to a lat/lon center.
2. Rank the bundled geo-relay directory by haversine distance.
2. Rank the geo-relay directory by haversine distance.
3. Take the nearest **5** relays (`TransportConfig.nostrGeoRelayCount`).
4. Ties break by hostname so every device with the same directory picks the
same set — publishers and subscribers must agree.
With an empty or unreachable directory for that cell, the client refuses to
publish rather than falling back to unrelated default relays.
### Directory source of truth
**Implication for external publishers:** replicate the same proximity selection
(or publish to a strict superset that includes those five hosts). Publishing
only to a personal fixed relay list is the most common reason a message never
appears in the app.
Do **not** rely only on the CSV bundled inside an older app build. At runtime
the client refreshes and caches the reviewed copy on `main`:
## Sparse and ocean cells
https://raw.githubusercontent.com/permissionlesstech/bitchat/refs/heads/main/relays/online_relays_gps.csv
Some geohashes (oceans, deserts, polar regions) map to few or unreachable
nearby relays. The channel UI still opens normally — there is no separate
"undeliverable" banner — but live messages may not round-trip. Treat an empty
timeline in those cells as a relay-coverage problem, not a client bug.
That URL is `GeoRelayDirectory`'s `remoteURL`. External publishers should rank
against this same file (or a freshly synced cache of it). A snapshot shipped
with an old binary can diverge after relay GPS rows change, so messages land
on hosts current clients no longer subscribe to.
### Empty-directory fallback
`closestRelays` returns `[]` only when the in-memory directory has no entries
(or `count <= 0`). In that case the publish path does **not** refuse — it
falls back to `NostrRelayManager`'s default relay list (built-in + any custom
relays). See `ChatPublicConversationCoordinator.sendPublicRaw`: empty
`targetRelays``sendEvent(event)` with no explicit `to:`.
Publishers should still prefer proximity selection against the live CSV.
Default-relay fallback is a last resort when the geo directory failed to load;
relying on it intentionally will miss clients that *did* load the directory
and are subscribed only to the nearest five.
## Sparse cells (stale directory rows)
A "sparse" cell is about **directory freshness**, not geographic remoteness.
Haversine ranking always returns the nearest five hosts from whatever GPS rows
the CSV currently has — including for oceans or deserts. When those rows are
stale or thin for a region (relay moved, GPS never updated, host offline), the
selected five can be the wrong place for live traffic even though they look
"close" on paper.
The channel UI still opens normally — there is no separate "undeliverable"
banner — but live messages may not round-trip. Treat an empty timeline in those
cells as a stale/coverage problem in the relay directory, not a client bug.
Refreshing from the canonical CSV above is the first fix to try.
## Ephemerality and late joiners
@ -68,8 +92,12 @@ with `g` (location notes / drops) — a different surface from live channel chat
1. Compute the target geohash at the precision the channel uses (`block` ≈ 7,
`neighborhood` ≈ 6, `city` ≈ 5, etc.).
2. Resolve the closest ~5 `wss://` hosts from the same geo-relay directory the
app ships (or an equivalent proximity ranking over that list).
2. Fetch the **current**
[`online_relays_gps.csv`](https://raw.githubusercontent.com/permissionlesstech/bitchat/refs/heads/main/relays/online_relays_gps.csv)
(same URL the app refreshes from). Rank by haversine to the geohash center
and take the nearest ~5 `wss://` hosts (hostname tie-break). Optionally
publish to a strict **superset** that covers both that selection and any
older bundled snapshot you still support.
3. Publish the signed kind-`20000` event to those relays with a correct `g` tag.
4. Expect no backlog for late subscribers; design tools around live delivery.
5. Do not assume App Store / mesh Bluetooth peers will see Nostr-only publishes
@ -77,7 +105,10 @@ with `g` (location notes / drops) — a different surface from live channel chat
## Related code
- `bitchat/Nostr/GeoRelayDirectory.swift` — proximity ranking
- `bitchat/Nostr/GeoRelayDirectory.swift` — proximity ranking, `remoteURL`, cache
- `bitchat/ViewModels/ChatPublicConversationCoordinator.swift` — empty-selection
fallback to default relays
- `bitchat/Services/GeohashPresenceService.swift` — subscribe/publish wiring
- `bitchat/Services/TransportConfig.swift``nostrGeoRelayCount`, lookback limits
- `bitchat/Nostr/NostrProtocol.swift` — kind `20000` / `20001` builders
- `relays/online_relays_gps.csv` — reviewed directory checked into this repo