Skip to content

Provide an SDK for easy third-party integration #89

Description

@asherp

Summary

Today the core nostr-mail capabilities (NIP-44/NIP-04 encryption, hybrid attachment encryption, Nostr key/profile/DM handling, SMTP/IMAP email delivery, and the glossia wire format) live inside the Tauri backend crate (tauri-app/backend, lib name nostr_mail_lib) and are tightly coupled to the desktop app. There is no clean, documented way for another application to reuse this logic.

We should extract and package the core functionality as a standalone SDK so developers can integrate nostr-mail-style encrypted email into their own apps without depending on the full Tauri desktop application.

Motivation

  • Reuse: The encryption + Nostr key registry bridge + glossia wire format is the valuable, reusable part of nostr-mail. Other clients (web, mobile, bots, server-side integrations) should be able to send/receive nostr-mail-encrypted messages.
  • Adoption: A documented SDK lowers the barrier for the ecosystem to adopt the nostr-mail message format, which is the project's stated goal of bridging Nostr and email for the average person.
  • Maintainability: Forcing a clean SDK boundary separates core protocol logic from app/UI/storage concerns, making the core easier to test and evolve.

Proposed scope

Expose the core building blocks behind a stable, well-documented API:

  • Crypto: derive shared secret from a sender private key + receiver public key; encrypt/decrypt with NIP-44 (default) and NIP-04 (legacy); hybrid encryption for attachments (AES-256 for files, NIP-44 for keys); sign/verify.
  • Nostr: keypair generation/import (nsec/npub), profile (kind 0) fetch/publish, contact/follow-list discovery, encrypted DMs, relay management.
  • Glossia encoding (wire format): encode/decode of NIP-04/NIP-44 ciphertext as natural-language prose (Latin, Bip39-english, …) carried as ordinary text/plain. This is what gives nostr-mail emails a wire format that survives replies, forwards, and inline quoting, and is what lets subject lines be encrypted (the subject is glossia-encoded ciphertext, not armor). The SDK should expose:
    • encode/decode (transcode) ciphertext ↔ glossia prose, with selectable dialect/wordlist;
    • dialect detection (detect_dialect_best) and raw base-N encode/decode;
    • subject-line encrypt/decode helpers;
    • the Cap'n Proto armor/structure (tauri-app/backend/schema/nostr_mail.capnp, nostr_mail_capnp.rs) that frames body / signature / seal blocks and supports nested quoted blocks so each quoted section in a reply chain remains independently decodable/verifiable.
    • Existing surface to draw from: the Tauri commands glossia_transcode, glossia_detect_dialect, glossia_encode_raw_base_n, glossia_decode_raw_base_n, glossia_get_default_wordlist, and per-field encoding settings (glossia_encoding_body / _signature / _pubkey).
  • Email: compose, encrypt (incl. glossia-encoded subject + body), and send via SMTP; fetch, de-glossia, and decrypt via IMAP; parse the nostr-mail message format including nested armor in reply/forward chains.
  • Message format: a documented, versioned spec for how an encrypted nostr-mail email is structured on the wire (glossia prose + Cap'n Proto armor + signature/seal nesting), so non-Rust clients can interoperate. See docs/nostr-mail-spec.md and docs/glossia-design.md.

Out of scope for the first cut: UI, local SQLite caching, and app-specific settings — those stay in the Tauri app and consume the SDK.

Note on glossia packaging

Glossia currently lives in a separate git submodule (asherp/glossia). The SDK should depend on it as a normal crate/dependency (and ship the wordlist data) rather than via the desktop app's submodule wiring. Decide whether glossia is published as its own crate the SDK depends on, or vendored into the SDK.

Possible approaches

  1. Rust crate — refactor the core out of nostr_mail_lib into a dependency-free (no Tauri) nostr-mail-core / nostr-mail-sdk crate published to crates.io, depending on the glossia crate. The Tauri app depends on it. Lowest-effort, highest-fidelity reuse of existing code.
  2. WASM bindings — compile the Rust core to WebAssembly with a JS/TS wrapper so web and Node integrations can use it directly. (Note: a prior ~38MB glossia WASM blob + wasm-pack hook were removed; revisit size/strategy for the WASM target.)
  3. Language bindings — expose the core via FFI / a thin C ABI (the crate already builds staticlib/cdylib) for use from other languages, or via uniffi for Swift/Kotlin/Python.

A reasonable path: start with (1) to establish a clean core crate + glossia integration + message-format spec, then layer (2)/(3) on top.

Acceptance criteria

  • Core encryption / Nostr / email / glossia logic extracted into a Tauri-independent SDK package.
  • SDK exposes glossia encode/decode (with dialect selection + detection), encrypted-subject helpers, and Cap'n Proto armor framing with nested-quote support.
  • Documented, versioned nostr-mail message-format spec covering glossia prose, the Cap'n Proto armor, and signature/seal nesting across reply/forward chains.
  • Public API documentation with usage examples (encrypt+glossia-encode+send, fetch+de-glossia+decrypt, encrypted subject round-trip, key/profile management).
  • Tests covering encrypt/decrypt round-trips, glossia transcode round-trips, encrypted-subject round-trips, and reply/forward chain survival (nested quoted glossia decodes independently).
  • The existing Tauri app builds on top of the SDK (no duplicated core logic).
  • At least one published/consumable artifact (e.g. crates.io crate and/or npm WASM package).

Open questions

  • Which target(s) to prioritize first: Rust crate, WASM/JS, or mobile bindings?
  • Glossia packaging: publish asherp/glossia as its own crate the SDK depends on, or vendor it? How are wordlists shipped, and how is WASM size handled?
  • Crate/package naming and versioning relative to the app (currently 1.0.8).
  • How much of relay/IMAP transport belongs in the SDK vs. left to the integrator.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions