diff --git a/BENCHMARK.md b/BENCHMARK.md index 4fe4590..26a53ed 100644 --- a/BENCHMARK.md +++ b/BENCHMARK.md @@ -1,6 +1,6 @@ Running 2 comparison run(s)... Versions: - parrhesia 0.2.0 + parrhesia 0.3.0 strfry 1.0.4 (nixpkgs) nostr-rs-relay 0.9.0 nostr-bench 0.4.0 @@ -16,18 +16,18 @@ Versions: === Bench comparison (averages) === metric parrhesia strfry nostr-rs-relay strfry/parrhesia nostr-rs/parrhesia -------------------------- --------- -------- -------------- ---------------- ------------------ -connect avg latency (ms) ↓ 11.50 3.50 2.50 0.30x 0.22x -connect max latency (ms) ↓ 20.00 5.50 3.50 0.28x 0.17x -echo throughput (TPS) ↑ 81805.50 62033.50 162281.50 0.76x 1.98x -echo throughput (MiB/s) ↑ 44.75 34.65 88.90 0.77x 1.99x -event throughput (TPS) ↑ 1524.50 3518.00 782.50 2.31x 0.51x -event throughput (MiB/s) ↑ 1.00 2.25 0.50 2.25x 0.50x -req throughput (TPS) ↑ 2539.00 1809.00 847.00 0.71x 0.33x -req throughput (MiB/s) ↑ 12.45 11.70 2.35 0.94x 0.19x +connect avg latency (ms) ↓ 13.50 3.00 2.00 0.22x 0.15x +connect max latency (ms) ↓ 22.50 5.50 3.00 0.24x 0.13x +echo throughput (TPS) ↑ 80385.00 61673.00 164516.00 0.77x 2.05x +echo throughput (MiB/s) ↑ 44.00 34.45 90.10 0.78x 2.05x +event throughput (TPS) ↑ 2000.00 3404.50 788.00 1.70x 0.39x +event throughput (MiB/s) ↑ 1.30 2.20 0.50 1.69x 0.38x +req throughput (TPS) ↑ 3664.00 1808.50 877.50 0.49x 0.24x +req throughput (MiB/s) ↑ 20.75 11.75 2.45 0.57x 0.12x Legend: ↑ higher is better, ↓ lower is better. Ratio columns are server/parrhesia (for ↓ metrics, <1.00x means that server is faster). Run details: - run 1: parrhesia(echo_tps=80431, event_tps=1427, req_tps=2546, connect_avg_ms=13) | strfry(echo_tps=61421, event_tps=3581, req_tps=1811, connect_avg_ms=3) | nostr-rs-relay(echo_tps=167436, event_tps=792, req_tps=897, connect_avg_ms=3) - run 2: parrhesia(echo_tps=83180, event_tps=1622, req_tps=2532, connect_avg_ms=10) | strfry(echo_tps=62646, event_tps=3455, req_tps=1807, connect_avg_ms=4) | nostr-rs-relay(echo_tps=157127, event_tps=773, req_tps=797, connect_avg_ms=2) + run 1: parrhesia(echo_tps=81402, event_tps=1979, req_tps=3639, connect_avg_ms=14) | strfry(echo_tps=61745, event_tps=3457, req_tps=1818, connect_avg_ms=3) | nostr-rs-relay(echo_tps=159974, event_tps=784, req_tps=905, connect_avg_ms=2) + run 2: parrhesia(echo_tps=79368, event_tps=2021, req_tps=3689, connect_avg_ms=13) | strfry(echo_tps=61601, event_tps=3352, req_tps=1799, connect_avg_ms=3) | nostr-rs-relay(echo_tps=169058, event_tps=792, req_tps=850, connect_avg_ms=2) diff --git a/PROGRESS.md b/PROGRESS.md deleted file mode 100644 index 4ba3292..0000000 --- a/PROGRESS.md +++ /dev/null @@ -1,86 +0,0 @@ -# PROGRESS (ephemeral) - -Implementation checklist for Parrhesia relay. - -## Phase 0 — foundation - -- [x] Confirm architecture doc with final NIP scope (`docs/ARCH.md`) -- [x] Add core deps (websocket/http server, ecto_sql/postgrex, telemetry, test tooling) -- [x] Establish application config structure (limits, policies, feature flags) -- [x] Wire initial supervision tree skeleton - -## Phase 1 — protocol core (NIP-01) - -- [x] Implement websocket endpoint + per-connection process -- [x] Implement message decode/encode for `EVENT`, `REQ`, `CLOSE` -- [x] Implement strict event validation (`id`, `sig`, shape, timestamps) -- [x] Implement filter evaluation engine (AND/OR semantics) -- [x] Implement subscription lifecycle + `EOSE` behavior -- [x] Implement canonical `OK`, `NOTICE`, `CLOSED` responses + prefixes - -## Phase 2 — storage boundary + postgres adapter - -- [x] Define `Parrhesia.Storage.*` behaviors (events/moderation/groups/admin) -- [x] Implement Postgres adapter modules behind behaviors -- [x] Create migrations for events, tags, moderation, membership -- [x] Implement replaceable/addressable semantics at storage layer -- [x] Add adapter contract test suite - -## Phase 3 — fanout + performance primitives - -- [x] Build ETS-backed subscription index -- [x] Implement candidate narrowing by kind/author/tag -- [x] Add bounded outbound queues/backpressure per connection -- [x] Add telemetry for ingest/query/fanout latency + queue depth - -## Phase 4 — relay metadata and auth - -- [x] NIP-11 endpoint (`application/nostr+json`) -- [x] NIP-42 challenge/auth flow -- [x] Enforce NIP-70 protected events (default reject, auth override) -- [x] Add auth-required/restricted response paths for writes and reqs - -## Phase 5 — lifecycle and moderation features - -- [x] NIP-09 deletion requests -- [x] NIP-40 expiration handling + purge worker -- [x] NIP-62 vanish requests (hard delete semantics) -- [x] NIP-13 PoW gate (configurable minimum) -- [x] Moderation tables + policy hooks (ban/allow/event/ip) - -## Phase 6 — query extensions - -- [x] NIP-45 `COUNT` (exact) -- [x] Optional HLL response support -- [x] NIP-50 search (`search` filter + ranking) -- [x] NIP-77 negentropy (`NEG-OPEN/MSG/CLOSE`) - -## Phase 7 — private messaging, groups, and MLS - -- [x] NIP-17/59 recipient-protected giftwrap read path (`kind:1059`) -- [x] NIP-29 group event policy + relay metadata events -- [x] NIP-43 membership request flow (`28934/28935/28936`, `8000/8001`, `13534`) -- [x] Marmot MIP relay surface: `443`, `445`, `10051` handling -- [x] MLS retention policy + tests for commit race edge cases - -## Phase 8 — management API + operations - -- [x] NIP-86 HTTP management endpoint -- [x] NIP-98 auth validation for management calls -- [x] Implement supported management methods + audit logging -- [x] Build health/readiness and Prometheus-compatible `/metrics` endpoints - -## Phase 9 — full test + hardening pass - -- [x] Unit + integration + property test coverage for all critical modules -- [x] End-to-end websocket conformance scenarios -- [x] Load/soak tests with target p95 latency budgets -- [x] Fault-injection tests (DB outages, high churn, restart recovery) -- [x] Final precommit run and fix all issues - -## Nice-to-have / backlog - -- [x] Multi-node fanout via PG LISTEN/NOTIFY or external bus -- [x] Partitioned event storage + archival strategy -- [x] Alternate storage adapter prototype (non-Postgres) -- [x] Compatibility mode for Marmot protocol transition (not required per user) diff --git a/PROGRESS_MARMOT.md b/PROGRESS_MARMOT.md deleted file mode 100644 index 3d4d88a..0000000 --- a/PROGRESS_MARMOT.md +++ /dev/null @@ -1,61 +0,0 @@ -# PROGRESS_MARMOT (ephemeral) - -Marmot-specific implementation checklist for Parrhesia relay interoperability. - -Spec source: `~/marmot/README.md` + MIP-00..05. - -## M0 — spec lock + interoperability profile - -- [ ] Freeze target profile to MIP-00..03 (mandatory) -- [ ] Keep MIP-04 and MIP-05 behind feature flags (optional) -- [ ] Document that legacy NIP-EE is superseded and no dedicated transition compatibility mode is planned -- [ ] Publish operator-facing compatibility statement in docs - -## M1 — MIP-00 (credentials + keypackages) - -- [x] Enforce kind `443` required tags and encoding (`encoding=base64`) -- [x] Validate `mls_protocol_version`, `mls_ciphersuite`, `mls_extensions`, `relays`, and `i` tag shape -- [x] Add efficient `#i` query/index path for KeyPackageRef lookup -- [x] Keep replaceable behavior for kind `10051` relay-list events -- [x] Add conformance tests for valid/invalid KeyPackage envelopes - -## M2 — MIP-01 (group construction data expectations) - -- [x] Enforce relay-side routing prerequisites for Marmot groups (`#h` query path) -- [x] Keep deterministic ordering for group-event catch-up (`created_at` + `id` tie-break) -- [x] Add guardrails for group metadata traffic volume and filter windows -- [x] Add tests for `#h` routing and ordering invariants - -## M3 — MIP-02 (welcome events) - -- [x] Support wrapped Welcome delivery via NIP-59 (`1059`) recipient-gated reads -- [x] Validate relay behavior for unsigned inner Welcome semantics (kind `444` envelope expectations) -- [x] Ensure durability/ack semantics support Commit-then-Welcome sequencing requirements -- [x] Add negative tests for malformed wrapped Welcome payloads - -## M4 — MIP-03 (group events) - -- [x] Enforce kind `445` envelope validation (`#h` tag presence/shape, base64 content shape) -- [x] Keep relay MLS-agnostic (no MLS decrypt/inspect in relay hot path) -- [x] Add configurable retention policy for kind `445` traffic -- [x] Add tests for high-volume fanout behavior and deterministic query results - -## M5 — optional MIP-04 (encrypted media) - -- [x] Accept/store MIP-04 metadata-bearing events as regular Nostr events -- [x] Add policy hooks for media metadata limits and abuse controls -- [x] Add tests for search/filter interactions with media metadata tags - -## M6 — optional MIP-05 (push notifications) - -- [x] Accept/store notification coordination events required by enabled profile -- [x] Add policy/rate-limit controls for push-related event traffic -- [x] Add abuse and replay protection tests for notification trigger paths - -## M7 — hardening + operations - -- [x] Add Marmot-focused telemetry breakdowns (ingest/query/fanout, queue pressure) -- [x] Add query-plan regression checks for `#h` and `#i` heavy workloads -- [x] Add fault-injection scenarios for relay outage/reordering behavior in group flows -- [x] Add docs for operator limits tuned for Marmot traffic patterns -- [x] Final `mix precommit` before merge diff --git a/default.nix b/default.nix index 45de1e6..a26f427 100644 --- a/default.nix +++ b/default.nix @@ -10,7 +10,7 @@ vips, }: let pname = "parrhesia"; - version = "0.2.0"; + version = "0.3.0"; beamPackages = beam.packages.erlang_28.extend ( final: _prev: { @@ -48,7 +48,7 @@ beamPackages.fetchMixDeps { pname = "${pname}-mix-deps"; inherit version src; - hash = "sha256-1v2+Q1MHbu09r5OBaLehiR+JfMP0Q5OHaWuwrQDzZJU="; + hash = "sha256-0KOyYRbYM0jjmp3tPn64qkp0YkmZKlqkGrlu/wCr4m8="; } else null; diff --git a/mix.exs b/mix.exs index 6456e10..0e867cd 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Parrhesia.MixProject do def project do [ app: :parrhesia, - version: "0.2.0", + version: "0.3.0", elixir: "~> 1.19", start_permanent: Mix.env() == :prod, deps: deps(),