- New contribute.html with honest project story and contributor guide - Discord and Contribute links added to nav on all pages - Forgejo + Discord links in all footers - Removed sensitive data from project-notes (passwords, server paths) - Updated .gitignore to exclude keystores, APKs, AppImages, credentials Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
215 lines
14 KiB
HTML
215 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>How It Works — ItsGoin</title>
|
|
<meta name="description" content="Technical overview of ItsGoin: mesh networking, encryption, sync protocol, content addressing, and peer discovery.">
|
|
<link rel="stylesheet" href="style.css">
|
|
</head>
|
|
<body>
|
|
<nav>
|
|
<a href="index.html" class="logo">ItsGoin</a>
|
|
<div class="links">
|
|
<a href="index.html">About</a>
|
|
<a href="tech.html" class="active">How It Works</a>
|
|
<a href="design.html">Design</a>
|
|
<a href="download.html">Download</a>
|
|
<a href="contribute.html">Contribute</a>
|
|
<a href="https://discord.gg/pCjMbY9PmN">Discord</a>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="container wide">
|
|
<section>
|
|
<h1 style="font-size: 2rem; font-weight: 800; letter-spacing: -0.03em; margin-bottom: 0.5rem;">How ItsGoin works</h1>
|
|
<p>A technical overview of the networking, encryption, and sync mechanisms. For the full design rationale, see the <a href="design.html">design document</a>.</p>
|
|
</section>
|
|
|
|
<!-- Networking -->
|
|
<section>
|
|
<h2>Networking: QUIC mesh over iroh</h2>
|
|
<p>ItsGoin uses <a href="https://iroh.computer">iroh</a> (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.</p>
|
|
|
|
<div class="card">
|
|
<h3>Connection types</h3>
|
|
<table>
|
|
<tr><th>Type</th><th>Slots</th><th>Lifetime</th><th>Purpose</th></tr>
|
|
<tr><td><strong>Mesh</strong></td><td>101 (desktop), 15 (mobile)</td><td>Long-lived</td><td>Structural backbone for routing and propagation</td></tr>
|
|
<tr><td><strong>Session</strong></td><td>20 (desktop), 5 (mobile)</td><td>Minutes</td><td>Active interactions: DMs, delivery, peer discovery</td></tr>
|
|
<tr><td><strong>Ephemeral</strong></td><td>Unlimited</td><td>Single request</td><td>One-off sync or blob fetch</td></tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Mesh architecture (101 slots)</h3>
|
|
<p>Desktop nodes maintain 101 mesh connections divided into:</p>
|
|
<ul style="padding-left: 1.25rem; margin: 0.5rem 0; color: var(--text-muted);">
|
|
<li><strong>10 Preferred</strong> — bilateral agreements with close peers, protected from eviction</li>
|
|
<li><strong>71 Local</strong> — discovered through N2 diversity scoring</li>
|
|
<li><strong>20 Wide</strong> — random connections for network-wide reach</li>
|
|
</ul>
|
|
<p>New mesh connections require bilateral agreement: the connector sends an <code>InitialExchange</code> message, and the acceptor either grants a slot or sends <code>RefuseRedirect</code> with an alternative peer suggestion.</p>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Growth loop</h3>
|
|
<p>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:</p>
|
|
<pre><code>score = 1/reporter_count + 0.3 if not_already_in_N3</code></pre>
|
|
<p>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.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Knowledge layers -->
|
|
<section>
|
|
<h2>Knowledge layers: N1 / N2 / N3</h2>
|
|
<p>Each node has three layers of network awareness, built passively from mesh peer exchanges:</p>
|
|
|
|
<div class="card">
|
|
<table>
|
|
<tr><th>Layer</th><th>Contains</th><th>Source</th><th>Shared?</th></tr>
|
|
<tr><td><strong>N1 (Mesh)</strong></td><td>Live connections + social contacts</td><td>Direct</td><td>Yes (merged, no addresses)</td></tr>
|
|
<tr><td><strong>N2 (Reach)</strong></td><td>Peers' N1 shares</td><td>1 hop</td><td>Yes (NodeIds only)</td></tr>
|
|
<tr><td><strong>N3 (Search)</strong></td><td>Peers' N2 shares</td><td>2 hops</td><td>Never</td></tr>
|
|
</table>
|
|
</div>
|
|
|
|
<p><strong>Privacy guarantee</strong>: 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.</p>
|
|
|
|
<p><strong>Discovery cascade</strong> when connecting to a specific peer:</p>
|
|
<ol class="steps">
|
|
<li><strong>Social route cache</strong> — cached addresses for follows/audience</li>
|
|
<li><strong>Peers table</strong> — stored addresses from previous connections</li>
|
|
<li><strong>N2: ask reporter</strong> — ask the mesh peer who reported the target</li>
|
|
<li><strong>N3: chain resolve</strong> — 2-hop chain through reporters</li>
|
|
<li><strong>Worm search</strong> — fan-out to all mesh peers, bloom to wide referrals</li>
|
|
<li><strong>Relay introduction</strong> — hole punch via intermediary</li>
|
|
<li><strong>Own-device relay</strong> — route through your own devices (e.g. home computer to phone) when direct connection fails</li>
|
|
</ol>
|
|
</section>
|
|
|
|
<!-- Encryption -->
|
|
<section>
|
|
<h2>Encryption: envelope model</h2>
|
|
<p>ItsGoin uses <strong>1-layer envelope encryption</strong> for all private content. The design preserves content addressing (PostId = BLAKE3 of ciphertext) while allowing visibility updates without changing the PostId.</p>
|
|
|
|
<div class="card">
|
|
<h3>Per-post encryption</h3>
|
|
<p>Each private post gets a random Content Encryption Key (CEK). The post body is encrypted with <strong>ChaCha20-Poly1305</strong> using this CEK. The CEK is then wrapped separately for each intended recipient using <strong>X25519 Diffie-Hellman</strong> derived from ed25519 identity keys.</p>
|
|
<pre><code>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</code></pre>
|
|
<p>Visibility is separate metadata — re-wrapping the CEK for new recipients doesn't change the PostId or require re-encrypting the content.</p>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Group keys for circles</h3>
|
|
<p>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).</p>
|
|
<p><strong>Epoch rotation</strong>: 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.</p>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Visibility levels</h3>
|
|
<table>
|
|
<tr><th>Level</th><th>Encryption</th><th>Audience</th></tr>
|
|
<tr><td><strong>Public</strong></td><td>None</td><td>Anyone</td></tr>
|
|
<tr><td><strong>Friends</strong></td><td>Per-recipient CEK wrapping</td><td>Mutual follows</td></tr>
|
|
<tr><td><strong>Circle</strong></td><td>Group key CEK wrapping</td><td>Circle members</td></tr>
|
|
<tr><td><strong>Direct</strong></td><td>Per-recipient CEK wrapping</td><td>Specified recipients</td></tr>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Sync -->
|
|
<section>
|
|
<h2>Sync protocol (v3)</h2>
|
|
<p>The protocol uses a single ALPN (<code>itsgoin/3</code>) with 37+ message types multiplexed over QUIC bi-streams and uni-streams.</p>
|
|
|
|
<div class="card">
|
|
<h3>Pull-based sync</h3>
|
|
<p>Every 5 minutes, nodes pull posts from connected peers. The <strong>sender</strong> 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.</p>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Push for immediacy</h3>
|
|
<p>Real-time push via uni-streams for instant delivery:</p>
|
|
<ul style="padding-left: 1.25rem; margin: 0.5rem 0; color: var(--text-muted);">
|
|
<li><strong>PostNotification</strong> (0x42) — lightweight "new post" alert to mesh peers</li>
|
|
<li><strong>PostPush</strong> (0x43) — direct encrypted delivery to recipients</li>
|
|
<li><strong>ProfileUpdate</strong> (0x50) — instant profile propagation</li>
|
|
<li><strong>DeleteRecord</strong> (0x51) — signed deletion propagation</li>
|
|
<li><strong>VisibilityUpdate</strong> (0x52) — re-wrap CEK without changing PostId</li>
|
|
</ul>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Files -->
|
|
<section>
|
|
<h2>Files & content distribution</h2>
|
|
<div class="card">
|
|
<h3>Content-addressed blobs</h3>
|
|
<p>Files are stored as content-addressed blobs: <code>BlobId = BLAKE3(file_bytes)</code>. Stored on the filesystem in 256 shards (<code>blobs/{hex[0..2]}/{hex}</code>) with metadata in SQLite. Max 4 attachments per post, 10MB each.</p>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>CDN hosting tree</h3>
|
|
<p>Each blob has a hosting tree: 1 upstream source + up to 100 downstream nodes. <strong>AuthorManifest</strong> (ed25519-signed) carries the author's 10 most recent posts as a neighborhood, traveling with blob responses. <strong>ManifestPush</strong> propagates updates down the tree.</p>
|
|
<p>When a node evicts a blob, it sends <strong>BlobDeleteNotice</strong> so the tree can heal — downstream nodes find a new upstream.</p>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Eviction priority</h3>
|
|
<p>When storage fills up, blobs are evicted using a social-aware scoring formula:</p>
|
|
<pre><code>priority = pin_boost + relationship * recency * freshness / (peer_copies + 1)</code></pre>
|
|
<p>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).</p>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Anchors -->
|
|
<section>
|
|
<h2>Anchors: always-on peers</h2>
|
|
<p>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:</p>
|
|
<ul style="padding-left: 1.25rem; margin: 0.5rem 0; color: var(--text-muted);">
|
|
<li><strong>Bootstrap</strong> — new nodes connect here to discover the network and get referrals to other peers</li>
|
|
<li><strong>Matchmaker</strong> — introduce peers to each other so they can establish direct connections</li>
|
|
</ul>
|
|
<p>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.</p>
|
|
<p>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.</p>
|
|
</section>
|
|
|
|
<!-- Stack -->
|
|
<section>
|
|
<h2>Technology stack</h2>
|
|
<div class="card">
|
|
<table>
|
|
<tr><th>Component</th><th>Technology</th></tr>
|
|
<tr><td>Core library</td><td>Rust</td></tr>
|
|
<tr><td>P2P networking</td><td>iroh 0.96 (QUIC + mDNS)</td></tr>
|
|
<tr><td>Local storage</td><td>SQLite (rusqlite 0.32)</td></tr>
|
|
<tr><td>Content addressing</td><td>BLAKE3</td></tr>
|
|
<tr><td>Encryption</td><td>ChaCha20-Poly1305 + X25519 (from ed25519)</td></tr>
|
|
<tr><td>Desktop/mobile shell</td><td>Tauri v2</td></tr>
|
|
<tr><td>Frontend</td><td>Plain HTML/CSS/JS (no build step)</td></tr>
|
|
<tr><td>Platforms</td><td>Android, Linux (AppImage)</td></tr>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
<section>
|
|
<h2>Help build ItsGoin</h2>
|
|
<div class="card">
|
|
<p>Full disclosure: ItsGoin has been built by a network/tech web dev and junior programmer who's been vibe-coding well beyond his skill level with the help of AI. The architecture is ambitious, the codebase is real, and it works — but it could use experienced eyes and hands.</p>
|
|
<p>If you're a Rust developer, a systems programmer, a cryptography enthusiast, or just someone who cares about decentralized tech — we'd love your help. The code is open source and the design document lays out everything.</p>
|
|
<p style="margin-top: 1rem;">
|
|
<a href="https://git.itsgoin.net/Sologretto/itsgoin" class="btn btn-primary" style="margin-right: 0.5rem;">Browse the code</a>
|
|
<a href="https://discord.gg/pCjMbY9PmN" class="btn btn-secondary">Join the Discord</a>
|
|
</p>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
|
|
<footer>
|
|
<p>ItsGoin — Apache 2.0 License — <a href="https://itsgoin.com">itsgoin.com</a> · <a href="https://discord.gg/pCjMbY9PmN">Discord</a> · <a href="https://git.itsgoin.net/Sologretto/itsgoin">Source Code</a></p>
|
|
</footer>
|
|
</body>
|
|
</html>
|