Files

15 KiB

TODO

Features

  • implement general way to fetch Documents (esp. PDFs) from URLs

Display content size in documents view

  • Description: BlobRef will gain a size_bytes: u64 field and a new ArtifactRef type will carry a size_bytes field, both as part of the document sync protocol (see docs/document-sync.md). Once those fields exist, surface the total content size per document in both the document list (as a compact e.g. "4.2 MB") and the document detail screen.
  • Details: Add total_blob_bytes and total_artifact_bytes aggregates to the DocumentStore query; expose them through DocumentDisplay in the GUI and Android DocumentDisplayModel. Show "—" or hide the field for documents with no blobs or artifacts.
  • fix issue in GUI with document import happening on main ui thread
  • Pins: a generic cross-agent piece of functionality for marking a Document as something to go through next ( could be implemented with Edges)

Type Generation & FFI

Android/Kotlin Improvements

Add node name editing to Android UI

  • Description: The desktop GUI and CLI now support changing the node's human-readable name (stored in Settings::node_name) and broadcasting the change via gossip. The Android app needs a matching settings screen.
  • Details: Add a text field in the Android settings UI that reads/writes node_name via the existing SettingsStore FFI. When saved, trigger a gossip re-announce so peers learn the new name. The Rust-side P2PNode::update_node_name method already handles this.

Fix UUID Serialization Inconsistency

  • Description: Current Kotlin code expects UUID as List<Int> but Rust serializes with compact format
  • Details: Align UUID serialization between Rust and Kotlin, potentially using string representation for consistency

Audit Android color scheme usage

  • Description: Review all color usages across the Android UI and ensure they are consistent and aesthetically coherent
  • Details: Audit every hardcoded color and every MaterialTheme.colorScheme.* token used in Compose screens; verify that semantic tokens (primary, secondary, surface, error, etc.) are applied to the right purposes and that the overall palette looks good together

Improve Error Handling in NativeLib

  • Description: Currently some exceptions are caught but not re-thrown in NativeLib.initialize()
  • Details: Decide on proper error handling strategy for native library initialization failures

Use Result<T> return types in NativeLib for all fallible FFI calls

  • Description: Every #[uniffi::export] Rust function that returns Result<T, SyncError> generates a Kotlin function annotated @Throws(SyncException::class). Kotlin does not enforce checked exceptions at the language level, so callers silently forget to handle errors and the app crashes at runtime.

  • Details: Change every NativeLib wrapper method that calls a fallible uniffi function to return Result<T> via runCatching { uniffi.synchronicity.foo(...) }. Callers then use .getOrNull(), .fold(onSuccess, onFailure), .onFailure { log }, etc. The return-type change causes a compile error at every affected call site, so the migration is compiler-guided. Methods where SyncException.NotFound means "does not exist" (rather than a bug) should return a nullable type directly (T?) rather than Result<T>, so callers get idiomatic null-safety instead of error handling.

  • Use the DocumentList component consistently any time another component needs a picker; in particular the podcast view should use this.

Key Management & Identity

  • Consider SLIP-0039 instead of BIP39

Consider a human-input secret derivation path using Argon2id

  • Description: Node and ensemble key derivation now uses HKDF-SHA256, which is correct because the inputs are already high-entropy (128-bit random seeds or validated diceware material). The argon2 crate is still a dependency in case we want a separate mechanism for deriving high-entropy keys from genuinely low-entropy human-chosen inputs — e.g. an encryption passphrase for at-rest node secrets, or a user-chosen PIN guarding access to a delegation cert.
  • Details: Argon2id is the right primitive for that job (memory-hard, tunable cost, designed exactly for password stretching). If/when such a path is needed, design it as a separate module rather than re-introducing it to Secret::to_keypair. Pick parameters appropriate for mobile (where the CPU/memory cost matters most) and store a per-secret random salt with the ciphertext.

Design a robust manifold key rotation scheme

  • Description: The current design has no mechanism for rotating the manifold (master) keypair if it is compromised. This is a known deliberate omission for the bootstrap phase.
  • Details: Evaluate options including:
    • PKI / chain-of-trust anchor that vouches for the new key
    • Social recovery via threshold signatures across trusted nodes (e.g. M-of-N)
    • Timestamped revocation records published to the DHT under the old key
    • Whether iroh's existing infrastructure provides any useful primitives here
  • See docs/KEY_MANAGEMENT.md for full context on the current design.
  • this also needs to apply to the PkarrKeypair

