Commit graph

20 commits

Author SHA1 Message Date
Scott Reimers
e6265b52b6 Phase 1 (0.6.0-beta): remove direct PostPush for encrypted posts
Encrypted posts now propagate only via the CDN (ManifestPush + neighbor
header updates), eliminating the sender→recipient traffic signal on the
wire. Encrypted DMs are indistinguishable from any other encrypted post.

- Remove push_post_to_recipients entirely from network.rs
- Remove call sites in create_post and re-encrypt-on-revoke
- PostPush handler now ignores non-public visibility (kept for public
  audience push path)

Known gap: non-follower DMs won't reach until Phase 3 (merged pull +
recipient-match). Followers receive via the existing CDN path — new
posts trigger neighbor-manifest updates, ManifestPush fans out to
downstream holders, recipients pull missing post IDs from followed
authors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 20:46:34 -04:00
Scott Reimers
ffb13d6791 Our Info: display peer-observed external address
Store the external address reported by peers via your_observed_addr in
initial exchange. Display it in Our Info panel with NAT classification.
Replaces reliance on iroh's pkarr/STUN for external address discovery
while keeping clear_address_lookup() (no dns.iroh.link publishing).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-19 17:37:33 -04:00
Scott Reimers
288b53ffb1 Feed pagination, duplicate identity detection, pkarr leak fix, Android SAF
Feed pagination:
- Cursor-based pagination: get_feed_page/get_all_posts_page (20 posts/page)
- Batched engagement queries (3 bulk SQL queries instead of 4 per post)
- IntersectionObserver for infinite scroll (sentinel at midpoint)
- Viewport-based media loading (blobs only load when post enters view)
- Pre-fetch next page immediately after current page renders

Duplicate identity detection:
- Anchor detects when a NodeId is already mesh-connected during initial
  exchange and sets duplicate_active flag in response
- Client skips sync tasks when duplicate detected
- Frontend shows red warning banner

Privacy:
- Fixed pkarr leak: clear_address_lookup() removes default dns.iroh.link
  publishing. Only mDNS (local network) discovery enabled.

Android:
- SAF integration via tauri-plugin-android-fs: exports open native "Save As"
  dialog so users can save to Downloads/Drive/etc.
- Download/export paths use app data dir on Android (writable)
- File picker gated behind desktop cfg (blocking_pick not on Android)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 15:35:23 -04:00
Scott Reimers
4379b6fdfc Fix 3 pre-existing lock contention issues in message handlers
- handle_pull_request: extracted to static method, conn_mgr lock no longer
  held during network I/O (read request + write response)
- handle_address_request: inlined at call site with brief lock to gather
  data, response written after lock release
- handle_session_relay: capacity check + target lookup under brief lock,
  rejection write moved outside lock scope

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 00:35:38 -04:00
Scott Reimers
be253e8001 Our Info panel, hole punch race fix, NAT profiles in relay introduction
- Network Diagnostics: "Our Info" button shows addresses with NAT status,
  device role, UPnP, HTTP capability. Addresses stacked for mobile.
- Hole punch race: re-check for existing connection before and after relay
  introduction to avoid wasting minutes on redundant punch attempts.
- Relay introduction now carries requester/target NAT mapping+filtering
  so hole punch strategy uses fresh profiles instead of stale stored ones.
  Critical for phones that switch between WiFi/cellular/VPN.
- STUN fix: filter DNS results to IPv4 (was resolving to IPv6 first on
  dual-stack, causing silent send failure and "NAT unknown").
- Welcome screen: Ready button with loading bar for instant feed access.
- LAN addresses show just "LAN" (no misleading punchability label).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:57:41 -04:00
Scott Reimers
97dc83f9f1 v0.5.0-beta: merge-with-key import, prior_author provenance, beta versioning
Merge-with-key: decrypt exported posts using original identity seed, re-create
under current identity with prior_author in BlobHeader for provenance tracking.
Download page restructured with stable (v0.4.4) + beta (v0.5.0-beta) sections.
Version bumped across all crates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 14:47:24 -04:00
Scott Reimers
fb1e92985c Audit fixes: key permissions, lock contention, Docker IP filter, doc updates
Security: identity.key written with 0600 permissions (Unix). Docker bridge
IPs (172.17-31.x) filtered from is_shareable_addr to prevent topology
disclosure in relay introductions.

