Crypto / protocol
- v4 envelope: X25519 + ML-KEM-768 hybrid (PQXDH-style HKDF over both
secrets). Heartbeats now carry an ML-KEM key; senders emit v4 whenever
the peer's heartbeat has one, v3 otherwise. Adds @noble/post-quantum.
- Heartbeats are signed by the KEZ identity and verified against the
recipient's primary before use. Previously a spoofed nostr pubkey
(from a re-wrapped replayed envelope) let an attacker substitute their
own heartbeat and read messages.
- Heartbeat retention counts from supersession, never deletes the
newest key, and GC runs after rotation — returning after a long
offline stretch no longer destroys the key the backlog needs.
- Verified-heartbeat cache per peer: 5-min TTL (one lookup per file
instead of per chunk) and anti-downgrade to identity ECDH.
- Always republish heartbeat on session start; rotate on the poll timer;
atomic IDB updates; wipe heartbeats on "Forget this account".
- Dedupe inbound envelopes by sender_sig (replays / re-broadcasts).
Server
- Proxy /v1/sigchains/* to the internal sig-server and hand out the
public https://<server>/v1/sigchains/... URL. Sigchain sync from the
app was failing ("Load failed") against http://sig-server:7878.
App
- Relay settings apply to profile / NIP-65 publishing (republished on
change); wss-only relays (ws:// for localhost); order-aware defaults.
- Clear chat also deletes attachments; retention changes apply
immediately with a warning when shortening.
- Photos: HEIC -> JPEG and downscale to 2048px (iOS canvas limit, strips
EXIF); snapshot the conversation at send time.
- Overlay a11y/Escape fixes, scroll-pin fix, svelte-check to 0 errors.
- Clear chat, image zoom, file-body fallback rendering, NIP-65 read
relays in publish set, relay settings UI, forward-secrecy settings.
Docs
- README rewritten to explain the whole app; SECURITY.md updated for v4;
heartbeat self-check (src/lib/heartbeat.selfcheck.ts).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
42 lines
1.6 KiB
TOML
42 lines
1.6 KiB
TOML
[package]
|
|
name = "kez-chat-server"
|
|
version = "0.1.0"
|
|
edition = "2024"
|
|
license = "MIT OR Apache-2.0"
|
|
description = "Home server for kez-chat: handle registry + NATS auth callout + WebFinger + static SPA host. Designed in kez-chat/document.md."
|
|
|
|
[dependencies]
|
|
anyhow = "1"
|
|
axum = "0.7"
|
|
chrono = { version = "0.4", features = ["serde"] }
|
|
clap = { version = "4.5", features = ["derive", "env"] }
|
|
hex = "0.4"
|
|
kez-core = { path = "../rust/crates/kez-core" }
|
|
rusqlite = { version = "0.32", features = ["bundled"] }
|
|
serde = { version = "1", features = ["derive"] }
|
|
serde_json = "1"
|
|
thiserror = "2"
|
|
tokio = { version = "1.48", features = ["macros", "rt-multi-thread", "sync", "signal"] }
|
|
tokio-stream = { version = "0.1", features = ["sync"] }
|
|
futures = "0.3"
|
|
web-push = "0.10"
|
|
base64 = "0.22"
|
|
p256 = { version = "0.13", features = ["pem"] }
|
|
rand = "0.8"
|
|
# Server-side nostr listener — subscribes to relays for every handle
|
|
# registered on this chat-server and fires Web Push when a kez-DM
|
|
# event for one of them lands. The web client publishes the same
|
|
# events; this is the missing link that lets push notifications work
|
|
# when chat goes over nostr instead of /v1/messages.
|
|
nostr-sdk = { version = "0.36", default-features = false, features = ["all-nips"] }
|
|
hkdf = "0.12"
|
|
sha2 = "0.10"
|
|
# Proxies /v1/sigchains/* to the internal sig-server (api.rs).
|
|
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json"] }
|
|
tower-http = { version = "0.6", features = ["trace", "cors", "fs"] }
|
|
tracing = "0.1"
|
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|
|
|
[dev-dependencies]
|
|
tempfile = "3"
|