v0.3.5: Private blob encryption, blob prefetch, intent-based filtering, crypto refactoring

Private blob encryption:
- Encrypted posts (Friends/Circle/Direct) now encrypt attachment blobs with same CEK
- Public blobs unchanged, CID computed on ciphertext for private
- decrypt_blob_for_post/get_blob_for_post for transparent decryption on retrieval

Blob prefetch:
- Pull cycle and sync_with eagerly fetch missing blobs after post sync
- prefetch_blobs_from_peer scans for missing attachments, fetches via fallback chain
- Runs outside conn_mgr lock at Node level

Crypto refactoring:
- Extracted: encrypt/decrypt_bytes_with_cek, wrap/unwrap_cek_for_recipients
- unwrap_cek_for_recipient, unwrap_group_cek, random_cek
- encrypt_post_with_cek, encrypt_post_for_group_with_cek variants
- All existing functions refactored to delegate, 19 crypto tests pass

Intent-based filtering:
- intent_kind field on PostDto ("public"/"friends"/"circle"/"direct"/"unknown")
- Feed/MyPosts filter on intentKind !== 'direct' instead of visibility
- Messages filter with backward-compatible fallback for pre-intent posts
- get_post_intent storage method

IPC updates:
- resolve_blob_data helper using get_blob_for_post with network fallback
- sanitize_download_filename prevents path traversal
- get_blob_path accepts optional post_id_hex

Website:
- Mobile hamburger nav on all pages
- Mesh/Non-mesh N1 labels in network diagnostics

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Scott Reimers 2026-03-20 12:44:07 -04:00
parent 0abc244ee9
commit a41b11c0b8
14 changed files with 562 additions and 325 deletions

View file

@ -10,6 +10,7 @@
<body>
<nav>
<a href="index.html" class="logo">ItsGoin</a>
<button class="menu-toggle" onclick="this.parentElement.querySelector('.links').classList.toggle('open')" aria-label="Menu">&#9776;</button>
<div class="links">
<a href="index.html">About</a>
<a href="tech.html">How It Works</a>

View file

