README.md (15023B)
1 # radroots_event 2 3 This is the README for `radroots_event`, which provides typed `radroots` event 4 models, kinds, and tag conventions for the `radroots` core libraries. 5 6 ## Overview 7 8 * typed content grouped under the singular `admission`, `calendar`, 9 `contract`, `draft`, `envelope`, `farm`, `food`, `id`, `knowledge`, 10 `listing`, `media`, `post`, `profile`, `social`, `tag`, `trade`, and `wire` 11 modules; 12 * shared event references, pointers, and kind and tag definitions used across 13 event-processing code; 14 * portable event model semantics for both `std` and `no_std` builds; 15 * optional integration with `serde` for serialization. 16 17 The crate root is intentionally limited to `Event`, `GenericEventDraft`, 18 `SignedEvent`, `VerifiedEvent`, `EventId`, `EventKind`, `EventTag`, and `Error`. 19 Domain-specific values live under their owning module. Existing focused models 20 are nested below that authority, including `food::availability`, 21 `listing::classified`, `listing::operational`, `post::comment`, 22 `post::deletion`, `post::reply`, `tag::relay_hint`, and the farm and social 23 subdomains. 24 25 ## Features 26 27 The default feature set is `std` plus `serde`. The complete public feature 28 vocabulary is `std`, `serde`, and `knowledge`; features are additive and 29 `knowledge` enables the optional knowledge-event model. Binding generation and 30 fixtures are private build/test concerns rather than public features. 31 Cryptographic verification implementations belong to `radroots_event_codec` 32 or signing crates and enter this crate only through the explicit 33 `admission::SignatureVerifier` host SPI. 34 35 The Profile module exposes only the strict authored and native profile values. It requires a 36 non-whitespace, control-free name; its media fields accept only image-typed, 37 byte-verified Blossom descriptors; and its NIP-05 identifier type validates 38 syntax without making a network identity claim. Tolerant reads use 39 `RadrootsInboundProfileMetadata` from `radroots_event_codec`; the lossy legacy 40 projection is quarantined there as `LegacyProfile` until its mandatory removal 41 in Step 087 and is not part of this crate's public surface. 42 43 The post module exposes only native authored values. New root kind-1 publication uses private-field 44 `AuthoredUpdate`, `AuthoredPhotoUpdate`, and 45 `AuthoredAsk` types. Photo and optional Ask media require nonzero 46 dimensions, bounded alt text, approved same-digest fallbacks, and an 47 image-typed byte-verified Blossom descriptor. That descriptor state is not an 48 upload receipt; BUD-02 completion remains a runtime prerequisite before 49 signing. The mutable legacy read projection is quarantined in 50 `radroots_event_codec` as `LegacyPost` until its mandatory removal in Step 087. 51 52 The shared `tag::relay_hint` module exposes `NostrRelayHint` for NIP-10 Reply 53 and NIP-22 Comment references. It is a byte-stable subset of WebSocket URLs: 54 exact lowercase `ws://` or `wss://`, visible ASCII, canonical lowercase DNS or 55 four-octet IPv4 or bracketed pure-hex RFC 5952 IPv6, canonical optional port 56 `1..65535`, and RFC 3986 path-abempty/query syntax with uppercase `%HH` 57 escapes. It rejects IDNA and percent-encoded hosts, legacy IPv4, userinfo, 58 fragments, controls, backslashes, and normalization-dependent spellings. 59 60 The `post::reply` module exposes opaque `Nip10ReplyReference` and 61 `AuthoredNip10Reply` types for strict direct and nested kind-1 62 authoring. References carry a validated event id, referenced author, and 63 optional shared relay hint; construction emits either one marked root or 64 distinct marked root and parent references. Relay-hint syntax is not a 65 wire-size claim: Reply construction separately enforces the 4,096-byte 66 tag-element ceiling. These values prove syntax and authored shape, not target 67 existence, target kind, signature, author, or relay availability. 68 69 The `post::comment` module implements the strict Radroots 70 [NIP-22](https://github.com/nostr-protocol/nips/blob/bdfa7e62ef87fcfcb992b1a27aee49d36b0b4f91/22.md) 71 kind-`1111` profile. `AuthoredNip22Comment` and its opaque event-root, 72 address-root, parent, position, and root-kind values admit only kind-`30402`, 73 kind-`31922`, or kind-`31923` event or address roots. External `I`/`i` 74 references and kind-`1` roots are unsupported. The authored model has no Serde 75 construction path. 76 77 Canonical authoring emits `E,K,P,e,k,p` for a top-level event root, 78 `A,K,P,a,e,k,p` for a top-level address root, or `E,K,P,e,k,p` and 79 `A,K,P,e,k,p` for nested event and address roots. Event references always 80 contain four elements, including an empty relay position when no hint exists 81 and a final author hint. Address and participant references contain two 82 elements plus an optional relay; an address root's current-revision `e` tag has 83 no author hint. A direct `k` repeats the root kind and a nested `k` is `1111`. 84 85 The event-contract registry v7 classifies `radroots.social.comment.v1` as 86 `TypedOnly` and `AdmissionOnly`; serialized registry versions `1` through `6` 87 are stale. Generic kind-`1111` draft and signing paths cannot claim the typed 88 contract. The Comment resource profile limits content to 131072 UTF-8 bytes, 89 tags to 1024, total tag elements including names to 4096, each element to 4096 90 bytes, aggregate tag bytes to 131072, and compact signed wire JSON to 262144 91 bytes. `RadrootsInboundNip22CommentProjection` and 92 `RadrootsAdmittedNip22CommentEvent` provide verified inbound projection and 93 admission. The three governed Comment operations are 94 `social.comment.build_authored_draft`, 95 `social.comment.project_verified_event`, and 96 `social.comment.verify_and_admit_event`; they and the canonical self-contained 97 114-case corpus are owned by `radroots_event_codec` and 98 `contracts/conformance`. 99 100 The `post::deletion` module implements the effect-free request layer of 101 [NIP-09](https://github.com/nostr-protocol/nips/blob/bdfa7e62ef87fcfcb992b1a27aee49d36b0b4f91/09.md). 102 `AuthoredNip09DeletionRequest` requires at least one validated event-id 103 or replaceable/addressable coordinate target. Event targets carry a 104 caller-asserted kind advisory in `0..=65535`; this is metadata rather than 105 proof of the target event. Address coordinates accept NIP-01 replaceable kinds 106 `0`, `3`, and `10000..=19999` only with an empty identifier, and addressable 107 kinds `30000..=39999` with an opaque identifier. 108 109 Construction canonicalizes event targets by event id, address targets by 110 coordinate, and unique derived kind advisories in ascending order. Duplicate 111 normalized targets are rejected. The request enforces the shared content, tag, 112 element, aggregate-tag-byte, and compact signed-event budgets before it can 113 reach signing. It represents only a kind-`5` protocol request: it does not 114 retrieve a target, prove same-author authority, compute an address cutoff, 115 suppress content, mutate a store, or make a deletion request itself deletable. 116 117 The immutable registry-v7 inventory and addressable-feed-v1 head functions are 118 historical protocol inputs to event-store reconciliation v1. The explicit 119 `event_contract_registry_v7`, `validate_event_contract_registry_v7`, 120 `event_head_candidate_for_nip01_event_v1`, and `select_event_head_v1` 121 entrypoints must retain their v7/v1 behavior when a later current registry or 122 head algorithm is introduced. 123 124 Kind `30402` has a raw, allocation-free marker partition before profile-specific 125 tag-shape validation. Presence of `radroots:price_unit` or `radroots:quantity` selects 126 the focused FoodAvailability marker family; presence of 127 `radroots:primary_bin`, `radroots:bin`, or `radroots:price` selects the richer 128 Operational Listing marker family. Focused-only, operational-only, marker-free 129 generic NIP-99, and mixed-marker events produce 130 `ClassifiedListingPartition::{FocusedFoodAvailability, 131 OperationalListing, GenericNip99, Ambiguous}` respectively. A malformed 132 one-element tag still contributes its raw first name, and marker matching is 133 case-sensitive. `classify_classified_listing_tags` and the borrowed-slice 134 variant inspect neither kind, tag values, nor tag arity. 135 136 The `food::availability` module provides `FoodAvailabilityDetails` and 137 checked identifier, text, publication timestamp, price, currency, unit, 138 quantity, status, image-dimension, and image values. Content contains at least 139 one scalar outside Unicode whitespace and U+001C through U+001F, and is bounded 140 to 131072 UTF-8 bytes. Identifiers reject whitespace plus Unicode control and 141 format characters; title, summary, and location use trimmed, nonempty, 142 control-free text bounded to 4096 UTF-8 bytes. Food units are closed to `g`, 143 `kg`, `lb`, `oz`, `each`, `dozen`, `bunch`, `punnet`, `bag`, and `basket`. 144 Price permits zero; quantity is strictly positive and uses the price unit. 145 Image dimensions use two nonzero canonical `u32` decimal values in 146 `WIDTHxHEIGHT` form. Details accept at most 64 images, require unique image URLs 147 and Blossom digests, and accept only `AuthoredImage` values that already 148 prove local descriptor-to-byte agreement. Details retain nonzero `published_at` 149 and can validate that it is not later than a supplied `created_at`. 150 151 These checked details are the exclusive typed input to the focused 152 `radroots.food.availability.v1` authoring contract. The event-contract registry 153 classifies that kind-`30402` profile as `TypedOnly` and `AdmissionOnly`, so a 154 generic unsigned classified listing cannot claim the focused contract. The 155 details themselves remain domain values rather than signed events; 156 `radroots_event_codec` owns deterministic wire construction, verified inbound 157 projection and admission, and strict revision comparison. 158 159 An authored image proves local descriptor-to-byte agreement only. Successful 160 BUD-02 upload completion and any required raster, retrieval, or availability 161 checks remain runtime responsibilities before signing. Neither this domain 162 module nor the registry signs, publishes, replicates, or retrieves an event. 163 164 The calendar module keeps three different states explicit for NIP-52 kinds 165 `31922`, `31923`, `31924`, and `31925`: the complete structural event envelope, 166 a tolerant baseline NIP-52 projection, and a strict Radroots-admitted 167 projection. The authored types are `AuthoredCalendarDateEvent`, 168 `AuthoredCalendarTimeEvent`, `AuthoredCalendar`, and 169 `AuthoredCalendarEventRsvp`; their inbound counterparts use matching 170 `ParsedNip52*` and `Admitted*` types. Envelope construction 171 validates structure, not a matching event id or Schnorr signature. Callers must 172 perform those cryptographic checks independently and keep any parsed or 173 admitted value bound to the verified envelope and expected kind. 174 175 Baseline projections retain the pinned NIP-52 common fields: repeated 176 locations, participants, categories, absolute-URI references, kind-`31924` 177 calendar-inclusion requests, and deprecated `name` compatibility data in 178 addition to `d`, `title`, description, summary, image, and geohash. Date 179 events use semantic Gregorian dates and retain observed uppercase-`D` tags as 180 uninterpreted extensions. Time events validate unsigned timestamps, exact IANA 181 time-zone identifiers, and at least one in-range `D` day while tolerating the 182 NIP's non-mandatory start-day and complete-coverage forms. An absent `end_tzid` 183 falls back to `start_tzid` when one is present. 184 185 Strict authoring and admission add canonical metadata and bounded resource 186 rules. Authored common fields include repeated locations, participants, 187 categories, references, and calendar-inclusion requests; deprecated `name` is 188 not authored. Strict date events reject uppercase `D`, while strict time 189 events derive or admit only the complete, ascending sequence of UTC-day 190 indices and cover at most 366 days. 191 192 Kind `31924` has one calendar-specific contract. It is not decoded or authored 193 through either generic NIP-51 list codec. Its NIP-52 detailed description is 194 plain-text event content and remains distinct from the optional NIP-51 195 `description` tag. It requires one `d` and one `title`, permits optional NIP-51 196 `description` and `image` tags, and contains zero or more `a` references to 197 kind `31922` or `31923` events. Each reference may have its own relay hint, and 198 an empty calendar collection is valid. 199 200 Kind `31925` has exactly one required `d`, one `a` event coordinate, and one 201 `status`; `e`, `fb`, and `p` are optional singletons. The `a`, `e`, and `p` 202 references preserve independent optional relay hints. The `p` tag is an event 203 author hint without participant-role semantics, and strict admission requires 204 it to match the author in the `a` coordinate. An inbound declined RSVP may 205 retain an observed `fb` for diagnostics but exposes no effective free/busy 206 state. Authored declined RSVPs cannot carry `fb`. 207 208 Strict collection and RSVP identifiers use exactly 22 unpadded base64url 209 characters representing 128 bits. The type proves only syntax; the runtime is 210 responsible for generating a fresh value for each new identity. Parsing or 211 admission does not prove reference existence, revision correspondence, RSVP 212 authority, upload completion, or network availability. 213 214 Inbound images begin as unverified absolute URIs. Strict admission, including 215 kind-`31924` collection images, requires a structural Blossom hash-path URL but 216 makes no byte or network claim. Authored calendar images require the shared 217 `image/*`, byte-verified Blossom descriptor. That state is not an upload 218 receipt: a runtime must require successful BUD-02 upload completion and a 219 bounded retrievability check before signing or publishing a media-bearing 220 event. 221 222 ## Field Event Boundary 223 224 `radroots_event` includes the public event-layer models needed by Field-style 225 farming operations: 226 227 * workspace manifests for discovering the farm group, relay set, media servers, 228 and supported event kinds; 229 * CRDT change envelopes for operation documents such as tasks, work sessions, 230 harvest records, and approvals; 231 * farm file metadata events for media attached to farm documents; 232 * NIP-42 relay auth and NIP-98 HTTP auth payload models; 233 * NIP-29 group metadata, member lists, roles, invites, joins, leaves, and user 234 operations for the supported `9000`, `9001`, `9002`, `9005`, `9007`, `9008`, 235 `9009`, `9021`, `9022`, `39000`, `39001`, `39002`, and `39003` subset. 236 237 The NIP-29 group surface uses bare metadata marker tags such as `private`, 238 `restricted`, `hidden`, and `closed`, `supported_kinds` declarations, and 239 `code` tags for invite and join flows. User management and moderation events 240 preserve optional reason content. LiveKit room metadata and live participant 241 state are not part of this crate's current group event subset. 242 243 Task records, work sessions, harvest records, approvals, and similar Field 244 business objects are CRDT document semantics carried by 245 `FarmCrdtChange`. They are not separate `rr-rs` event families and this 246 crate does not enforce private Field workflow authorization. 247 248 ## Copyright 249 250 Except as otherwise noted, all files in the `radroots_event` distribution are 251 252 Copyright (c) 2025 Tyson Lupul 253 254 For information on usage and redistribution, and for a DISCLAIMER OF ALL 255 WARRANTIES, see LICENSE included in the `radroots_event` distribution.