Testing

Integration test: cold-start-after-total-outage bootstrap

  • Description: Write an integration test that exercises the scenario where all nodes go offline long enough for the DHT entry to expire, then come back online one by one.
  • Details:
    • Will need a fake/mock DHT whose state can be controlled from the test harness (e.g. inject entries, clear entries, simulate expiry)
    • Verify: first node back online detects no DHT record and publishes its own
    • Verify: second node discovers first via DHT, both join gossip, node IDs propagate
    • Verify: subsequent nodes bootstrap successfully via gossip without needing DHT
    • See docs/BOOTSTRAPPING.md for the full design

Core Functionality

Implement Preliminary P2P System using Iroh

  • Description: Add proof-of-concept peer-to-peer networking for decentralized document synchronization
  • Details:
    • Add iroh dependencies to rust/lib/Cargo.toml
    • Implement basic iroh node setup and endpoint creation
    • Create FFI bindings to expose p2p functionality to Android
    • Add device discovery and connection capabilities
    • Implement document sync using iroh-blobs for artifacts and iroh-docs for metadata
    • Add basic UI for device pairing and connection status
    • Test synchronization of documents between two devices on local network
  • Benefits: Foundation for true decentralized document exchange, eliminates need for central server, enables offline-first synchronization
  • consider some key management ideas inspired by FOKS the Federated Open Key Service https://foks.pub/#roadmap

Set up Rich Podcast episode experience

  • create a rust data structure that can represent transcript, sections, etc.
  • and also display them in the UI
  • Linux Unplugged is a good model to use for a podcast that supports these

Add Podcast Episode Management

  • Description: Currently only podcast subscription is implemented
  • Details: Add functionality to manage individual podcast episodes, including download, playback position tracking
  • make sure that you can handle podcasts (e.g. Linux Unplugged) that have chapter metadata
  • Description: Add search functionality across all document types
  • Details: Extend DocumentStore with full-text search capabilities

Reliability

  • the app needs to not have unclear user-facing errors under conditions of network unreliability or deliberately cutting off the network
  • there should be tests for this

Agent Display

Make agent sidebar order user-configurable

  • Description: Currently activity agents are sorted alphabetically at the top of the sidebar and control agents alphabetically at the bottom. The order should eventually be configurable by the user.
  • Details: Allow users to drag-reorder agents within each group, or provide a settings UI to reorder them. The ordering preference should be persisted per-node and synced across nodes.

UX / CLI

Show authoritative document type strings in CLI help

  • Description: The --filter flag on docs list currently has a hand-written list of type names in its help text. This will drift as new DocumentType variants are added.
  • Details:
    • DocumentType already derives strum's IntoStaticStr and EnumString with serialize_all = "snake_case", so the canonical string for each variant is already machine-readable via strum::IntoEnumIterator (needs the EnumIter derive added)
    • Generate the accepted values string at runtime (e.g. in a lazy_static or OnceLock) from DocumentType::iter().map(|t| t.type_name()) and inject it into the bpaf help string via a custom parser or a help() override
    • This ensures the help text is always an exact and complete reflection of what the parser will accept, with no manual maintenance required

CLI/daemon protocol versioning

  • Description: The CLI has no way to detect that the running daemon was compiled from an older version of the code, leading to opaque "failed to parse daemon response" errors when the protocol changes.
  • Details:
    • Add a protocol version constant (e.g. DAEMON_PROTOCOL_VERSION: u32) to syn-daemon
    • Expose it via a GET /version endpoint (no auth needed, no command dispatch)
    • On startup, ensure_daemon_running (or a new check_daemon_version helper in cli-app/src/client.rs) queries /version and compares against the compiled-in constant
    • If the versions differ, automatically stop the stale daemon and start a fresh one before proceeding with the command
    • The version should be bumped whenever a breaking change is made to DaemonCommand or CommandResponse; a CI lint or build script could enforce this

