Phase 3: Memories in Your Hands
-2026-02-14
-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.
-What was built
-tesseras-embedded — A full P2P node that runs inside a mobile app. The
-EmbeddedNode 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 (Mutex<Option<EmbeddedNode>>) 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.
Eleven FFI functions are exposed to Dart via flutter_rust_bridge: lifecycle
-(node_start, node_stop, node_is_running), identity (create_identity,
-get_identity), memories (create_memory, get_timeline, get_memory), and
-network status (get_network_stats, get_replication_status). All types
-crossing the FFI boundary are flat structs with only String, Option<String>,
-Vec<String>, and primitives — no trait objects, no generics, no lifetimes.
Four adapter modules bridge core ports to concrete implementations:
-Blake3HasherAdapter, Ed25519SignerAdapter/Ed25519VerifierAdapter for
-cryptography, DhtPortAdapter for DHT operations, and
-ReplicationHandlerAdapter for incoming fragment and attestation RPCs.
The bundled-sqlite 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.
Flutter app — A Material Design 3 application with Riverpod state -management, targeting Android, iOS, Linux, macOS, and Windows from a single -codebase.
-The onboarding flow 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.
-The timeline screen 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
-memory creation screen, which supports photo selection from gallery or camera
-via image_picker, 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.
The network screen shows two cards: node status (peer count, DHT size, -bootstrap state, uptime) and replication health (total fragments, healthy -fragments, repairing fragments, replication factor). The settings screen -displays the user's identity — name, truncated node ID, truncated public key, -and creation date.
-Three Riverpod providers manage state: nodeProvider starts the embedded node
-on app launch using the app documents directory and stops it on dispose;
-identityProvider loads the existing profile or creates a new one;
-timelineProvider fetches the memory list with pagination.
Testing — 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.
-Architecture decisions
--
-
- Embedded node, not client-server: 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. -
- Synchronous FFI: all flutter_rust_bridge functions are marked
-
#[frb(sync)]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.
- - Global singleton: a
Mutex<Option<EmbeddedNode>>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.
- - Flat FFI types: 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. -
- Three-screen onboarding: 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. -
What comes next
--
-
- Phase 4: Resilience and Scale — 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 -
- Phase 5: Exploration and Culture — Public tessera browser by -era/location/theme/language, institutional curation, genealogy integration, -physical media export (M-DISC, microfilm, acid-free paper with QR) -
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.
- -