# How It Works — ItsGoin
Technical overview of ItsGoin: mesh networking, encryption, sync protocol, content addressing, and peer discovery.

# How ItsGoin works

A technical overview of the networking, encryption, and sync mechanisms. For the full design rationale, see the design document.

## Networking: QUIC mesh over iroh

ItsGoin uses iroh (v0.96) for peer-to-peer networking. iroh provides QUIC transport with built-in NAT traversal, hole punching, and mDNS LAN discovery. Every node has a persistent ed25519 identity key.

### Connection types

Type | Slots | Lifetime | Purpose
--- | --- | --- | ---
Mesh | 101 (desktop), 15 (mobile) | Long-lived | Structural backbone for routing and propagation
Session | 20 (desktop), 5 (mobile) | Minutes | Active interactions: DMs, delivery, peer discovery
Ephemeral | Unlimited | Single request | One-off sync or blob fetch

### Mesh architecture (101 slots)

Desktop nodes maintain 101 mesh connections divided into:

- 10 Preferred — bilateral agreements with close peers, protected from eviction
- 71 Local — discovered through N2 diversity scoring
- 20 Wide — random connections for network-wide reach

New mesh connections require bilateral agreement: the connector sends an `InitialExchange` message, and the acceptor either grants a slot or sends `RefuseRedirect` with an alternative peer suggestion.

### Growth loop

Nodes reactively grow their mesh using diversity scoring. When a slot opens, the node evaluates N2 candidates (peers reported by mesh connections) and selects the one that maximizes reach into new parts of the network. The scoring formula:

    score = 1/reporter_count + 0.3 if not_already_in_N3

Low reporter count means fewer of your existing peers know this candidate — connecting to them brings the most new reach. The growth loop backs off after 3 consecutive failures to avoid wasting resources on unreachable candidates.

## Knowledge layers: N1 / N2 / N3

Each node has three layers of network awareness, built passively from mesh peer exchanges:

Layer | Contains | Source | Shared?
--- | --- | --- | ---
N1 (Mesh) | Live connections + social contacts | Direct | Yes (merged, no addresses)
N2 (Reach) | Peers' N1 shares | 1 hop | Yes (NodeIds only)
N3 (Search) | Peers' N2 shares | 2 hops | Never

Privacy guarantee: N1 shares merge mesh peers with social contacts into one list, making it impossible for outsiders to distinguish infrastructure connections from social relationships. Addresses are never shared — they're resolved on-demand through the chain.

Discovery cascade when connecting to a specific peer:

1. Social route cache — cached addresses for follows/audience
2. Peers table — stored addresses from previous connections
3. N2: ask reporter — ask the mesh peer who reported the target
4. N3: chain resolve — 2-hop chain through reporters
5. Worm search — fan-out to all mesh peers, bloom to wide referrals
6. Relay introduction — hole punch via intermediary
7. Own-device relay — route through your own devices (e.g. home computer to phone) when direct connection fails

## Encryption: envelope model

ItsGoin uses 1-layer envelope encryption for all private content. The design preserves content addressing (PostId = BLAKE3 of ciphertext) while allowing visibility updates without changing the PostId.

### Per-post encryption

Each private post gets a random Content Encryption Key (CEK). The post body is encrypted with ChaCha20-Poly1305 using this CEK. The CEK is then wrapped separately for each intended recipient using X25519 Diffie-Hellman derived from ed25519 identity keys.

    PostId = BLAKE3(ciphertext)
    CEK = random 256-bit key
    ciphertext = ChaCha20-Poly1305(post_body, CEK)
    wrapped_key[i] = X25519_DH(author_ed25519, recipient_ed25519[i]) XOR CEK

Visibility is separate metadata — re-wrapping the CEK for new recipients doesn't change the PostId or require re-encrypting the content.

### Group keys for circles

Circles (private groups) use per-circle ed25519 keypairs instead of per-recipient wrapping. A single DH operation wraps the CEK for the entire group, keeping overhead constant regardless of group size (~100 bytes vs. ~500 bytes per recipient).

Epoch rotation: when a member is removed, the circle generates a new keypair (new epoch). The new seed is distributed to remaining members. Old posts remain readable with old keys; new posts use the new epoch. Forward secrecy without re-encrypting history.

### Visibility levels

Level | Encryption | Audience
--- | --- | ---
Public | None | Anyone
Friends | Per-recipient CEK wrapping | Mutual follows
Circle | Group key CEK wrapping | Circle members
Direct | Per-recipient CEK wrapping | Specified recipients

## Sync protocol (v3)

The protocol uses a single ALPN (`itsgoin/3`) with 37+ message types multiplexed over QUIC bi-streams and uni-streams.

### Pull-based sync

Every 5 minutes, nodes pull posts from connected peers. The sender filters posts before sending — only posts the requester is authorized to see (based on follows, encryption recipients, and audience membership). This means the requester never learns about posts they can't decrypt.

### Push for immediacy

Real-time push via uni-streams for instant delivery:

- PostNotification (0x42) — lightweight "new post" alert to mesh peers
- PostPush (0x43) — direct encrypted delivery to recipients
- ProfileUpdate (0x50) — instant profile propagation
- DeleteRecord (0x51) — signed deletion propagation
- VisibilityUpdate (0x52) — re-wrap CEK without changing PostId

## Files & content distribution

### Content-addressed blobs

Files are stored as content-addressed blobs: `BlobId = BLAKE3(file_bytes)`. Stored on the filesystem in 256 shards (`blobs/{hex[0..2]}/{hex}`) with metadata in SQLite. Max 4 attachments per post, 10MB each.

### CDN hosting tree

Each blob has a hosting tree: 1 upstream source + up to 100 downstream nodes. AuthorManifest (ed25519-signed) carries the author's 10 most recent posts as a neighborhood, traveling with blob responses. ManifestPush propagates updates down the tree.

When a node evicts a blob, it sends BlobDeleteNotice so the tree can heal — downstream nodes find a new upstream.

### Eviction priority

When storage fills up, blobs are evicted using a social-aware scoring formula:

    priority = pin_boost + relationship * recency * freshness / (peer_copies + 1)

Your own blobs are auto-pinned and never evicted. Blobs from people you follow score higher. Blobs with many copies elsewhere score lower (safe to drop).

## Anchors: always-on peers

Anchors are standard ItsGoin nodes running on stable servers. They're regular peers that happen to be always on, which makes them useful for two things:

- Bootstrap — new nodes connect here to discover the network and get referrals to other peers
- Matchmaker — introduce peers to each other so they can establish direct connections

Anchors maintain a referral list of recently-seen peers with tiered usage caps (3/2/1 uses depending on list size). When an anchor's mesh is full, it keeps session connections for matchmaking so it remains useful as a bootstrap point.

Anchors run the same code as every other node. No special protocol, no special trust, no special services. They're just peers that are always on.

## Technology stack

Component | Technology
--- | ---
Core library | Rust
P2P networking | iroh 0.96 (QUIC + mDNS)
Local storage | SQLite (rusqlite 0.32)
Content addressing | BLAKE3
Encryption | ChaCha20-Poly1305 + X25519 (from ed25519)
Desktop/mobile shell | Tauri v2
Frontend | Plain HTML/CSS/JS (no build step)
Platforms | Android, Linux (AppImage)