lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

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.