@ -26,6 +26,7 @@
<body>
<nav>
<a href="index.html" class="logo">ItsGoin</a>
<button class="menu-toggle" onclick="this.parentElement.querySelector('.links').classList.toggle('open')" aria-label="Menu">&#9776;</button>
<div class="links">
<a href="index.html">About</a>
<a href="tech.html">How It Works</a>
@ -43,7 +44,9 @@
<p>This is the canonical technical reference for ItsGoin. It describes the vision, the architecture, and the current state of every subsystem &mdash; with full implementation detail. This document is versioned; each update records what changed.</p>
<div class="card" style="margin-top: 1rem;">
<strong style="font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em;">Changelog</strong>
<p style="margin-top: 0.5rem;"><strong>v0.3.3</strong> (2026-03-16): Connection rate limiting &mdash; incoming auth failures rate-limited per source IP (3 attempts, exponential backoff to ~256s). Schema versioning &mdash; PRAGMA user_version tracks DB version with migration framework. N2/N3 freshness &mdash; TTL 7d&rarr;5h, full N1/N2 re-broadcast every 4h, startup sweep clears stale entries. Bootstrap isolation recovery &mdash; 24h check verifies bootstrap is in N1/N2/N3, reconnects + sticky N1 advertisement if absent. IPv6 HTTP address fix &mdash; nodes advertise actual public IPv6 (not 0.0.0.0) for share link redirects. Upstream tracking &mdash; post_upstream table records post source for engagement diff routing toward author. Video preload fix &mdash; share links and in-app videos use preload=auto. Following Online/Offline split. DM filter from My Posts. Any-type file attachments with download prompt + trust warning. Image lightbox. Audio player.</p>
<p style="margin-top: 0.5rem;"><strong>v0.3.5</strong> (2026-03-20): Private blob encryption &mdash; attachments on encrypted posts (Friends/Circle/Direct) now encrypted with same CEK as post text; public blobs unchanged; CID on ciphertext. Blob prefetch on sync &mdash; attachments eagerly fetched after post pull for offline availability. Crypto refactoring &mdash; extracted reusable primitives (encrypt/decrypt_bytes_with_cek, unwrap_cek_for_recipient, unwrap_group_cek). Intent-based post filtering &mdash; feed/myposts/messages filter on intentKind instead of encryption state. Blob decryption API (get_blob_for_post). Download filename sanitization.</p>
<p><strong>v0.3.4</strong> (2026-03-18): Comment edit &amp; delete with trust-based propagation. Native notifications via Tauri plugin (messages, posts, reactions, comments). Forward-compatible BlobHeaderDiffOp::Unknown variant. Following Online/Offline lightbox. Comment threading scoping fix. Dropdown text legibility fix. Mobile hamburger nav for website.</p>
<p><strong>v0.3.3</strong> (2026-03-16): Connection rate limiting &mdash; incoming auth failures rate-limited per source IP (3 attempts, exponential backoff to ~256s). Schema versioning &mdash; PRAGMA user_version tracks DB version with migration framework. N2/N3 freshness &mdash; TTL 7d&rarr;5h, full N1/N2 re-broadcast every 4h, startup sweep clears stale entries. Bootstrap isolation recovery &mdash; 24h check verifies bootstrap is in N1/N2/N3, reconnects + sticky N1 advertisement if absent. IPv6 HTTP address fix &mdash; nodes advertise actual public IPv6 (not 0.0.0.0) for share link redirects. Upstream tracking &mdash; post_upstream table records post source for engagement diff routing toward author. Video preload fix &mdash; share links and in-app videos use preload=auto. Following Online/Offline split. DM filter from My Posts. Any-type file attachments with download prompt + trust warning. Image lightbox. Audio player.</p>
<p><strong>v0.3.2</strong> (2026-03-14): Bidirectional engagement propagation &mdash; BlobHeaderDiff flows upstream + downstream through CDN tree. Auto downstream registration on pull sync/push notification. TCP hole punch protocol (TcpPunchRequest/Result 0xD6/0xD7). Tiered web serving (redirect &rarr; TCP punch &rarr; QUIC proxy). Video playback fix (asset protocol + blob URL fallback). On-demand blob fetch for synced posts missing blob data.</p>
<p><strong>v0.3.1</strong> (2026-03-13): Share links + QUIC proxy + content search. Share link format: <code>itsgoin.net/p/&lt;postid_hex&gt;/&lt;author_nodeid_hex&gt;</code> &mdash; simple, no host encoding needed. itsgoin.net web handler acts as QUIC proxy: receives browser request, searches the network for the post, fetches it on-demand via PostFetch (0xD4/0xD5), renders HTML, serves to browser. No permanent storage of fetched content. Extended worm search &mdash; <code>WormQuery</code> now carries optional <code>post_id</code> and <code>blob_id</code> fields for unified node/post/blob search. Each peer checks local storage, CDN downstream tree (up to 100 hosts per post), and blob store. <code>WormResponse</code> gains <code>post_holder</code> and <code>blob_holder</code> fields. Nova fan-out pattern &mdash; burst peers include one N2 wide referral; referred peer does its own 101-burst, reaching ~10K nodes with ~202 relay hops. PostFetch (0xD4/0xD5) &mdash; lightweight single-post retrieval after worm finds a holder, much lighter than full PullSync. itsgoin.net node deployed as anchor + web handler (<code>--web 8080</code>). &ldquo;Unavailable&rdquo; page with honest network model explanation + install CTA. Universal Links / App Links planned for native app interception. | Engagement sync &mdash; pull sync now fetches reactions, comments, and policies via BlobHeaderRequest/Response after every sync. Profile push fix &mdash; profile updates now sent to all connected mesh peers (not just audience). Auto-sync on follow &mdash; following a peer triggers immediate post pull + engagement fetch. Popover UI &mdash; notifications settings, network diagnostics, and message threads now open as popovers. Notification settings &mdash; per-key settings table in SQLite, configurable message/post/nearby notifications with JS Notification API. Tiered DM polling &mdash; smart message refresh based on conversation recency. Reaction display &mdash; posts show top 5 most popular emoji + total response count. UI cleanup &mdash; removed Suggested Peers and Find Nearby sections, placeholder text changed to &ldquo;How&rsquo;s it goin?&rdquo;, clickable node IDs in activity log.</p>
<p><strong>v0.3.0</strong> (2026-03-12): Full rename distsoc &rarr; ItsGoin. ALPN, crypto contexts, data paths, Android package ID all changed. Clean break &mdash; incompatible with prior versions.</p>

