# Project Notes from EliteBook (sologretto-HP-EliteBook-840-G3)
# Last updated: 2026-03-14

These notes capture practical knowledge learned during development that isn't in CLAUDE.md.
Other Claude instances: read this to avoid re-learning things the hard way.

## CRITICAL: Git + Nextcloud
The git repo lives inside a Nextcloud-synced folder. Nextcloud IS the git transport between machines. NEVER run git commands without confirming with the user that Nextcloud sync is paused. After git operations, ALWAYS remind the user to restart Nextcloud sync. Violating this can corrupt the repo.

## User Preferences
- Terse responses, no trailing summaries — user can read diffs
- `cargo tauri dev` reports "completed" when compilation finishes but the app is STILL RUNNING (~45s startup). Don't say it closed.
- Session relay is OPT-IN ONLY and DISABLED BY DEFAULT. Never enable it as a fallback.
- The canonical design document is `website/design.html`. Consult it FIRST using the section index below.

## design.html Section Index
Read targeted sections with `Read(file_path="website/design.html", offset=LINE, limit=RANGE)`.
Lines shift when edited — grep for section ID if not found.

| Lines | Section ID | Topic |
|-------|-----------|-------|
| 152-175 | nplus10 | N+10 Identification — preferred peers in profile |
| 176-234 | connections | Connections & Growth — 101 slots, growth loop |
| 235-300 | lifecycle | Connection Lifecycle — connect cascade (7 steps) |
| 301-335 | layers | Network Knowledge Layers — N1/N2/N3 |
| 356-437 | anchors | Anchors — detection, registration, self-verification |
| 454-586 | relay | Relay & NAT Traversal — hole punch, sessions, NAT types |
| 679-713 | worm | Worm Search — burst/nova, content search, PostFetch |
| 864-941 | files | File Layer — blobs, CDN manifest, hosting tree, eviction |
| 942-1030 | sync | Sync Protocol — pull sync, push, engagement propagation |
| 1020-1089 | encryption | Encryption — envelope, group keys, circles |
| 1216-1314 | share-links | Share Links — QUIC proxy, itsgoin.net handler |

## iroh 0.96 API Gotchas
- `endpoint.connect(addr, alpn)` — addr is `EndpointAddr`, not `NodeAddr`
- `EndpointId::from_bytes(&[u8; 32])` — fallible, returns Result
- `endpoint.id()` not `.endpoint_id()`
- `endpoint.direct_addresses()` is a stream, use `.next().await`
- `connection.remote_endpoint_id()` not `.remote_node_id()`
- Module: `iroh::endpoint::Connection`, `iroh::EndpointId`, `iroh::EndpointAddr`
- Version pins required: `curve25519-dalek = "=5.0.0-pre.1"`, `ed25519-dalek = "=3.0.0-pre.1"`
- Crypto: use `rand::rng()` not `OsRng` (iroh re-exports conflict)

## Bugs Fixed (DO NOT REINTRODUCE)
1. **IPv6-only DNS blocks mobile** — Always use all addresses (v4 + v6), never filter to just one family
2. **Mesh peer addresses not persisted** — Must call `upsert_peer()` after connection, not just in-memory
3. **Target-side hole punch discarded** — Register hole-punch results as sessions, not dropped
4. **IPv4-mapped IPv6 breaks connectivity** — Use `normalize_addr()` everywhere
5. **Stale anchor addresses block reconnect** — Update from fresh DNS, not cached
6. **Session peers invisible to relay** — `remote_addr` field required on SessionConnection
7. **Sequential hole punch wastes 30s** — Use `hole_punch_parallel()` for all attempts
8. **Requester sends unfiltered IPs in relay intro** — Filter via `is_publicly_routable()`

## Technical Lessons
- rusqlite Storage is NOT Send/Sync — always access behind `Arc<Mutex<Storage>>`
- QUIC `CONNECTION_CLOSE` can race with stream reads — handle gracefully
- Tauri: `withGlobalTauri: true` → `window.__TAURI__.core.invoke()`
- Tauri: shared tokio runtime via `tauri::async_runtime::set()`
- Desktop data dir: `~/.local/share/itsgoin/` (or next to AppImage executable)
- Connect string format: `<64-hex-node-id>@<ip:port>`
- WebKitGTK needs GStreamer codec packages for video (gstreamer1.0-plugins-bad/ugly/libav)
- Asset protocol needs `protocol-asset` feature in Cargo.toml + `assetProtocol` scope in tauri.conf.json

## Web Handler (itsgoin.net)
- Must fetch posts ON-DEMAND via QUIC, not rely on local sync
- Apache reverse proxy in `~/domains/itsgoin.net/public_html/.htaccess` forwards `/p/*` and `/b/*` to localhost:8080
- Tiered serving: redirect to HTTP holder → TCP punch → QUIC proxy fallback
- Anchor process: `/home/itsgoin/bin/itsgoin /home/itsgoin/itsgoin-anchor-data --bind 0.0.0.0:4433 --daemon --web 8080`

## Deploy Procedures
- **APK signing**: keystore `itsgoin.keystore`, alias `itsgoin` (password in local credential file)
- **Anchor deploy**: build release CLI → scp to server → stop/swap/start (see local deploy notes)
- **Website**: scp to server `~/public_html/`
- **Creds**: in local credential file (not in repo)

## Current Engagement Architecture (v0.3.2)
- BlobHeaderDiff (0xD0) flows both upstream and downstream through CDN tree
- `post_upstream` table tracks who each post was received from
- Auto downstream registration on pull sync and push notification
- Pull cycle (5 min) fetches engagement headers as safety net
- Reactions/comments: `store_reaction` upserts, `store_comment` inserts with ON CONFLICT DO NOTHING (additive merge)
- Planned: pull engagement from both upstream AND downstream peers
