-
Notifications
You must be signed in to change notification settings - Fork 14
chore(deps): adopt mostro-core 0.13.0 (transport-v2 foundation) #176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| # mostro-cli — Transport v2 (NIP-44 Direct) client support | ||
|
|
||
| **Status:** Phase 1 implemented · Phases 2–3 pending | ||
| **Daemon spec:** `MostroP2P/mostro` → `docs/TRANSPORT_V2_SPEC.md` | ||
| **Issue:** [#626 — Messaging Transport Abstraction Layer](https://github.com/MostroP2P/mostro/issues/626) | ||
| **Core:** `transport` module shipped in **mostro-core 0.13.0** | ||
|
|
||
| This document is the client-side counterpart to the daemon's transport-v2 | ||
| spec. It drives the work of teaching `mostro-cli` to speak protocol **v2** | ||
| (signed kind-`14` events with NIP-44 encrypted content) in addition to | ||
| protocol **v1** (NIP-59 gift wraps, kind `1059`), so the CLI can trade | ||
| against a node running either wire transport — and in particular so we can | ||
| exercise the daemon's Phase 2 anti-spam gates (which only engage on the v2 | ||
| transport). | ||
|
|
||
| ## 1. Why | ||
|
|
||
| The daemon now speaks one of two wire transports per node, selected by its | ||
| `[mostro] transport` setting and advertised on the kind-`38385` instance-info | ||
| event via a `protocol_versions` tag (`"1"` or `"2"`). A v1-only client cannot | ||
| talk to a `transport = "nip44"` node at all (it never sees kind-14 traffic and | ||
| its gift wraps are ignored). To test and use v2 nodes, the CLI must: | ||
|
|
||
| - send protocol messages through the node's transport, and | ||
| - subscribe to / unwrap the matching event kind. | ||
|
|
||
| mostro-core 0.13.0 provides everything needed; the CLI work is wiring. | ||
|
|
||
| ## 2. Wire format recap | ||
|
|
||
| | | v1 (`gift-wrap`) | v2 (`nip44`) | | ||
| |---|---|---| | ||
| | event kind | `1059` (GiftWrap) | `14` (signed, NIP-44 content) | | ||
| | outer author | throwaway ephemeral key | **the trade key** (signature is load-bearing) | | ||
| | inner payload | 2-tuple `(Message, Option<sig>)` | 3-tuple `(Message, Option<sig>, identity-proof?)` | | ||
| | `Message.version` | 2 (since core 0.13) | 2 | | ||
| | expiration | none | NIP-40 `expiration` tag | | ||
|
|
||
| The v2 identity proof lives **inside** the NIP-44 ciphertext (never at the | ||
| event level), bound to the authoring trade key — exactly as private as v1's | ||
| seal-carried identity. mostro-core handles the tuple, the proof, and its | ||
| verification; the CLI only chooses which wrap/unwrap entry point to call. | ||
|
|
||
| > **Note — kind 14 is overloaded.** The CLI already uses kind 14 for NIP-17 | ||
| > peer-to-peer chat (`SendDm` / `dm-to-user`). Protocol-v2 Mostro messages are | ||
| > *also* kind 14 but use mostro-core's `wrap_message_nip44` layout and are | ||
| > authored by / addressed to Mostro. The two are disambiguated on receive by | ||
| > author + `p` tag and by which conversation key decrypts (a non-matching | ||
| > event yields `Ok(None)` from `unwrap_incoming`). Peer chat is out of scope | ||
| > for this effort and stays as-is. | ||
|
|
||
| ## 3. mostro-core 0.13.0 APIs the client uses | ||
|
|
||
| All re-exported from `mostro_core::prelude`: | ||
|
|
||
| - `Transport` — `enum { GiftWrap, Nip44Direct }`; `event_kind() -> Kind` | ||
| (`1059` / `14`), `protocol_version() -> u8` (`1` / `2`), `FromStr`/`Display` | ||
| (`"gift-wrap"` / `"nip44"`), `Default = GiftWrap`. | ||
| - `wrap_message_with(transport, message, identity_keys, trade_keys, receiver, opts) -> Event` | ||
| — send-side dispatcher; routes to gift-wrap or kind-14 wrap. | ||
| - `unwrap_incoming(event, receiver_keys) -> Option<UnwrappedMessage>` | ||
| — receive-side dispatcher; routes on `event.kind`, returns `Ok(None)` for | ||
| "not addressed to me" (decrypt miss), same as the existing `unwrap_message`. | ||
| - `wrap_message` / `unwrap_message` (the v1 pair) keep their 0.11 signatures — | ||
| no change. | ||
|
|
||
| `WrapOptions`, `UnwrappedMessage`, `validate_response`, `Message`, and | ||
| `nip59::RANGE_RANDOM_TIMESTAMP_TWEAK` are unchanged from 0.11.3. | ||
|
|
||
| ## 4. Phases | ||
|
|
||
| ### Phase 1 — Adopt mostro-core 0.13.0 (foundation) — IMPLEMENTED | ||
|
|
||
| Pure dependency bump; **no transport behaviour change** (the CLI still speaks | ||
| gift-wrap). De-risks the major-version jump on its own. | ||
|
|
||
| - `Cargo.toml`: `mostro-core` `0.11.3` → `0.13.0`. | ||
| - The only source break: a non-exhaustive `match` on `order::Status` in | ||
| `src/parser/dms.rs` gains the new `Status::WaitingMakerBond` arm (the bond | ||
| feature's maker-side status; rendered like `WaitingTakerBond`). The new | ||
| `Action`/`Payload` variants (bond + cashu) and `Order`'s new optional Cashu | ||
| fields do not break any call site (existing matches already carry catch-alls; | ||
| `Order` is only deserialized, and the new fields are `Option`). | ||
|
|
||
| Effect: messages now carry `Message.version = 2` (core `PROTOCOL_VER`), still | ||
| inside gift wraps. The daemon dispatches on event **kind**, not the version | ||
| field, and `verify()` validates action↔payload shape (not version), so a 0.13 | ||
| CLI interoperates with a 0.13 daemon over gift-wrap unchanged. (Talking to a | ||
| pre-0.13, version-1 daemon is out of scope — that is the v1-deprecation | ||
| timeline's concern.) | ||
|
|
||
| Acceptance: `cargo build`, `cargo test`, `cargo clippy --all-targets | ||
| --all-features`, `cargo fmt --check` all clean; behaviour identical to before | ||
| against a gift-wrap node. | ||
|
|
||
| ### Phase 2 — Transport selection (v2 capability) — PENDING | ||
|
|
||
| Teach the CLI to send and receive on either transport, selected explicitly. | ||
|
|
||
| - **Config:** a `TRANSPORT` env var / `--transport <gift-wrap|nip44>` flag, | ||
| parsed into `Transport` (default `gift-wrap` — wire-identical to today). | ||
| Mirrors the daemon's `[mostro] transport` knob. Store it on `Context`. | ||
| - **Send:** route the Mostro-protocol path of `send_dm` through | ||
| `wrap_message_with(ctx.transport, …)` instead of the hard-wired | ||
| `wrap_message`. The NIP-17 peer-chat path (`to_user`) is untouched. | ||
| - **Receive:** replace the hard-coded `Kind::GiftWrap` filter in `wait_for_dm` | ||
| (and the notification-loop kind check) with `ctx.transport.event_kind()`. | ||
| For v2, additionally constrain the filter to `author = mostro_pubkey` so the | ||
| Mostro reply is not confused with NIP-17 peer chat on the same kind. | ||
| - **Unwrap:** `parse_dm_events` calls `unwrap_incoming` instead of | ||
| `unwrap_message`, so it transparently handles whichever kind arrived. | ||
| - **Blast radius:** the ~12 command call sites of `send_dm` thread | ||
| `ctx.transport` through; no per-command logic changes. | ||
|
|
||
| Acceptance: against a `transport = "nip44"` daemon, a full | ||
| `new-order → take → add-invoice → fiat-sent → release` round-trips; against a | ||
| gift-wrap daemon, behaviour is unchanged. This is the phase that lets us test | ||
| the daemon's Phase 2 anti-spam gates. | ||
|
Comment on lines
+115
to
+118
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: The Mostro daemon implements anti-spam and message validation through a multi-stage pipeline designed to mitigate denial-of-service (DoS) attacks and ensure protocol integrity [1][2]. While there is no specific architectural component labeled as "Phase 2 anti-spam gates," anti-abuse measures are foundational to the daemon's message processing flow [1]. Anti-spam and validation gates include: 1. Proof-of-Work (PoW): The daemon enforces a PoW difficulty threshold configured in settings.toml [1][3]. Events failing this check are discarded immediately before any expensive processing [1][2]. 2. Event Freshness: Nostr events older than 10 seconds are silently discarded to prevent replay attacks [1]. 3. Trade Index Validation: The check_trade_index function enforces sequential trade index validation for actions, preventing replay attacks and ensuring proper user tracking [1][2]. 4. Silent Rejections: To further mitigate DoS, the daemon uses silent discards for invalid timestamp and non-GiftWrap events, avoiding unnecessary logging or responses [1]. Regarding protocol versions, Mostro v0.13.0, released in January 2025, was a significant milestone that introduced key management [4][5]. This version implemented: - Key Rotation: A mechanism for clients to rotate keys for every trade, adding a privacy layer beyond previous NIP-59 GiftWrap implementations [4][5]. - Core Updates: The release bumped mostro-core to v0.6.25, reflecting these architectural changes [6]. The "Phase 2" nomenclature is often associated with specific feature rollouts in the Mostro development roadmap, such as recent enhancements to the anti-abuse bond system (e.g., solver-directed dispute slashing) [7], rather than a formal anti-spam gate versioning. The anti-abuse bond system itself provides node operators with granular control over trade requirements (e.g., bond_enabled, bond_apply_to, bond_amount_pct) to further gate participation and deter malicious activity [8]. Citations:
🏁 Script executed: # Find and inspect the TRANSPORT_V2_SPEC.md file
find . -name "TRANSPORT_V2_SPEC.md" -type fRepository: MostroP2P/mostro-cli Length of output: 92 🏁 Script executed: # Search for PROTOCOL_VER and related protocol version configuration
rg -i "PROTOCOL_VER|protocol.*version|phase.*2.*anti.spam" --max-count=20Repository: MostroP2P/mostro-cli Length of output: 643 🏁 Script executed: # Search for anti-spam gate implementation in the codebase
rg -i "anti.spam|anti-spam|phase.*2" --type rust --max-count=30Repository: MostroP2P/mostro-cli Length of output: 354 🏁 Script executed: # Read the TRANSPORT_V2_SPEC.md file to understand full context
cat -n ./docs/TRANSPORT_V2_SPEC.md | head -200Repository: MostroP2P/mostro-cli Length of output: 8156 🏁 Script executed: # Get the specific lines mentioned in the review (115-118) and surrounding context
sed -n '100,140p' ./docs/TRANSPORT_V2_SPEC.mdRepository: MostroP2P/mostro-cli Length of output: 2245 Clarify Phase 2 anti-spam gate trigger mechanism in acceptance criteria. The document already states (line 13) that Phase 2 anti-spam gates "only engage on the v2 transport," but lines 115–118 could be clearer about this distinction. Since Phase 1 still uses gift-wrap despite 🤖 Prompt for AI Agents |
||
|
|
||
| ### Phase 3 — Capability auto-detection + docs/UX — PENDING | ||
|
|
||
| - Read the node's `protocol_versions` tag from its kind-`38385` info event | ||
| (same fetch path as the existing `pow` probe) and, when `--transport` is not | ||
| given, auto-select the matching transport — warning on a mismatch | ||
| ("this node speaks v2; re-run with --transport nip44") instead of silently | ||
| timing out. | ||
| - Surface the active transport in verbose output. | ||
| - Update `docs/architecture.md`, `docs/commands.md`, and the README. | ||
|
|
||
| ## 5. Testing notes | ||
|
|
||
| - The daemon under test (`MostroP2P/mostro` PR #780) defaults to | ||
| `transport = "gift-wrap"`; set `transport = "nip44"` in its `settings.toml` | ||
| to exercise v2 + the anti-spam gate. | ||
| - After Phase 2, run the CLI with `--transport nip44` (or `TRANSPORT=nip44`) | ||
| against that node. | ||
| - The daemon's first-contact PoW lane (`pow_first_contact`) is testable by | ||
| combining `--transport nip44` with `--pow <bits>` on the CLI. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
mostro-core 0.13.0 public API wrap_message_nip44💡 Result:
In mostro-core version 0.13.0, there is no public API function named wrap_message_nip44. The project handles messaging primarily through its nip59 transport module, which implements dual-key gift wrapping [1]. Historical documentation and protocol references indicate that NIP-44 encryption is used within the Mostro architecture for private direct messaging and as a component of NIP-59 gift wrapping, rather than being exposed as a top-level wrap_message_nip44 API function [2][3]. For managing wrapped messages in mostro-core, developers typically interact with the
mostro_core::nip59module, which provides functions such aswrap_messageandunwrap_message[1]. These utilities handle the necessary protocol-specific encryption (including NIP-44) and identity/trade key signing [1][2]. If you are looking for specific NIP-44 encryption implementation, it is generally handled by the underlying nostr-sdk crate, which mostro-core depends on and exposes via its internal message-handling pipelines [4][5].Citations:
🏁 Script executed:
Repository: MostroP2P/mostro-cli
Length of output: 92
🏁 Script executed:
Repository: MostroP2P/mostro-cli
Length of output: 165
🏁 Script executed:
Repository: MostroP2P/mostro-cli
Length of output: 5583
🏁 Script executed:
Repository: MostroP2P/mostro-cli
Length of output: 1922
🏁 Script executed:
Repository: MostroP2P/mostro-cli
Length of output: 148
🏁 Script executed:
Repository: MostroP2P/mostro-cli
Length of output: 46
Clarify
wrap_message_nip44reference.Line 46 mentions
wrap_message_nip44layout, but this term does not correspond to a public API in mostro-core 0.13.0. The actual public dispatcher iswrap_message_with(transport, …)(listed in the API section). Replace the informalwrap_message_nip44reference on line 46 with the correct public API name to avoid reader confusion.🤖 Prompt for AI Agents