Unified "waiting on network" indicator

  • Description: Any time the CLI blocks on a network operation it should show a consistent spinner so the user knows the process is alive and waiting, not stuck. The current spinner::with_spinner in the CLI covers the pkarr publish and member fetch calls; the pattern should be applied everywhere a blocking network call occurs.
  • Details:
    • P2P ping dispatched via daemon (syn p2p ping) — the daemon round-trip itself is fast, but the underlying iroh connection attempt can take several seconds
    • Any future syn sync or document-fetch commands
    • Consider whether Android needs an analogous loading indicator in Compose (e.g. a CircularProgressIndicator while fetchEnsembleMembers or startP2PNode are in flight)
    • The spinner style is defined in cli-app/src/spinner.rs; keep all call sites consistent with that helper rather than writing ad-hoc prints

Build & Development

Improve Build Process

  • Description: Streamline the build process between Rust library and Android app
  • Details: Consider using cargo-ndk or similar tools for better Android build integration
  • find a way to make sure typeshare is installed before attempting a build
  • investigate https://github.com/mozilla/uniffi-rs and see if there's any way to use this to make the build process better

Make relay/DNS infrastructure configurable and self-hostable

  • Currently the P2P layer relies entirely on n0's public infrastructure:
    • Relay servers: *.relay.n0.iroh-canary.iroh.link for NAT traversal
    • pkarr DNS: dns.iroh.link for publishing/resolving node addresses by public key
  • Both are open source (iroh-relay, iroh-dns-server) and designed to be self-hosted
  • Options to investigate:
    • Allow configuring custom relay URLs and DNS server in Settings
    • Ship a bundled iroh-relay mode so a node can optionally act as a relay for others
    • Investigate the Mainline DHT path (DhtAddressLookup) as a fully decentralized fallback that requires no trusted server at all. The Mainline DHT is the BitTorrent DHT — ~20M nodes, running continuously since ~2005, maintained "for free" by the existence of BitTorrent. BEP 44 (a BitTorrent extension from ~2014) added support for storing arbitrary signed data in it, not just torrent hashes. pkarr builds on BEP 44: you sign a packet of DNS-like resource records (IP addresses, relay URLs) with your Ed25519 secret key and publish them indexed by your public key. The records are self-authenticating — DHT nodes can't forge them without your key. iroh's PkarrPublisher currently uses dns.iroh.link as an HTTP relay to the DHT (convenient but centralized); DhtAddressLookup participates in the DHT directly with no intermediary.
    • Consider mDNS (address_lookup::mdns) for local-network discovery with no infrastructure dependency

Explore alternatives to JSON for daemon protocol

  • Current JSON over Unix socket is human-readable and easy to debug, but has overhead
  • Consider MessagePack or CBOR for compact binary encoding with the same serde support
  • Consider a typed IDL (Cap'n Proto, Flatbuffers) for schema evolution and zero-copy reads
  • Postcard is a good no-std-friendly option if Android nodes ever speak directly to the daemon

Add Documentation

  • Description: Add comprehensive documentation for the FFI layer and type sharing
  • Details: Document the interface between Rust and Kotlin, especially around serialization formats

Code Editing & Version Control

Implement Code Editing Features

  • Description: Add support for code editing and version control integration
  • Details:
    • Integrate with Radicle DVCS for decentralized version control
    • Add support for both Git and Jujutsu VCS
    • Implement code editing capabilities with syntax highlighting
    • Add diff viewing and conflict resolution tools
    • Support for collaborative editing features

Functionality to add

  • bulk storage backend using s3

Other Agents to Implement

Notes Functionality

Music Functionality

Done

### Switch to cargo-nextest for Rust tests

  • Install cargo-nextest and update the just recipes and CLAUDE.md to use cargo nextest run instead of cargo test
  • nextest offers better output, parallel execution, and per-test timeouts

Audit manual FromStr implementations for strum replacement

  • Description: The HashAlgorithm enum was initially written with a manual FromStr impl that was trivially replaced by strum's EnumString derive. There may be other enums in the codebase with similar unnecessary manual implementations.
  • Details: Search for impl FromStr for across the Rust code and evaluate whether each can be replaced with #[derive(EnumString)] (and Display where applicable).