View file

@ -10,6 +10,7 @@
<body>
<nav>
<a href="index.html" class="logo">ItsGoin</a>
<button class="menu-toggle" onclick="this.parentElement.querySelector('.links').classList.toggle('open')" aria-label="Menu">&#9776;</button>
<div class="links">
<a href="index.html">About</a>
<a href="tech.html">How It Works</a>
@ -24,16 +25,16 @@
<section>
<h1 style="font-size: 2rem; font-weight: 800; letter-spacing: -0.03em; margin-bottom: 0.25rem;">Download ItsGoin</h1>
<p>Available for Android and Linux. Free and open source.</p>
<p style="color: var(--text-muted); font-size: 0.85rem;">Version 0.3.4 &mdash; March 15, 2026</p>
<p style="color: var(--text-muted); font-size: 0.85rem;">Version 0.3.5 &mdash; March 15, 2026</p>
<div class="downloads">
<a href="itsgoin-0.3.4.apk" class="download-btn btn-android">
<a href="itsgoin-0.3.5.apk" class="download-btn btn-android">
Android APK
<span class="sub">v0.3.4</span>
<span class="sub">v0.3.5</span>
</a>
<a href="itsgoin_0.3.4_amd64.AppImage" class="download-btn btn-linux">
<a href="itsgoin_0.3.5_amd64.AppImage" class="download-btn btn-linux">
Linux AppImage
<span class="sub">v0.3.4</span>
<span class="sub">v0.3.5</span>
</a>
</div>
</section>
@ -45,7 +46,7 @@
<h3 style="color: var(--accent);">Android</h3>
<ol class="steps">
<li><strong>Download the APK</strong> &mdash; Tap the button above. Your browser may warn that this type of file can be harmful &mdash; tap <strong>Download anyway</strong>.</li>
<li><strong>Open the file</strong> &mdash; When the download finishes, tap the notification or find <code>itsgoin-0.3.4.apk</code> in your Downloads folder and tap it.</li>
<li><strong>Open the file</strong> &mdash; When the download finishes, tap the notification or find <code>itsgoin-0.3.5.apk</code> in your Downloads folder and tap it.</li>
<li><strong>Allow installation</strong> &mdash; Android will ask you to allow installs from this source. Tap <strong>Settings</strong>, toggle <strong>"Allow from this source"</strong>, then go back and tap <strong>Install</strong>.</li>
<li><strong>Launch the app</strong> &mdash; Once installed, tap <strong>Open</strong> or find ItsGoin in your app drawer.</li>
</ol>
@ -58,8 +59,8 @@
<h3 style="color: var(--green);">Linux (AppImage)</h3>
<ol class="steps">
<li><strong>Download the AppImage</strong> &mdash; Click the button above to download.</li>
<li><strong>Make it executable</strong> &mdash; Open a terminal and run:<br><code>chmod +x itsgoin_0.3.4_amd64.AppImage</code></li>
<li><strong>Run it</strong> &mdash; Double-click the file, or from the terminal:<br><code>./itsgoin_0.3.4_amd64.AppImage</code></li>
<li><strong>Make it executable</strong> &mdash; Open a terminal and run:<br><code>chmod +x itsgoin_0.3.5_amd64.AppImage</code></li>
<li><strong>Run it</strong> &mdash; Double-click the file, or from the terminal:<br><code>./itsgoin_0.3.5_amd64.AppImage</code></li>
</ol>
<div class="note">
<strong>Note:</strong> If it doesn't launch, you may need to install FUSE:<br><code>sudo apt install libfuse2</code> (Debian/Ubuntu) or <code>sudo dnf install fuse</code> (Fedora).
@ -70,6 +71,16 @@
<section>
<h2>Changelog</h2>
<div class="changelog">
<div class="changelog-date">v0.3.5 &mdash; March 20, 2026</div>
<ul>
<li><strong>Private blob encryption</strong> &mdash; Attachments on encrypted posts (Friends, Circle, Direct) are now encrypted with the same CEK as the post text. Public blobs remain plaintext. CID computed on ciphertext preserves content addressing.</li>
<li><strong>Blob prefetch on sync</strong> &mdash; When posts are pulled from peers, their attachments are eagerly fetched for offline availability. Previously blobs were only fetched on view.</li>
<li><strong>Crypto refactoring</strong> &mdash; Extracted reusable primitives: <code>encrypt_bytes_with_cek</code>, <code>decrypt_bytes_with_cek</code>, <code>unwrap_cek_for_recipient</code>, <code>unwrap_group_cek</code>. Foundation for encrypted blob storage and future chunk-level encryption.</li>
<li><strong>Intent-based post filtering</strong> &mdash; Feed, My Posts, and Messages now filter on the author's original visibility intent (<code>intentKind</code>) rather than encryption state. Direct messages are identified by intent, not by being &ldquo;encrypted-for-me.&rdquo; Backward-compatible with pre-intent posts.</li>
<li><strong>Blob decryption on retrieval</strong> &mdash; New <code>get_blob_for_post</code> API decrypts private blobs in context of their post&rsquo;s visibility. Public blobs pass through unchanged.</li>
<li><strong>Download filename sanitization</strong> &mdash; Prevents path traversal in downloaded file names.</li>
</ul>
<div class="changelog-date">v0.3.4 &mdash; March 18, 2026</div>
<ul>
<li><strong>Comment edit &amp; delete</strong> &mdash; Edit or delete your own comments. Trust-based: post authors can also delete comments on their posts. Propagates via BlobHeaderDiff to all holders.</li>

