Skip to content

Ports

The engine depends on small interfaces ("ports"); the other packages provide the production implementations ("adapters"). Everything here is on package:passnote/engine.dart, the advanced library. Swap any of them through the builder or passnoteLowLevel.

Transport

Discovers peers and moves opaque, already-encrypted frames.

abstract interface class Transport {
  Stream<PeerEvent> get events;     // PeerConnected / PeerLost
  Stream<InboundFrame> get inbound; // frames arriving from peers
  Future<void> start({required NodeId self, required String appId});
  Future<void> send(PeerId to, Frame frame);
  Future<void> stop();
}

Shipped: LanTransport, WebSocketTransport (see Transports), and the in-memory pair InMemoryBus/InMemoryTransport for tests.

CrdtTable

The record store beneath a collection: put, read, merge, watch, query, digest. Implementations must satisfy runCrdtTableConformance (and runCrdtTypeConformance), the behavioural contracts shipped in test_support/. Shipped: SqliteCrdtTable (disk, the default; see SQLite schema) and MapCrdtTable (in-memory, for tests).

Collections wrap any engine in TypedCrdtTable, the layer that gives OrSet and PnCounter fields their merge rules; an engine only stores records and merges last-write-wins.

Cipher

Authenticated encryption, built per key.

abstract interface class Cipher {
  Future<List<int>> encrypt(List<int> plaintext);
  Future<List<int>> decrypt(List<int> ciphertext); // throws CipherAuthError
}

Shipped: XChaChaPoly1305Cipher (default) and AesGcmCipher in passnote_crypto. Decrypt must fail closed on any tampered input.

KeyStore

Persists derived keys and the device identity blob.

abstract interface class KeyStore {
  Future<List<int>?> getKey(String id);
  Future<void> setKey(String id, List<int> key);
  Future<void> clear(String id);
}

Shipped: KeychainKeyStore (OS keychain, in passnote_flutter).

Identity

The device keypair: wrap and unwrap keys for peers, sign and verify roster rows, fingerprint. verify checks any device's signature against its public signing key; the device roster uses it to reject rows that are unsigned or signed by a non-member. Shipped: DeviceIdentity (X25519 + Ed25519) in passnote_crypto, including DeviceIdentity.fromRecoveryPhrase for the recovery flow.

Pake

The password-authenticated exchange behind pairing codes. Shipped: Spake2Pake (SPAKE2 over P-256, RFC 9382) in passnote_crypto.

MergeStrategy

How a document decomposes into records, and each field's CrdtType:

abstract interface class MergeStrategy {
  Iterable<FieldSpec> fields(Map<String, dynamic> document);
  Map<String, dynamic> unflatten(Iterable<FieldValue> rows);
  CrdtType typeAt(String path);
}

Shipped: ObjectMergeStrategy, ShallowMergeStrategy, DeepMergeStrategy, SchemaMergeStrategy. See Data model.

Codec

Your type to a stored map and back, plus the document id:

abstract interface class Codec<T> {
  Map<String, dynamic> toMap(T value);
  T fromMap(Map<String, dynamic> map);
  String idOf(T value);
}

Generate one with @Document() and passnote_generator, or hand-write a JsonCodec.