Lock contention: ManifestPush relay and DeleteRecord CDN notify now gather
connections under lock, then send outside lock.

UI: syncBtn null guard prevents crash on hidden element.

Documentation: design.html version badge updated to v0.4.4. Self Last
Encounter threshold corrected from 3h to 4h. Multi-Device Identity section
rewritten for multi-identity-per-device (complete) + multi-device (planned)
+ post merge (planned). MEMORY.md updated to v0.4.4+ status.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 19:37:17 -04:00
Scott Reimers
1030dc21a7 Sync fixes, hole punch address family filtering, ManifestPush blob fetch
Per-peer sync (People tab Sync button) now resets last_sync_ms to 0 so
the responder sends ALL posts, not just posts newer than last sync.

ManifestPush post discovery now fetches blobs alongside discovered posts
while the connection is live, instead of waiting for the next prefetch cycle.

Hole punch: filter_reachable_families() checks endpoint bound sockets and
removes addresses the local endpoint can't reach (IPv4-only won't try IPv6).
Applied to hole_punch_single, hole_punch_parallel, hole_punch_with_scanning.

Relay introduce paths switched from is_publicly_routable to is_shareable_addr
so LAN addresses (192.168.x.x) are included for same-WiFi hole punching.
is_shareable_addr made pub(crate).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 17:00:43 -04:00
Scott Reimers
409e44762a Clean up warnings: remove dead code, unused imports
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 23:38:18 -04:00
Scott Reimers
6320a82852 Keepalive fix, auto-reconnect on disconnect, tab icon fix, video playback guard
Keepalive: tokio::time::sleep inside select! was resetting every iteration —
keepalives never fired. Switched to tokio::time::interval which ticks reliably.
This caused connections to be zombie-reaped (10min timeout with no pings).

Auto-reconnect: unexpected disconnects (stream error, not SocialDisconnectNotice)
now attempt direct reconnect after 3s delay using last known address from peers
table or social route. Falls back to notify_growth() if direct reconnect fails.

Tab icons: updateTabBadge was using textContent which destroyed the icon and
label spans inside tab buttons. Now updates only the .tab-label span and manages
a separate .tab-badge element.

Video playback: feed re-render skipped while any video or audio is actively
playing, preventing echo from DOM destruction and media element recreation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 23:27:41 -04:00
Scott Reimers
7b9edc13da Fix blocking_lock panic inside async runtime, reduce pool to 4 connections
spawn_with_arc used blocking_lock() which panics inside tokio runtime.
Changed to async lock().await. Reduced StoragePool from 8 to 4 connections
for Android compatibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:10:50 -04:00
Scott Reimers
43adbbdf7d v0.4.3: Lock contention overhaul, StoragePool, mobile bottom nav, text scaling
Eliminate all conn_mgr lock holds during network I/O across 14 actor commands
and bi-stream handlers. PostFetch, TcpPunch, PullFromPeer, FetchEngagement,
ResolveAddress, AnchorProbe use brief locks for data gathering only. WormLookup,
ContentSearch, WormQuery use connection snapshots for lock-free cascade fan-out.
RelayIntroduce extracts forwarding data under brief lock, does I/O outside.
BlobRequest, PostFetchRequest, ManifestRefresh use Arc clones instead of conn_mgr
lock. ConnectionActor hoists shared Arcs (storage, blob_store, endpoint) for
lock-free access. ResolveAddress adds 5s per-query timeout (was unbounded).

Initial exchange failure now aborts mesh upgrade (was silently continuing with
broken connection). connect_to_peer/connect_to_anchor use consistent 15s timeout.
Rebalance connects outside the lock via pending_connects pattern.

StoragePool: 8 concurrent SQLite connections in WAL mode replace single
Mutex<Storage>. Reads run fully parallel; writes serialize at SQLite level only.
PRAGMA busy_timeout=5000 for graceful write contention.

Mobile bottom nav bar (<=768px) with icon tabs. Text sizes: XS/S/M/L/XL
(75%/100%/125%/150%/200%), default M. localStorage persistence for instant
restore. Toast repositioned above mobile nav.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 21:35:38 -04:00
Scott Reimers
6004cae8a8 v0.4.2: Welcome screen, status ticker, notifications, text scaling, networking fixes
Welcome screen with staggered counters while backend bootstraps. Header status
ticker for new posts/messages/reactions/comments/connection changes. Notification
fallback chain (Tauri plugin → Web API → notify-rust). Responsive text scaling
(Small/Normal/Large, persisted). Diagnostics moved to popover with on-demand
connections. Share details lightbox with QR code. Connect string prefers external
address. Stale N1 fix (disconnected routes excluded). Replication handler actively
fetches posts+blobs from requester. Hole punch registers remote address for relay.
Replication semaphore (3 concurrent). Peer labels show truncated node ID.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 14:15:49 -04:00
Scott Reimers
bb6f2b64b0 v0.4.1: Security hardening, lock contention fixes, data cleanup
Security:
- Reaction signatures: ed25519 sign/verify (sign_reaction, verify_reaction_signature)
  Backward-compatible — unsigned reactions from old nodes still accepted
- Comment signature verification: verify_comment_signature now called on receipt
- Reaction removal authorization: only reactor or post author can remove
- BlobHeader author verification: lookup actual author from storage, don't trust payload

Lock contention (4 fixes):
- ManifestPush discovery: cm lock released before PostFetch I/O
- Pull request handler: load under lock, filter without lock, brief re-lock for is_deleted
- Pull sender: split into two brief locks (store posts, then batch upstream+sync)
- Engagement checker: batch all chunk results, single lock for writes

Data cleanup:
- Post deletion cleans post_downstream, post_upstream, seen_engagement tables
- Added TODO-hardening.md documenting remaining DOS/security/lock/data issues

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 19:30:38 -04:00
Scott Reimers
bbaacf9b6c v0.4.0: Protocol v4 — header-driven sync, tiered engagement, multi-upstream
Protocol v4 sync overhaul:
- Slim PullSyncRequest: per-author timestamps (since_ms) replace full post ID lists
  Request size O(follows) instead of O(posts). Backward-compatible via serde default.
- Tiered pull frequency: 60s ticks, only syncs stale authors (4hr default)
  Full pull only on first tick (bootstrap). Most ticks skip — no stale authors.
- Tiered engagement checks: frequency scales with content age
  5min (<72h), 1hr (3-14d), 4hr (14-30d), 24hr (>30d)
  Single SQL query filters posts due for check.
- Header-driven post discovery: ManifestPush triggers PostFetch for missing
  followed-author posts (capped 10 per manifest). CDN tree = notification system.
- Multi-upstream (3 max): composite PK, priority ordering, engagement diffs
  sent to all upstreams, promote/remove on failure.

DB schema:
- follows.last_sync_ms — Self Last Encounter per author
- posts.last_engagement_ms — last reaction/comment timestamp
- posts.last_check_ms — last engagement check timestamp
- post_upstream: single-row → 3-row with priority column

Lock contention fixes:
- get_blob_for_post: 3 locks → 1
- prefetch_blobs_from_peer: lock-free blob checks
- fetch_engagement_from_peer: explicit lock release before I/O
- serve_post: 4 locks → 2 (eliminated redundant queries)
- run_replication_check: 2 locks → 1
- Badge cycle: N+2 IPC calls → 1 (get_badge_counts)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 16:13:45 -04:00
Scott Reimers
89d6a853f5 Fix storage lock contention: reduce lock holds across 6 hot paths
- get_blob_for_post: 3 sequential locks → 1 combined acquisition
- prefetch_blobs_from_peer: lock only for DB reads, blob checks outside lock
- fetch_engagement_from_peer: explicit lock release before next network I/O
- serve_post: 4 locks (2 redundant) → 2
- run_replication_check: 2 locks → 1 combined
- Badge cycle: N+2 IPC calls → 1 (new get_badge_counts command)
- Follow timeout: 15s cap on auto-sync-on-follow to prevent UI hang
- Notification clearing: clear system notifications on conversation read

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 13:02:30 -04:00
Scott Reimers
a7e632de88 v0.3.6: Active CDN replication, device roles, budgets, tombstones, engagement fix, DOS hardening
Active CDN replication:
- All devices proactively replicate recent posts (<72h, <2 replicas) to peers
- Target priority: desktops (300) > anchors (200) > phones (100) + cache_pressure
- ReplicationRequest/Response (0xE1/0xE2) wire messages
- 10-min cycle, 2-min initial delay, cap 20 posts per request
- Graceful with small networks (1 peer = 1 replica, 0 peers = silent skip)