View file

@ -10,6 +10,7 @@
<body>
<nav>
<a href="index.html" class="logo">ItsGoin</a>
<button class="menu-toggle" onclick="this.parentElement.querySelector('.links').classList.toggle('open')" aria-label="Menu">&#9776;</button>
<div class="links">
<a href="index.html" class="active">About</a>
<a href="tech.html">How It Works</a>

View file

@ -51,6 +51,15 @@ nav .links a {
transition: color 0.15s;
}
nav .links a:hover, nav .links a.active { color: var(--text); text-decoration: none; }
nav .menu-toggle { display: none; background: none; border: none; color: var(--text); font-size: 1.5rem; cursor: pointer; padding: 0.2rem; }
@media (max-width: 640px) {
nav { padding: 0.75rem 1rem; gap: 0; justify-content: space-between; flex-wrap: wrap; }
nav .menu-toggle { display: block; }
nav .links { display: none; width: 100%; flex-direction: column; gap: 0; padding-top: 0.5rem; }
nav .links.open { display: flex; }
nav .links a { padding: 0.5rem 0; border-top: 1px solid var(--border); font-size: 0.95rem; }
}
/* --- Layout --- */
.container { max-width: 800px; margin: 0 auto; padding: 3rem 2rem; }

View file

@ -10,6 +10,7 @@
<body>
<nav>
<a href="index.html" class="logo">ItsGoin</a>
<button class="menu-toggle" onclick="this.parentElement.querySelector('.links').classList.toggle('open')" aria-label="Menu">&#9776;</button>
<div class="links">
<a href="index.html">About</a>
<a href="tech.html" class="active">How It Works</a>