From 6c840f03524187d1f056fdaa70e5f1f9b24cf793 Mon Sep 17 00:00:00 2001 From: bndw Date: Fri, 13 Feb 2026 17:35:32 -0800 Subject: feat: add Protocol Buffer definitions and build tooling --- proto/nostr/v1/nostr.proto | 183 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 proto/nostr/v1/nostr.proto (limited to 'proto/nostr') diff --git a/proto/nostr/v1/nostr.proto b/proto/nostr/v1/nostr.proto new file mode 100644 index 0000000..7e8eacb --- /dev/null +++ b/proto/nostr/v1/nostr.proto @@ -0,0 +1,183 @@ +syntax = "proto3"; + +package nostr.v1; + +option go_package = "northwest.io/nostr-grpc/api/nostr/v1;nostrv1"; + +// Core Nostr event as defined in NIP-01 +message Event { + string id = 1; + string pubkey = 2; + int64 created_at = 3; + int32 kind = 4; + repeated Tag tags = 5; + string content = 6; + string sig = 7; + + // Optional: only populated if client requests verification + // Contains exact canonical JSON bytes that were signed + optional bytes canonical_json = 8; +} + +// Tag is an array of strings (e.g., ["e", "event_id", "relay_url"]) +message Tag { + repeated string values = 1; +} + +// Nostr filter for querying events (REQ) +message Filter { + repeated string ids = 1; + repeated string authors = 2; // pubkeys + repeated int32 kinds = 3; + repeated string e_tags = 4; // #e tag values + repeated string p_tags = 5; // #p tag values + optional int64 since = 6; + optional int64 until = 7; + optional int32 limit = 8; + + // Extension: support for arbitrary tag filters + map tag_filters = 9; +} + +// Filter for arbitrary tags +message TagFilter { + repeated string values = 1; +} + +// Request to publish a single event +message PublishEventRequest { + Event event = 1; +} + +// Response after publishing an event +message PublishEventResponse { + bool accepted = 1; + string message = 2; // Error message or "duplicate" or "success" + + // Always include canonical JSON so client can verify + // what the relay stored + bytes canonical_json = 3; +} + +// Request to subscribe to events matching filters +message SubscribeRequest { + repeated Filter filters = 1; + + // If true, include canonical_json in streamed Event messages + // Allows client-side signature verification + // Default: false (most clients trust the relay) + bool include_canonical_json = 2; + + // Optional client-provided subscription ID for tracking + string subscription_id = 3; +} + +// Request to unsubscribe from an active subscription +message UnsubscribeRequest { + string subscription_id = 1; +} + +// Batch publish request +message PublishBatchRequest { + repeated Event events = 1; +} + +// Batch publish response +message PublishBatchResponse { + repeated PublishEventResponse results = 1; +} + +// Paginated query request +message QueryRequest { + repeated Filter filters = 1; + bool include_canonical_json = 2; + + // Pagination + string cursor = 3; // Opaque cursor from previous response + int32 page_size = 4; // Default: 100 +} + +// Paginated query response +message QueryResponse { + repeated Event events = 1; + string next_cursor = 2; // Empty if no more results + int32 total_count = 3; // Optional: total matching events +} + +// Event count request (NIP-45) +message CountRequest { + repeated Filter filters = 1; +} + +// Event count response +message CountResponse { + int64 count = 1; +} + +// Empty message +message Empty {} + +// Main relay service +service NostrRelay { + // Publish a single event + rpc PublishEvent(PublishEventRequest) returns (PublishEventResponse); + + // Subscribe to events matching filters (streaming) + rpc Subscribe(SubscribeRequest) returns (stream Event); + + // Unsubscribe from an active subscription + rpc Unsubscribe(UnsubscribeRequest) returns (Empty); + + // gRPC-specific: batch publish + rpc PublishBatch(PublishBatchRequest) returns (PublishBatchResponse); + + // gRPC-specific: paginated query (non-streaming) + rpc QueryEvents(QueryRequest) returns (QueryResponse); + + // Event counts (NIP-45) + rpc CountEvents(CountRequest) returns (CountResponse); +} + +// Admin service (optional, secured separately) +service RelayAdmin { + rpc GetStats(Empty) returns (RelayStats); + rpc GetConnections(Empty) returns (ConnectionList); + rpc BanPublicKey(BanRequest) returns (Empty); + rpc GetStorageInfo(Empty) returns (StorageStats); +} + +// Relay statistics +message RelayStats { + int64 total_events = 1; + int64 total_subscriptions = 2; + int64 connected_clients = 3; + int64 events_per_second = 4; + int64 uptime_seconds = 5; +} + +// List of active connections +message ConnectionList { + repeated Connection connections = 1; +} + +// Single connection info +message Connection { + string client_id = 1; + string protocol = 2; // "websocket" or "grpc" + int64 connected_at = 3; + int32 active_subscriptions = 4; +} + +// Request to ban a public key +message BanRequest { + string pubkey = 1; + int64 until = 2; // Unix timestamp, 0 for permanent + string reason = 3; +} + +// Storage statistics +message StorageStats { + int64 total_bytes = 1; + int64 total_events = 2; + int64 db_size_bytes = 3; +} -- cgit v1.2.3