Skip to content

Add supabase_chat_seal: permissive (MIT) E2EE for closed-source apps - #4

Merged
AndroidPoet merged 10 commits into
mainfrom
feat/permissive-e2ee
Jun 27, 2026
Merged

AndroidPoet merged 10 commits into
mainfrom
feat/permissive-e2ee

Conversation

@AndroidPoet

Copy link
Copy Markdown
Owner

Why

supabase_chat_e2ee gives the strongest crypto (Signal Protocol, forward secrecy) but depends on libsignal_protocol_dart, which is GPL-3.0. Shipping a GPL-3.0 dependency (App Store / Play Store = distribution) forces the entire app open-source — so it can't be used in a closed-source app.

This adds supabase_chat_seal, a permissively licensed alternative with the same verified-E2EE API.

What

  • Sealed box: X25519 ECDH → HKDF-SHA256 → AES-256-GCM, via the Apache-2.0 cryptography package (+ crypto BSD-3 for safety numbers). No copyleft dependencies → safe for closed-source apps.
  • API mirrors the Signal package: SealIdentity, SealManager, SealedChatRoom, PublicKeyDirectory (Supabase + in-memory), TrustStore, 60-digit SafetyNumber.
  • Strict requireVerified mode, IdentityChangedException on a directory key swap (MITM), and sender self-readback via the static pairwise key.
  • SQL migration 0003_e2ee_public_keys.sql.

Trade-off

No forward secrecy (static pairwise key) — documented throughout. The root README now presents both E2EE options side by side, chosen by license.

supabase_chat_seal (new) supabase_chat_e2ee
Crypto ECDH + AES-GCM Signal (X3DH + Double Ratchet)
Forward secrecy ❌ ✅
License MIT (closed-source OK) GPL-3.0 (open-source only)

Verified

  • dart analyze packages → No issues found
  • dart format → 0 of 59 changed
  • 9/9 backend-free round-trip tests pass (handshake, multi-turn, self-readback, strict mode, MITM rejection, identity restore)
  • dart run example/seal_demo.dart → full walkthrough, matching safety numbers, ciphertext-only storage, tamper rejection

The Signal-based supabase_chat_e2ee depends on libsignal_protocol_dart, which
is GPL-3.0 — unusable in a closed-source app (distribution forces the whole app
open-source). This adds a permissively licensed alternative with the same
verified-E2EE API.

- Sealed box over X25519 ECDH -> HKDF-SHA256 -> AES-256-GCM via the Apache-2.0
  cryptography package (+ crypto BSD-3 for safety numbers). No copyleft deps.
- SealIdentity / SealManager / SealedChatRoom mirror the e2ee API surface;
  PublicKeyDirectory (Supabase + in-memory), TrustStore, 60-digit SafetyNumber.
- Strict requireVerified mode, IdentityChangedException on key swap, sender
  self-readback via the static pairwise key.
- 9 backend-free round-trip tests + a runnable console demo (seal_demo.dart).
- SQL migration 0003 (e2ee_public_keys directory).
- Trade-off vs Signal: no forward secrecy. Documented throughout; root README
  now presents both options side by side, chosen by license.
- Prominent GPL-3.0 warning at the top of supabase_chat_e2ee: distributing it
  (incl. app stores) requires open-sourcing the whole app; closed-source apps
  must use supabase_chat_seal instead.
- Root README: a license-compatibility table across all packages showing which
  are closed-source safe and why supabase_chat_e2ee is not.
example/lib/two_users.dart boots two independent anonymous clients into one
shared room and renders both panes in a single window, so presence (online),
typing, send status (sending/sent), and read receipts (unread badge clearing)
are all visible propagating live between the two users. Rewrites the example
README (was the default Flutter placeholder) to document both entrypoints.
example/lib/encrypted_two_users.dart: two users chat end-to-end encrypted via
supabase_chat_seal, and a bottom panel renders the exact ciphertext stored in
messages.encrypted — showing the humans read plaintext while Supabase only ever
holds an opaque blob. Each pane shows the verified safety number, presence, and
typing. Adds supabase_chat_seal to the example deps and documents the demo.
example/lib/encrypted_device.dart: one anonymous user per device, paired with a
one-time ID handshake (create on one, join on the other), chatting E2E-encrypted
via supabase_chat_seal. A toolbar toggle reveals the ciphertext stored in
messages.encrypted. Documents the two-emulator run in the example README.
Bulk inserting room_members with differing key sets (owner row carried
keys the member rows lacked) made PostgREST reject the insert with
PGRST102, breaking every room creation that included members. Build all
rows with the same keys (room_id, user_id, role).
- README: device migration & passphrase-encrypted key-backup recipe
  (the identity key is the master key; no server-side recovery)
- README: forward-secrecy / dynamic-key trade-off vs supabase_chat_e2ee
  (Double Ratchet is the GPL path; static pairwise key here, by design)
- README: 'what E2EE can't do' matrix (server search/restore impossible;
  local plaintext export is fine)
- add macOS platform target so the example runs on desktop
- gitignore Supabase CLI local state (.temp/.branches/.env)
…e2ee GPL-3.0

supabase_chat_ui hard-depended on the GPL supabase_chat_e2ee via
EncryptedChatView, so importing the (MIT) widgets transitively pulled
libsignal and forced GPL on consumers — even for plain, unencrypted chat.

- supabase_chat_ui: drop the e2ee dependency; remove the GPL-coupled
  EncryptedChatView; split out the presentational EncryptedChatBanner
  (no crypto dep, works above either E2EE flavor).
- supabase_chat_e2ee: relicense MIT -> GPL-3.0 so the pub.dev badge
  matches the obligation it links (libsignal is GPL-3.0); ship the
  EncryptedChatView as a copy-paste recipe in its README.
- README/CHANGELOGs: correct the license table (e2ee is GPL-3.0; the
  other four packages are MIT and closed-source-safe).
The pub.dev name supabase_chat_ui is already taken by another author, so
rename our Flutter widgets package to the available supabase_chat_widgets
(dir, pubspec, library file, imports/exports, workspace path, and all docs).
@AndroidPoet
AndroidPoet merged commit 45b75e7 into main Jun 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant