From 0ba82370f1aab4c71819424fc9678dd426749da2 Mon Sep 17 00:00:00 2001 From: randogoth Date: Sat, 26 Sep 2026 22:51:29 +0300 Subject: [PATCH] docs: rewrite README as a brief overview crediting FlashMail origin --- README.md | 60 ++++++++++++++++--------------------------------------- 1 file changed, 17 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 32be7ae..92549c2 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,41 @@ # kirakira -A mobile client for [Smol Mail](../smolmail), the minimalist end-to-end encrypted mail protocol: one Ed25519 identity, five operations, Noise_NX transport, sealed and signed messages. The sibling of the reference CLI client (`../smolmail`) and the browser client (`../gsmol`) — everything they do, in an Android app. +A mobile client for [Smol Mail](https://smol.place), a minimalist end-to-end encrypted mail protocol: one Ed25519 identity, five operations, sealed and signed messages over a Noise_NX transport. Sibling of the reference CLI client (`https://smol.place`) and the browser client (`https://code.randogoth.com/randogoth/gsmol`). -## How it works +kirakira began as [FlashMail](https://github.com/sarthakkimtani/flash-mail), a Flutter UI template for a disposable-email app built on mail.tm. Its networking layer was replaced end to end with a from-scratch Smol Mail implementation (`lib/smol/`: crypto, Noise handshake, framing, client flows), and the UI was redesigned around a Material 3 theme — dark navy and mint green by default, with a matching light theme — built from a single `ColorScheme` and `AppColors` extension rather than hardcoded colors per screen. -``` -app (all crypto, all keys) ⇄ smolmaild (TCP :1961, Noise_NX) -``` +## What it does -There is no bridge and no server-side account: the Noise session, sealing, signing, pinning and trust-on-first-use all run on the device. The seed lives in Hive app storage — the phone's app sandbox is the trust boundary, as with the CLI client's `identity.key` file. - -## First use - -1. Create an identity (or restore from a seed) and back the seed up — it is the only secret. -2. Pin your home server's public key, obtained from the operator through a trusted channel (SPEC.md §4: registration and fetching refuse unpinned servers). -3. Register an address, then fetch. Restoring a seed on a new device? Enter the address alongside the seed — or "Recall" from settings — and it rebinds by RESOLVE + key check, without re-registering. - -## What it implements - -- **Identity** (§2): one 32-byte seed; the X25519 agreement keys are derived from the Ed25519 keypair. Superseded seeds are kept after rotation, since mail sealed to them is readable with nothing else. -- **Addressing** (§3): short `user@host[:1961]` and self-certifying `smol://user@host/key` addresses; base32 fingerprints. -- **Trust** (§4, §8): server keys pinned explicitly; mismatch aborts the handshake; registration and fetching require a pin. -- **Mail** (§5): sealed and signed envelopes with 1 KiB padding, flat frontmatter bodies, sent copies sealed to self. Fetch verifies id and signature before acknowledging — anything unreadable stays on the server. -- **Contacts**: outgoing mail carries a signed `Reply-To` field with the sender's full `smol://` address (an "anonymous" switch omits it); a first-contact claim binds only when its key matches the message's signer, and never over an address already pinned to a different key. The contacts screen shows each bound key with its trust badge and the keys it displaced — the only local record that a contact rotated — and a re-resolve applies §8: a chain-validated rotation is accepted and announced, an unexplained key change is refused until verified out of band. Trust warnings (unpinned sessions, rotations) persist on screen until dismissed, as the spec's §4/§8 messages demand. -- **Rotation** (§7): rotate with a signed certificate; contacts accept the change from the chain. -- **Backup**: settings can export the inbox, sent mail, contacts and server pins to one shareable file (and import it back) — the same JSON shape as gsmol's export, so backups move between the two clients. It never contains the seed, which has its own reveal-and-copy flow. Import never overwrites a pin or contact that already differs locally; malformed entries are skipped and counted. +- Create or restore an identity from a 32-byte seed — that's the only secret. +- Pin a server's key, register or recall an address, then fetch and send sealed mail. +- Trust-on-first-use for server and contact keys, with rotation support and on-screen warnings for anything unverified. +- Export/import inbox, sent mail, contacts and server pins as one shareable backup file — never the seed. ## Layout ``` -lib/smol/crypto.dart SHA-2, HKDF, ChaCha20-Poly1305, X25519, Ed25519, §2 conversions -lib/smol/noise.dart Noise_NX_25519_ChaChaPoly_SHA256 initiator -lib/smol/proto.dart addresses, seal/unseal, frontmatter, rotation, op framing -lib/smol/transport.dart TCP byte pipe (dart:io) -lib/smol/store.dart Hive: identity, pins, contacts, sealed mail, export/import -lib/smol/config.dart deploy-time preset server pin (--dart-define) -lib/smol/client.dart connect/fetch/send/register/rotate flows -lib/presentation/ the UI (Riverpod + auto_route) -test/smol_test.dart byte-exact vectors + protocol cases -test/store_test.dart contact key history, import -test/widget_test.dart UI smoke test -test/e2e_test.dart live round-trip against smolmaild +lib/smol/ the protocol: crypto, Noise handshake, framing, client flows +lib/presentation/ screens, widgets and theme (Riverpod + auto_route) +lib/data/ Riverpod providers +test/ protocol vectors, store tests, a live e2e round-trip ``` -The crypto is pure Dart, mirroring gsmol's dependency-free modules, so `test/vectors.json` — generated from the reference stack (PyNaCl, noiseprotocol) by `../gsmol/test/gen_vectors.py` — pins every operation byte for byte. - ## Run and verify ``` devbox run analyze devbox run test +flutter run ``` -Then `flutter run` with a device or emulator attached. `test/e2e_test.dart` additionally runs a live round-trip (register, send to self, fetch, unseal, drain) against `../smolmail/smolmaild.py` on `127.0.0.1:1961`, and self-skips when no server is listening. - -Not verified here: clicking through the app on a real device — the logic under every button is what the tests exercise, as with gsmol. +`test/e2e_test.dart` runs a live round-trip against `https://smol.place/smolmaild.py` on `127.0.0.1:1961` and skips itself when nothing's listening there. ## Notes and limits -- No server push or notifications in v1 (SPEC.md §13): tap fetch. -- The seed sits in app storage: a compromised device is game over, same as a stolen `identity.key` file for the CLI client. -- Rotation is not revocation (§7): a stolen key can rotate onward and the chain validates. The settings screen surfaces rotations instead of applying them invisibly; out-of-band re-verification is the only defence. +- No server push in v1 — fetch is manual. +- The seed lives in app storage; a compromised device is a compromised identity. +- Rotation isn't revocation — a stolen key can still rotate onward. Settings surfaces rotations for out-of-band verification rather than applying them silently. ## License -Distributed under the MIT License; see LICENSE.md. +MIT — see LICENSE.md.