Device roles & budgets:
- Intermittent (phone), Available (desktop), Persistent (anchor)
- Advertised in InitialExchange, stored per-peer
- Replication budget: phones 100MB/hr, desktops/anchors 200MB/hr
- Delivery budget: phones 1GB/hr, desktops 2GB/hr, anchors 1GB/hr
- Hourly auto-reset, enforcement on blob serving

Cache management:
- 1GB default cache limit, configurable in settings UI
- Eviction cycle activated (was implemented but never started)
- Share-link priority boost (+100 for 3+ downstream)
- Cache pressure score (0-255) for replication targeting

Engagement distribution fix:
- BlobHeader JSON rebuilt after BlobHeaderDiff ops
- Previously reactions/comments stored in tables but header stayed stale

Tombstone system:
- deleted_at column on reactions and comments
- Tombstones propagate through pull sync (additive merge respects timestamps)
- UI queries filter WHERE deleted_at IS NULL

Persistent notifications:
- seen_engagement and seen_messages tables replace in-memory Sets
- Only notify on genuinely unseen content, survives restarts

DOS hardening:
- BlobHeaderDiff fan-out: single batched task, max 10 concurrent via JoinSet
- Blob prefetch: cap 20 per cycle, newest first
- PostDownstreamRegister: cap 50 per sync
- Delivery budget enforcement on BlobRequest handler
- Pull preference: non-anchors first to preserve anchor delivery budget

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 21:00:28 -04:00
Scott Reimers
b7f2d369fa v0.3.5: Encrypted receipt & comment slots, message delivery indicators
Encrypted slots in BlobHeader:
- Private posts get noise-prefilled receipt slots (64B, 1 per participant)
  and comment slots (256B, ceil(participants/3), expandable)
- Slot key derived from post CEK via BLAKE3 — only participants can read
- CDN relays propagate opaque encrypted bytes without decryption
- 3 new BlobHeaderDiffOps: WriteReceiptSlot, WriteCommentSlot, AddCommentSlots

Receipt system:
- States: empty(0), delivered(1), seen(2), reacted(3)
- Slot index = position in sorted participant NodeId list
- Author can pre-feed emoji reaction at creation time
- 6 new crypto tests for slot encrypt/decrypt/derivation

Node methods:
- write_receipt_slot, write_comment_slot with upstream+downstream propagation
- read_receipt_slots, read_comment_slots with CEK-based decryption
- get_post_cek_and_participants helper for both Encrypted and GroupEncrypted

IPC: write_message_receipt, write_message_comment, get_message_receipts,
     get_message_comments

Frontend:
- DM chat bubbles show delivery indicators (check → double → blue → emoji)
- Opening conversation auto-marks incoming messages as seen
- React button on messages with emoji prompt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 14:15:33 -04:00
Scott Reimers
0abc244ee9 v0.3.4: Comment edit/delete, native notifications, forward-compatible protocol, UI fixes
Comment edit & delete:
- EditComment/DeleteComment BlobHeaderDiffOps with upstream+downstream propagation
- Trust-based: comment author can edit/delete, post author can delete
- Storage: edit_comment(), delete_comment() methods
- Frontend: inline edit (Enter/Escape), delete with confirm

Native notifications:
- tauri-plugin-notification for system notifications on all platforms
- Triggers for messages, new posts, reactions, and comments
- notif_reacts setting added, button-group toggles replace dropdowns
- _notifReady flag prevents startup spam

Protocol hardening:
- BlobHeaderDiffOp::Unknown variant with #[serde(other)] for forward compatibility
- Old nodes silently skip unknown ops instead of crashing

UI fixes:
- Self removed from Following list
- Offline follows in lightbox popup (auto-show if no one online)
- Sent DMs filtered from My Posts
- Comment threading scoped to closest .post (fixes duplicate ID issue)
- Select dropdown text legible in WebKitGTK (black on white options)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 00:47:53 -04:00
Scott Reimers
800388cda4 ItsGoin v0.3.2 — Decentralized social media network
No central server, user-owned data, reverse-chronological feed.
Rust core + Tauri desktop + Android app + plain HTML/CSS/JS frontend.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 20:23:09 -04:00