diff options
Diffstat (limited to 'news/phase3-api-and-apps/index.html')
| -rw-r--r-- | news/phase3-api-and-apps/index.html | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/news/phase3-api-and-apps/index.html b/news/phase3-api-and-apps/index.html new file mode 100644 index 0000000..1f0feab --- /dev/null +++ b/news/phase3-api-and-apps/index.html @@ -0,0 +1,163 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>Phase 3: Memories in Your Hands — Tesseras</title> + <meta name="description" content="Tesseras now has a Flutter app and an embedded Rust node — anyone can create and preserve memories from their phone."> + <!-- Open Graph --> + <meta property="og:type" content="article"> + <meta property="og:title" content="Phase 3: Memories in Your Hands"> + <meta property="og:description" content="Tesseras now has a Flutter app and an embedded Rust node — anyone can create and preserve memories from their phone."> + <meta property="og:image" content="https://tesseras.net/images/social.jpg"> + <meta property="og:image:width" content="1200"> + <meta property="og:image:height" content="630"> + <meta property="og:site_name" content="Tesseras"> + <!-- Twitter Card --> + <meta name="twitter:card" content="summary_large_image"> + <meta name="twitter:title" content="Phase 3: Memories in Your Hands"> + <meta name="twitter:description" content="Tesseras now has a Flutter app and an embedded Rust node — anyone can create and preserve memories from their phone."> + <meta name="twitter:image" content="https://tesseras.net/images/social.jpg"> + <link rel="stylesheet" href="https://tesseras.net/style.css?h=21f0f32121928ee5c690"> + + + <link rel="alternate" type="application/atom+xml" title="Tesseras" href="https://tesseras.net/atom.xml"> + + + <link rel="icon" type="image/png" sizes="32x32" href="https://tesseras.net/images/favicon.png?h=be4e123a23393b1a027d"> + +</head> +<body> + <header> + <h1> + <a href="https://tesseras.net/"> + <img src="https://tesseras.net/images/logo-64.png?h=c1b8d0c4c5f93b49d40b" alt="Tesseras" width="40" height="40" class="logo"> + Tesseras + </a> + </h1> + <nav> + + <a href="https://tesseras.net/about/">About</a> + <a href="https://tesseras.net/news/">News</a> + <a href="https://tesseras.net/releases/">Releases</a> + <a href="https://tesseras.net/faq/">FAQ</a> + <a href="https://tesseras.net/subscriptions/">Subscriptions</a> + <a href="https://tesseras.net/contact/">Contact</a> + + </nav> + <nav class="lang-switch"> + + <strong>English</strong> | <a href="/pt-br/news/phase3-api-and-apps/">Português</a> + + </nav> + </header> + + <main> + +<article> + <h2>Phase 3: Memories in Your Hands</h2> + <p class="news-date">2026-02-14</p> + <p>People can now hold their memories in their hands. Phase 3 delivers what the +previous phases built toward: a mobile app where someone downloads Tesseras, +creates an identity, takes a photo, and that memory enters the preservation +network. No cloud accounts, no subscriptions, no company between you and your +memories.</p> +<h2 id="what-was-built">What was built</h2> +<p><strong>tesseras-embedded</strong> — A full P2P node that runs inside a mobile app. The +<code>EmbeddedNode</code> struct owns a Tokio runtime, SQLite database, QUIC transport, +Kademlia DHT engine, replication service, and tessera service — the same stack +as the desktop daemon, compiled into a shared library. A global singleton +pattern (<code>Mutex<Option<EmbeddedNode>></code>) ensures one node per app lifecycle. On +start, it opens the database, runs migrations, loads or generates an Ed25519 +identity with proof-of-work node ID, binds QUIC on an ephemeral port, wires up +DHT and replication, and spawns the repair loop. On stop, it sends a shutdown +signal and drains gracefully.</p> +<p>Eleven FFI functions are exposed to Dart via flutter_rust_bridge: lifecycle +(<code>node_start</code>, <code>node_stop</code>, <code>node_is_running</code>), identity (<code>create_identity</code>, +<code>get_identity</code>), memories (<code>create_memory</code>, <code>get_timeline</code>, <code>get_memory</code>), and +network status (<code>get_network_stats</code>, <code>get_replication_status</code>). All types +crossing the FFI boundary are flat structs with only <code>String</code>, <code>Option<String></code>, +<code>Vec<String></code>, and primitives — no trait objects, no generics, no lifetimes.</p> +<p>Four adapter modules bridge core ports to concrete implementations: +<code>Blake3HasherAdapter</code>, <code>Ed25519SignerAdapter</code>/<code>Ed25519VerifierAdapter</code> for +cryptography, <code>DhtPortAdapter</code> for DHT operations, and +<code>ReplicationHandlerAdapter</code> for incoming fragment and attestation RPCs.</p> +<p>The <code>bundled-sqlite</code> feature flag compiles SQLite from source, required for +Android and iOS where the system library may not be available. Cargokit +configuration passes this flag automatically in both debug and release builds.</p> +<p><strong>Flutter app</strong> — A Material Design 3 application with Riverpod state +management, targeting Android, iOS, Linux, macOS, and Windows from a single +codebase.</p> +<p>The <em>onboarding flow</em> is three screens: a welcome screen explaining the project +in one sentence ("Preserve your memories across millennia. No cloud. No +company."), an identity creation screen that triggers Ed25519 keypair generation +in Rust, and a confirmation screen showing the user's name and cryptographic +identity.</p> +<p>The <em>timeline screen</em> displays memories in reverse chronological order with +image previews, context text, and chips for memory type and visibility. +Pull-to-refresh reloads from the Rust node. A floating action button opens the +<em>memory creation screen</em>, which supports photo selection from gallery or camera +via <code>image_picker</code>, optional context text, memory type and visibility dropdowns, +and comma-separated tags. Creating a memory calls the Rust FFI synchronously, +then returns to the timeline.</p> +<p>The <em>network screen</em> shows two cards: node status (peer count, DHT size, +bootstrap state, uptime) and replication health (total fragments, healthy +fragments, repairing fragments, replication factor). The <em>settings screen</em> +displays the user's identity — name, truncated node ID, truncated public key, +and creation date.</p> +<p>Three Riverpod providers manage state: <code>nodeProvider</code> starts the embedded node +on app launch using the app documents directory and stops it on dispose; +<code>identityProvider</code> loads the existing profile or creates a new one; +<code>timelineProvider</code> fetches the memory list with pagination.</p> +<p><strong>Testing</strong> — 9 Rust unit tests in tesseras-embedded covering node lifecycle +(start/stop without panic), identity persistence across restarts, restart cycles +without SQLite corruption, network event streaming, stats retrieval, memory +creation and timeline retrieval, and single memory lookup by hash. 2 Flutter +tests: an integration test verifying Rust initialization and app startup, and a +widget smoke test.</p> +<h2 id="architecture-decisions">Architecture decisions</h2> +<ul> +<li><strong>Embedded node, not client-server</strong>: the phone runs the full P2P stack, not a +thin client talking to a remote daemon. This means memories are preserved even +without internet. Users with a Raspberry Pi or VPS can optionally connect the +app to their daemon via GraphQL for higher availability, but it's not +required.</li> +<li><strong>Synchronous FFI</strong>: all flutter_rust_bridge functions are marked +<code>#[frb(sync)]</code> and block on the internal Tokio runtime. This simplifies the +Dart side (no async bridge complexity) while the Rust side handles concurrency +internally. Flutter's UI thread stays responsive because Riverpod wraps calls +in async providers.</li> +<li><strong>Global singleton</strong>: a <code>Mutex<Option<EmbeddedNode>></code> global ensures the node +lifecycle is predictable — one start, one stop, no races. Mobile platforms +kill processes aggressively, so simplicity in lifecycle management is a +feature.</li> +<li><strong>Flat FFI types</strong>: no Rust abstractions leak across the FFI boundary. Every +type is a plain struct with strings and numbers. This makes the auto-generated +Dart bindings reliable and easy to debug.</li> +<li><strong>Three-screen onboarding</strong>: identity creation is the only required step. No +email, no password, no server registration. The app generates a cryptographic +identity locally and is ready to use.</li> +</ul> +<h2 id="what-comes-next">What comes next</h2> +<ul> +<li><strong>Phase 4: Resilience and Scale</strong> — Advanced NAT traversal (STUN/TURN), +Shamir's Secret Sharing for heirs, sealed tesseras with time-lock encryption, +performance tuning, security audits, OS packaging for +Alpine/Arch/Debian/FreeBSD/OpenBSD</li> +<li><strong>Phase 5: Exploration and Culture</strong> — Public tessera browser by +era/location/theme/language, institutional curation, genealogy integration, +physical media export (M-DISC, microfilm, acid-free paper with QR)</li> +</ul> +<p>The infrastructure is complete. The network exists, replication works, and now +anyone with a phone can participate. What remains is hardening what we have and +opening it to the world.</p> + +</article> + + </main> + + <footer> + <p>© 2026 Tesseras Project. <a href="/atom.xml">News Feed</a> · <a href="https://git.sr.ht/~ijanc/tesseras">Source</a></p> + </footer> +</body> +</html> |