Skip to main content

contextvm_sdk/core/
constants.rs

1//! ContextVM protocol constants
2//!
3//! Event kinds and tag names matching the ContextVM specification.
4//! See: <https://contextvm.org>
5
6/// ContextVM messages (ephemeral events, kind 25910)
7pub const CTXVM_MESSAGES_KIND: u16 = 25910;
8
9/// Encrypted messages using NIP-59 Gift Wrap (kind 1059)
10pub const GIFT_WRAP_KIND: u16 = 1059;
11
12/// Ephemeral variant of NIP-59 Gift Wrap (kind 21059, CEP-19)
13///
14/// Same structure and semantics as kind 1059, but in NIP-01's ephemeral range.
15/// Relays are not expected to store ephemeral events beyond transient forwarding.
16pub const EPHEMERAL_GIFT_WRAP_KIND: u16 = 21059;
17
18/// Replaceable relay list metadata event following NIP-65 (CEP-17)
19pub const RELAY_LIST_METADATA_KIND: u16 = 10002;
20
21/// Server announcement (addressable, kind 11316)
22pub const SERVER_ANNOUNCEMENT_KIND: u16 = 11316;
23
24/// Tools list (addressable, kind 11317)
25pub const TOOLS_LIST_KIND: u16 = 11317;
26
27/// Resources list (addressable, kind 11318)
28pub const RESOURCES_LIST_KIND: u16 = 11318;
29
30/// Resource templates list (addressable, kind 11319)
31pub const RESOURCETEMPLATES_LIST_KIND: u16 = 11319;
32
33/// Prompts list (addressable, kind 11320)
34pub const PROMPTS_LIST_KIND: u16 = 11320;
35
36/// Nostr tag constants
37pub mod tags {
38    /// Public key tag
39    pub const PUBKEY: &str = "p";
40
41    /// Relay URL tag (CEP-17)
42    pub const RELAY: &str = "r";
43
44    /// Event ID tag for correlation
45    pub const EVENT_ID: &str = "e";
46
47    /// Capability tag for pricing metadata
48    pub const CAPABILITY: &str = "cap";
49
50    /// Name tag for server announcements
51    pub const NAME: &str = "name";
52
53    /// Website tag for server announcements
54    pub const WEBSITE: &str = "website";
55
56    /// Picture tag for server announcements
57    pub const PICTURE: &str = "picture";
58
59    /// About tag for server announcements
60    pub const ABOUT: &str = "about";
61
62    /// Support encryption tag
63    pub const SUPPORT_ENCRYPTION: &str = "support_encryption";
64
65    /// Support ephemeral gift wrap kind (21059) for encrypted messages (CEP-19)
66    pub const SUPPORT_ENCRYPTION_EPHEMERAL: &str = "support_encryption_ephemeral";
67
68    /// Support CEP-22 oversized payload transfer via notifications/progress framing
69    pub const SUPPORT_OVERSIZED_TRANSFER: &str = "support_oversized_transfer";
70}
71
72/// Maximum message size (1MB)
73pub const MAX_MESSAGE_SIZE: usize = 1024 * 1024;
74
75/// Default LRU cache size for deduplication
76pub const DEFAULT_LRU_SIZE: usize = 5000;
77
78/// Default timeout for network/relay operations (30 seconds)
79pub const DEFAULT_TIMEOUT_MS: u64 = 30_000;
80
81/// Default relay targets for discoverability publication (CEP-17).
82///
83/// These are used as additional publication targets for server metadata,
84/// even when they are not part of the server's operational relay list.
85pub const DEFAULT_BOOTSTRAP_RELAY_URLS: &[&str] = &[
86    "wss://relay.damus.io",
87    "wss://relay.primal.net",
88    "wss://nos.lol",
89    "wss://relay.snort.social/",
90    "wss://nostr.mom/",
91    "wss://nostr.oxtr.dev/",
92];
93
94/// MCP protocol method for the initialization request
95pub const INITIALIZE_METHOD: &str = "initialize";
96
97/// MCP protocol method for the initialized notification
98pub const NOTIFICATIONS_INITIALIZED_METHOD: &str = "notifications/initialized";
99
100/// Kinds that should never be encrypted (public announcements)
101pub const UNENCRYPTED_KINDS: &[u16] = &[
102    SERVER_ANNOUNCEMENT_KIND,
103    TOOLS_LIST_KIND,
104    RESOURCES_LIST_KIND,
105    RESOURCETEMPLATES_LIST_KIND,
106    PROMPTS_LIST_KIND,
107];
108
109#[cfg(feature = "rmcp")]
110pub fn mcp_protocol_version() -> &'static str {
111    use std::sync::OnceLock;
112    static VERSION: OnceLock<String> = OnceLock::new();
113    VERSION
114        .get_or_init(|| rmcp::model::ProtocolVersion::LATEST.to_string())
115        .as_str()
116}
117
118#[cfg(not(feature = "rmcp"))]
119pub const fn mcp_protocol_version() -> &'static str {
120    "2025-11-25"
121}
122
123// Compile-time range checks (NIP-01 kind ranges).
124// Placed at module level so violations are caught in every build, not just `cargo test`.
125const _: () = {
126    // Ephemeral events: 20000 <= kind < 30000
127    assert!(EPHEMERAL_GIFT_WRAP_KIND >= 20000);
128    assert!(EPHEMERAL_GIFT_WRAP_KIND < 30000);
129    assert!(CTXVM_MESSAGES_KIND >= 20000);
130    assert!(CTXVM_MESSAGES_KIND < 30000);
131    // Replaceable events: 10000 <= kind < 20000
132    assert!(RELAY_LIST_METADATA_KIND >= 10000);
133    assert!(RELAY_LIST_METADATA_KIND < 20000);
134};
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[test]
141    fn test_event_kind_values_match_spec() {
142        assert_eq!(CTXVM_MESSAGES_KIND, 25910);
143        assert_eq!(GIFT_WRAP_KIND, 1059);
144        assert_eq!(EPHEMERAL_GIFT_WRAP_KIND, 21059);
145        assert_eq!(RELAY_LIST_METADATA_KIND, 10002);
146        assert_eq!(SERVER_ANNOUNCEMENT_KIND, 11316);
147        assert_eq!(TOOLS_LIST_KIND, 11317);
148        assert_eq!(RESOURCES_LIST_KIND, 11318);
149        assert_eq!(RESOURCETEMPLATES_LIST_KIND, 11319);
150        assert_eq!(PROMPTS_LIST_KIND, 11320);
151    }
152
153    #[test]
154    fn test_tag_values_match_ts_sdk() {
155        assert_eq!(tags::PUBKEY, "p");
156        assert_eq!(tags::RELAY, "r");
157        assert_eq!(tags::EVENT_ID, "e");
158        assert_eq!(tags::CAPABILITY, "cap");
159        assert_eq!(tags::NAME, "name");
160        assert_eq!(tags::WEBSITE, "website");
161        assert_eq!(tags::PICTURE, "picture");
162        assert_eq!(tags::ABOUT, "about");
163        assert_eq!(tags::SUPPORT_ENCRYPTION, "support_encryption");
164        assert_eq!(
165            tags::SUPPORT_ENCRYPTION_EPHEMERAL,
166            "support_encryption_ephemeral"
167        );
168        assert_eq!(
169            tags::SUPPORT_OVERSIZED_TRANSFER,
170            "support_oversized_transfer"
171        );
172    }
173
174    #[test]
175    fn test_announcement_kinds_in_addressable_range() {
176        // NIP-01: addressable events are 30000 <= kind < 40000
177        // However, the spec uses 11316-11320 which are in the replaceable range.
178        // These are parameterized replaceable events per the ContextVM spec.
179        for &kind in UNENCRYPTED_KINDS {
180            assert!(kind >= 11316);
181            assert!(kind <= 11320);
182        }
183    }
184
185    #[test]
186    fn test_bootstrap_relays_are_wss() {
187        for url in DEFAULT_BOOTSTRAP_RELAY_URLS {
188            assert!(
189                url.starts_with("wss://"),
190                "Bootstrap relay must use wss: {url}"
191            );
192        }
193    }
194
195    #[test]
196    fn test_bootstrap_relays_nonempty() {
197        assert!(
198            !DEFAULT_BOOTSTRAP_RELAY_URLS.is_empty(),
199            "Must have at least one bootstrap relay"
200        );
201    }
202
203    #[test]
204    fn test_mcp_method_constants() {
205        assert_eq!(INITIALIZE_METHOD, "initialize");
206        assert_eq!(
207            NOTIFICATIONS_INITIALIZED_METHOD,
208            "notifications/initialized"
209        );
210    }
211
212    #[test]
213    fn test_unencrypted_kinds_contains_all_announcements() {
214        assert!(UNENCRYPTED_KINDS.contains(&SERVER_ANNOUNCEMENT_KIND));
215        assert!(UNENCRYPTED_KINDS.contains(&TOOLS_LIST_KIND));
216        assert!(UNENCRYPTED_KINDS.contains(&RESOURCES_LIST_KIND));
217        assert!(UNENCRYPTED_KINDS.contains(&RESOURCETEMPLATES_LIST_KIND));
218        assert!(UNENCRYPTED_KINDS.contains(&PROMPTS_LIST_KIND));
219    }
220
221    #[test]
222    fn test_gift_wrap_not_in_unencrypted() {
223        assert!(!UNENCRYPTED_KINDS.contains(&GIFT_WRAP_KIND));
224        assert!(!UNENCRYPTED_KINDS.contains(&EPHEMERAL_GIFT_WRAP_KIND));
225    }
226}