Skip to main content

Decision register

These decisions are binding for the current implementation. A change requires an architecture update, capability-ledger update, interoperability evidence, and the relevant cross-language tests.

Distribution and ownership

  1. One implementation. Rust is the semantic source of truth. Kotlin, Swift, TypeScript, and Android are idiomatic facades, not independent protocol or cryptographic implementations.
  2. Canonical packages. Rust is softchat; Kotlin/JVM is com.softcose:softchat; Android is com.softcose:softchat-android; Swift is SoftchatKit; JavaScript is @softcose/softchat.
  3. Generated bindings are internal. Consumers cannot import UniFFI/JNA, UniFFI Swift/C, or raw wasm-bindgen APIs.
  4. Platform ownership. Rust owns protocol, validation, cryptography, projections, relay state, Noise, reconciliation, and request plans. Platforms own databases, sockets, HTTP, files, background work, lifecycle, secure authority, and UI.

Identity and security

  1. Identity lifecycle. Import secret bytes once into a session-scoped opaque identity, clear the caller buffer, use automatic language ownership, and remove it only on logout or authority loss. Native Rust drops the identity; foreign opaque identities expose idempotent erase().
  2. Stable security boundary. Bound input before expensive work. Errors and diagnostics are stable and redacted; plaintext, keys, ciphertext, and cryptographic-oracle detail must not escape.
  3. NIP-44 compatibility. Writers remain compatible with released clients; readers accept legacy and current extended-length encodings. Retain the dual dependency until one maintained implementation meets all compatibility requirements.

Events and private envelopes

  1. NIP-01. Preserve exact raw tag arrays, require canonical hexadecimal values, recompute the event ID before signature verification, use caller-supplied timestamps, and enforce portable timestamp and 512-KiB bounds.
  2. NIP-59. Use one fresh wrapper key per recipient copy, exactly one recipient p tag, empty seal tags, and independently randomized seal and wrapper timestamps within the previous two days. Authenticate every layer before exposing a rumor. Kind 1059 is durable; 21059 is ephemeral.
  3. NIP-17/chat identity. Authenticated rumors remain the identity-preserving source. Typed one-to-one and group views never replace or rewrite raw tags.

Softchat event profile

  1. The canonical subject tag is subject.
  2. Icons temporarily emit matching imeta and image.
  3. Replies use marked e; forwards use q.
  4. Legacy unmarked Android reply tags remain reader compatibility.
  5. NIP-18 kind 16 is receive-only and is not a Softchat forward.
  6. Edits replace text and emoji only, with author validation, deterministic ordering, and deletion precedence.
  7. User metadata is a private kind-0 rumor retaining unknown JSON.
  8. Follow lists are private author-copy kind-3 events.
  9. App-data sync is signed, self-encrypted kind 30079.
  10. Attachment metadata uses the bounded Softchat imeta union; waveform data remains local.

Relay, synchronization, and media

  1. Relay batches. EVENTS is flat, non-empty, bounded, and unique by event ID. Every event receives its own terminal OK; partial acceptance is preserved.
  2. Relay sessions. The host drives a serialized state machine. Receiving bytes does not mean persistence; sending bytes does not mean delivery. Confirm durable ingestion explicitly and persist publish intent before sending.
  3. Noise and synchronization. Use the exact deployed Noise_NK_25519_ChaChaPoly_SHA256 profile. Rust owns authenticated state and chunking; the platform owns WebSockets. Negentropy operates on bounded snapshots and never owns the database.
  4. Attachments. Stream bounded chunks into private staging. Decrypted output is untrusted until final authentication succeeds. Rust produces NIP-98 plans; the host executes HTTP and handles redirects.

Performance and documentation

  1. Performance evidence. Use frozen historical baselines, three independent optimized host runs, non-debuggable AndroidX Benchmark runs, and descriptive results until runner/device calibration exists. Documentation-only changes do not rerun benchmarks.
  2. Unified documentation. Conceptual and cookbook content has one source with a persistent Rust/Kotlin/Android/Swift/TypeScript selector. Generated symbol references stay language-native. Performance audit reports remain standalone Markdown and measured operations receive contextual cards.
  3. Current-main site. softchat-sdk.jackl.dev publishes the current main revision without analytics, cookies, or external search. Version selection is deferred until a stable release exists.

Evidence

The machine-readable capability ledger maps every feature to implementation and tests. The full historical reasoning remains in the Phase 2 plan and accepted architecture pages in the source repository.