lib

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

registry_v7.rs (150335B)


      1 #![forbid(unsafe_code)]
      2 
      3 //! Frozen event-contract registry-v7 inventory and validation semantics.
      4 
      5 #[cfg(not(feature = "std"))]
      6 use alloc::{borrow::ToOwned, string::String, vec::Vec};
      7 
      8 use crate::{
      9     calendar::{
     10         CalendarDate, CalendarUid, CalendarUri, IanaTimeZoneId, RADROOTS_CALENDAR_MAX_PARTICIPANTS,
     11         canonical_calendar_geohash_is_valid, canonical_calendar_tag_text_is_valid,
     12         covered_utc_days,
     13     },
     14     envelope::EventEnvelope,
     15     envelope::kind::*,
     16     id::{
     17         AddressableCoordinate, DTag, EventId, MutationId, Nip01Coordinate, parse_public_key,
     18         relay_url_is_valid,
     19     },
     20     listing::classified::{
     21         ClassifiedListingPartition, TAG_RADROOTS_PRICE_UNIT, TAG_RADROOTS_QUANTITY,
     22     },
     23 };
     24 use radroots_blossom::BlobUrl;
     25 
     26 pub const RADROOTS_EVENT_CONTRACT_REGISTRY_VERSION: u32 = 7;
     27 
     28 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
     29 pub enum EventClass {
     30     Regular,
     31     Replaceable,
     32     Addressable,
     33     Ephemeral,
     34 }
     35 
     36 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
     37 pub enum NostrStandard {
     38     Nip01,
     39     Nip09,
     40     Nip17,
     41     Nip18,
     42     Nip22,
     43     Nip23,
     44     Nip25,
     45     Nip28,
     46     Nip29,
     47     Nip42,
     48     Nip51,
     49     Nip52,
     50     Nip53,
     51     Nip54,
     52     Nip56,
     53     Nip57,
     54     Nip78,
     55     Nip90,
     56     Nip94,
     57     Nip98,
     58     Nip99,
     59     Radroots,
     60 }
     61 
     62 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
     63 pub enum EventPrivacy {
     64     Public,
     65     Encrypted,
     66     LocalOnly,
     67     Secret,
     68 }
     69 
     70 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
     71 pub enum EventStability {
     72     Stable,
     73     Experimental,
     74 }
     75 
     76 /// Event-contract role required of an author.
     77 ///
     78 /// This is event authoring policy, not signer provenance, authentication
     79 /// state, account persistence, or proof that a host granted the role.
     80 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
     81 pub enum AuthorRole {
     82     Any,
     83     Application,
     84     Buyer,
     85     Farmer,
     86     Member,
     87     Moderator,
     88     Relay,
     89     Seller,
     90     Service,
     91 }
     92 
     93 impl AuthorRole {
     94     pub const ALL: [Self; 9] = [
     95         Self::Any,
     96         Self::Application,
     97         Self::Buyer,
     98         Self::Farmer,
     99         Self::Member,
    100         Self::Moderator,
    101         Self::Relay,
    102         Self::Seller,
    103         Self::Service,
    104     ];
    105 
    106     /// Stable registry label used by versioned manifest encoders.
    107     #[must_use]
    108     pub const fn as_str(self) -> &'static str {
    109         match self {
    110             Self::Any => "any",
    111             Self::Application => "application",
    112             Self::Buyer => "buyer",
    113             Self::Farmer => "farmer",
    114             Self::Member => "member",
    115             Self::Moderator => "moderator",
    116             Self::Relay => "relay",
    117             Self::Seller => "seller",
    118             Self::Service => "service",
    119         }
    120     }
    121 }
    122 
    123 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    124 pub enum Reducer {
    125     CalendarProjection,
    126     FarmOpsProjection,
    127     GroupProjection,
    128     KnowledgeProjection,
    129     OperationalListingInventoryAccounting,
    130     OperationalListingProjection,
    131     MarketProjection,
    132     OrderProjection,
    133     ProfileProjection,
    134     NostrRelayPolicyProjection,
    135     SocialProjection,
    136     TradeProjection,
    137     TradeValidation,
    138 }
    139 
    140 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    141 pub enum ContentSchema {
    142     Empty,
    143     JsonObject,
    144     PlainText,
    145     Markdown,
    146     Djot,
    147     Encrypted,
    148     BinaryReference,
    149 }
    150 
    151 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    152 pub enum TagCardinality {
    153     RequiredOne,
    154     OptionalOne,
    155     OptionalMany,
    156     RequiredMany,
    157 }
    158 
    159 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    160 pub enum TagSemantic {
    161     AddressableCoordinate,
    162     CalendarEventAuthor,
    163     CalendarEventReference,
    164     CalendarEventRevision,
    165     CalendarInclusionRequest,
    166     CalendarEnd,
    167     CalendarStart,
    168     Category,
    169     Citation,
    170     Contract,
    171     Counterparty,
    172     Evidence,
    173     EventPointer,
    174     FreeBusy,
    175     Geohash,
    176     GroupId,
    177     Identifier,
    178     Image,
    179     Kind,
    180     ClassifiedListingAddress,
    181     OperationalListingSnapshot,
    182     ListDescription,
    183     Location,
    184     Nip01Coordinate,
    185     Participant,
    186     PreviousEvent,
    187     Price,
    188     PublishedAt,
    189     Relay,
    190     Reference,
    191     ReviewTarget,
    192     RootEvent,
    193     ServiceInput,
    194     ServiceOutput,
    195     Source,
    196     Status,
    197     Summary,
    198     Title,
    199     Topic,
    200     TimeZone,
    201     TradeMutation,
    202     Url,
    203     UtcDayCoverage,
    204 }
    205 
    206 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    207 pub enum TagValueType {
    208     AddressableCoordinate,
    209     CalendarDate,
    210     CalendarEventCoordinate,
    211     CalendarFreeBusy,
    212     CalendarRsvpStatus,
    213     CalendarUid,
    214     ContractId,
    215     DTag,
    216     EventId,
    217     EventPointer,
    218     Geohash,
    219     IanaTimeZoneId,
    220     Kind,
    221     MutationId,
    222     Nip01Coordinate,
    223     PublicKey,
    224     RelayUrl,
    225     Sha256,
    226     Text,
    227     UnixTimestamp,
    228     Uri,
    229     Url,
    230     UtcDayIndex,
    231     Uuid,
    232 }
    233 
    234 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    235 pub struct TagContract {
    236     pub name: &'static str,
    237     pub cardinality: TagCardinality,
    238     pub semantic: TagSemantic,
    239     pub value_type: TagValueType,
    240     pub relay_indexed: bool,
    241 }
    242 
    243 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    244 pub enum EventDiscriminator {
    245     KindOnly,
    246     /// Exact profile selection is owned by a verified admission algorithm.
    247     AdmissionOnly,
    248     /// Exact NIP-99 profile selection uses the central raw marker partition.
    249     ClassifiedListingPartition(ClassifiedListingPartition),
    250     DTagExact(&'static str),
    251     DTagPrefix(&'static str),
    252     DTagSuffix(&'static str),
    253     TagEquals {
    254         name: &'static str,
    255         value: &'static str,
    256     },
    257     ContentJsonFieldEquals {
    258         field: &'static str,
    259         value: &'static str,
    260     },
    261     EnvelopeType(&'static str),
    262     Composite(&'static [EventDiscriminator]),
    263 }
    264 
    265 /// Governs whether a contract may enter the generic frozen-draft boundary.
    266 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    267 pub enum EventAuthoringPolicy {
    268     /// Generic contract validation is sufficient to construct a frozen draft.
    269     GenericDraft,
    270     /// Authoring requires a sealed typed API instead of generic draft parts.
    271     TypedOnly,
    272     /// The contract is an inbound/read boundary and cannot be authored.
    273     ReadOnly,
    274 }
    275 
    276 impl EventAuthoringPolicy {
    277     /// Returns whether untyped contract parts may construct a frozen draft.
    278     #[must_use]
    279     pub const fn permits_generic_draft(self) -> bool {
    280         matches!(self, Self::GenericDraft)
    281     }
    282 
    283     /// Returns whether a sealed typed authoring API may construct an event.
    284     #[must_use]
    285     pub const fn permits_typed_authoring(self) -> bool {
    286         !matches!(self, Self::ReadOnly)
    287     }
    288 
    289     /// Returns whether the contract is exclusively an inbound/read boundary.
    290     #[must_use]
    291     pub const fn is_read_only(self) -> bool {
    292         matches!(self, Self::ReadOnly)
    293     }
    294 }
    295 
    296 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    297 pub enum ContractMatchError {
    298     UnsupportedKind(u32),
    299     UnsupportedShape(u32),
    300     AmbiguousShape(u32),
    301 }
    302 
    303 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    304 pub enum ContractFamily {
    305     Account,
    306     Application,
    307     Calendar,
    308     Farm,
    309     Group,
    310     Http,
    311     Job,
    312     Knowledge,
    313     List,
    314     Market,
    315     Message,
    316     Profile,
    317     Relay,
    318     Social,
    319     Trade,
    320 }
    321 
    322 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    323 pub struct ContractFamilyMetadata {
    324     pub family: ContractFamily,
    325     pub id: &'static str,
    326     pub name: &'static str,
    327 }
    328 
    329 #[derive(Clone, Debug, PartialEq, Eq)]
    330 pub enum ContractValidationError {
    331     UnknownContract {
    332         contract_id: String,
    333     },
    334     AdmissionRequired {
    335         contract_id: &'static str,
    336     },
    337     ContractMatch {
    338         error: ContractMatchError,
    339     },
    340     KindMismatch {
    341         expected: u32,
    342         actual: u32,
    343     },
    344     ContentMustBeEmpty {
    345         contract_id: &'static str,
    346     },
    347     InvalidJsonContent {
    348         contract_id: &'static str,
    349     },
    350     MissingTag {
    351         contract_id: &'static str,
    352         name: &'static str,
    353     },
    354     TagCardinalityMismatch {
    355         contract_id: &'static str,
    356         name: &'static str,
    357     },
    358     TagValueMismatch {
    359         contract_id: &'static str,
    360         name: &'static str,
    361         expected: String,
    362         actual: Option<String>,
    363     },
    364     MissingContentField {
    365         contract_id: &'static str,
    366         field: &'static str,
    367     },
    368     ContentFieldMismatch {
    369         contract_id: &'static str,
    370         field: &'static str,
    371         expected: String,
    372     },
    373     ForbiddenContentField {
    374         contract_id: &'static str,
    375         field: &'static str,
    376     },
    377 }
    378 
    379 impl ContractValidationError {
    380     pub const fn code(&self) -> &'static str {
    381         match self {
    382             Self::UnknownContract { .. } => "unknown_contract",
    383             Self::AdmissionRequired { .. } => "admission_required",
    384             Self::ContractMatch { .. } => "contract_match",
    385             Self::KindMismatch { .. } => "kind_mismatch",
    386             Self::ContentMustBeEmpty { .. } => "content_must_be_empty",
    387             Self::InvalidJsonContent { .. } => "invalid_json_content",
    388             Self::MissingTag { .. } => "missing_tag",
    389             Self::TagCardinalityMismatch { .. } => "tag_cardinality_mismatch",
    390             Self::TagValueMismatch { .. } => "tag_value_mismatch",
    391             Self::MissingContentField { .. } => "missing_content_field",
    392             Self::ContentFieldMismatch { .. } => "content_field_mismatch",
    393             Self::ForbiddenContentField { .. } => "forbidden_content_field",
    394         }
    395     }
    396 }
    397 
    398 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    399 pub struct KindContract {
    400     pub kind: u32,
    401     pub canonical_constant: &'static str,
    402     pub name: &'static str,
    403     pub class: EventClass,
    404     pub standard: NostrStandard,
    405     pub accepted_event_contracts: &'static [&'static str],
    406 }
    407 
    408 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    409 pub struct EventContract {
    410     pub id: &'static str,
    411     pub kind: u32,
    412     pub name: &'static str,
    413     pub payload_type: &'static str,
    414     pub class: EventClass,
    415     pub stability: EventStability,
    416     pub privacy: EventPrivacy,
    417     required_author_role: AuthorRole,
    418     pub content_schema: ContentSchema,
    419     authoring_policy: EventAuthoringPolicy,
    420     pub discriminator: EventDiscriminator,
    421     pub tags: &'static [TagContract],
    422     pub reducers: &'static [Reducer],
    423 }
    424 
    425 impl EventContract {
    426     /// Returns the event-authoring role required by this contract.
    427     ///
    428     /// Hosts decide whether a concrete signer or actor has this role; the
    429     /// event package only owns the contract requirement.
    430     #[must_use]
    431     pub const fn required_author_role(&self) -> AuthorRole {
    432         self.required_author_role
    433     }
    434 
    435     /// Returns the single registry-owned policy governing authoring routes.
    436     #[must_use]
    437     pub const fn authoring_policy(&self) -> EventAuthoringPolicy {
    438         self.authoring_policy
    439     }
    440 }
    441 
    442 static CONTRACT_FAMILIES: &[ContractFamilyMetadata] = &[
    443     ContractFamilyMetadata {
    444         family: ContractFamily::Account,
    445         id: "account",
    446         name: "Account",
    447     },
    448     ContractFamilyMetadata {
    449         family: ContractFamily::Application,
    450         id: "application",
    451         name: "Application",
    452     },
    453     ContractFamilyMetadata {
    454         family: ContractFamily::Calendar,
    455         id: "calendar",
    456         name: "Calendar",
    457     },
    458     ContractFamilyMetadata {
    459         family: ContractFamily::Farm,
    460         id: "farm",
    461         name: "Farm",
    462     },
    463     ContractFamilyMetadata {
    464         family: ContractFamily::Group,
    465         id: "group",
    466         name: "Group",
    467     },
    468     ContractFamilyMetadata {
    469         family: ContractFamily::Http,
    470         id: "http",
    471         name: "HTTP",
    472     },
    473     ContractFamilyMetadata {
    474         family: ContractFamily::Job,
    475         id: "job",
    476         name: "Job",
    477     },
    478     ContractFamilyMetadata {
    479         family: ContractFamily::Knowledge,
    480         id: "knowledge",
    481         name: "Knowledge",
    482     },
    483     ContractFamilyMetadata {
    484         family: ContractFamily::List,
    485         id: "list",
    486         name: "List",
    487     },
    488     ContractFamilyMetadata {
    489         family: ContractFamily::Market,
    490         id: "market",
    491         name: "Market",
    492     },
    493     ContractFamilyMetadata {
    494         family: ContractFamily::Message,
    495         id: "message",
    496         name: "Message",
    497     },
    498     ContractFamilyMetadata {
    499         family: ContractFamily::Profile,
    500         id: "profile",
    501         name: "Profile",
    502     },
    503     ContractFamilyMetadata {
    504         family: ContractFamily::Relay,
    505         id: "relay",
    506         name: "Relay",
    507     },
    508     ContractFamilyMetadata {
    509         family: ContractFamily::Social,
    510         id: "social",
    511         name: "Social",
    512     },
    513     ContractFamilyMetadata {
    514         family: ContractFamily::Trade,
    515         id: "trade",
    516         name: "Trade",
    517     },
    518 ];
    519 
    520 const fn tag(
    521     name: &'static str,
    522     cardinality: TagCardinality,
    523     semantic: TagSemantic,
    524     value_type: TagValueType,
    525     relay_indexed: bool,
    526 ) -> TagContract {
    527     TagContract {
    528         name,
    529         cardinality,
    530         semantic,
    531         value_type,
    532         relay_indexed,
    533     }
    534 }
    535 
    536 const TAG_D: TagContract = tag(
    537     "d",
    538     TagCardinality::RequiredOne,
    539     TagSemantic::Identifier,
    540     TagValueType::DTag,
    541     true,
    542 );
    543 const TAG_P_REQUIRED: TagContract = tag(
    544     "p",
    545     TagCardinality::RequiredOne,
    546     TagSemantic::Counterparty,
    547     TagValueType::PublicKey,
    548     true,
    549 );
    550 const TAG_P_MANY: TagContract = tag(
    551     "p",
    552     TagCardinality::OptionalMany,
    553     TagSemantic::Counterparty,
    554     TagValueType::PublicKey,
    555     true,
    556 );
    557 const TAG_P_TRADE_PARTIES: TagContract = tag(
    558     "p",
    559     TagCardinality::RequiredMany,
    560     TagSemantic::Participant,
    561     TagValueType::PublicKey,
    562     true,
    563 );
    564 const TAG_X_TRADE_MUTATIONS: TagContract = tag(
    565     "x",
    566     TagCardinality::RequiredMany,
    567     TagSemantic::TradeMutation,
    568     TagValueType::MutationId,
    569     true,
    570 );
    571 const TAG_X_RHI_ATTESTATION_REFERENCES: TagContract = tag(
    572     "x",
    573     TagCardinality::RequiredMany,
    574     TagSemantic::Reference,
    575     TagValueType::Sha256,
    576     true,
    577 );
    578 const TAG_T_RHI_ATTESTATION_OUTCOME: TagContract = tag(
    579     "t",
    580     TagCardinality::RequiredOne,
    581     TagSemantic::Status,
    582     TagValueType::Text,
    583     true,
    584 );
    585 const TAG_E_RHI_SUPERSEDES_EVENT: TagContract = tag(
    586     "e",
    587     TagCardinality::OptionalOne,
    588     TagSemantic::PreviousEvent,
    589     TagValueType::EventId,
    590     true,
    591 );
    592 const TAG_CALENDAR_PARTICIPANT: TagContract = tag(
    593     "p",
    594     TagCardinality::OptionalMany,
    595     TagSemantic::Participant,
    596     TagValueType::PublicKey,
    597     true,
    598 );
    599 const TAG_A_ADDRESS_REQUIRED: TagContract = tag(
    600     "a",
    601     TagCardinality::RequiredOne,
    602     TagSemantic::AddressableCoordinate,
    603     TagValueType::AddressableCoordinate,
    604     true,
    605 );
    606 const TAG_A_OPTIONAL: TagContract = tag(
    607     "a",
    608     TagCardinality::OptionalOne,
    609     TagSemantic::AddressableCoordinate,
    610     TagValueType::AddressableCoordinate,
    611     true,
    612 );
    613 const TAG_A_MANY: TagContract = tag(
    614     "a",
    615     TagCardinality::OptionalMany,
    616     TagSemantic::AddressableCoordinate,
    617     TagValueType::AddressableCoordinate,
    618     true,
    619 );
    620 const TAG_CALENDAR_INCLUSION_REQUEST: TagContract = tag(
    621     "a",
    622     TagCardinality::OptionalMany,
    623     TagSemantic::CalendarInclusionRequest,
    624     TagValueType::AddressableCoordinate,
    625     true,
    626 );
    627 const TAG_E_SOURCE_VERSION: TagContract = tag(
    628     "e",
    629     TagCardinality::RequiredOne,
    630     TagSemantic::Source,
    631     TagValueType::EventId,
    632     true,
    633 );
    634 const TAG_E_BASE_VERSION: TagContract = tag(
    635     "e",
    636     TagCardinality::OptionalOne,
    637     TagSemantic::PreviousEvent,
    638     TagValueType::EventId,
    639     true,
    640 );
    641 const TAG_E_MANY: TagContract = tag(
    642     "e",
    643     TagCardinality::OptionalMany,
    644     TagSemantic::EventPointer,
    645     TagValueType::EventId,
    646     true,
    647 );
    648 const TAG_NIP09_E_TARGET: TagContract = tag(
    649     "e",
    650     TagCardinality::OptionalMany,
    651     TagSemantic::EventPointer,
    652     TagValueType::EventId,
    653     true,
    654 );
    655 const TAG_NIP09_A_TARGET: TagContract = tag(
    656     "a",
    657     TagCardinality::OptionalMany,
    658     TagSemantic::Nip01Coordinate,
    659     TagValueType::Nip01Coordinate,
    660     true,
    661 );
    662 const TAG_NIP09_K_ADVISORY: TagContract = tag(
    663     "k",
    664     TagCardinality::OptionalMany,
    665     TagSemantic::Kind,
    666     TagValueType::Kind,
    667     true,
    668 );
    669 const TAG_NIP10_E_REQUIRED: TagContract = tag(
    670     "e",
    671     TagCardinality::RequiredMany,
    672     TagSemantic::EventPointer,
    673     TagValueType::EventId,
    674     true,
    675 );
    676 const TAG_NIP10_P_OPTIONAL: TagContract = tag(
    677     "p",
    678     TagCardinality::OptionalMany,
    679     TagSemantic::Participant,
    680     TagValueType::PublicKey,
    681     true,
    682 );
    683 const TAG_NIP22_E_ROOT: TagContract = tag(
    684     "E",
    685     TagCardinality::OptionalOne,
    686     TagSemantic::RootEvent,
    687     TagValueType::EventId,
    688     true,
    689 );
    690 const TAG_NIP22_A_ROOT: TagContract = tag(
    691     "A",
    692     TagCardinality::OptionalOne,
    693     TagSemantic::AddressableCoordinate,
    694     TagValueType::AddressableCoordinate,
    695     true,
    696 );
    697 const TAG_NIP22_K_ROOT: TagContract = tag(
    698     "K",
    699     TagCardinality::RequiredOne,
    700     TagSemantic::Kind,
    701     TagValueType::Kind,
    702     true,
    703 );
    704 const TAG_NIP22_P_ROOT: TagContract = tag(
    705     "P",
    706     TagCardinality::RequiredOne,
    707     TagSemantic::Participant,
    708     TagValueType::PublicKey,
    709     true,
    710 );
    711 const TAG_NIP22_A_PARENT: TagContract = tag(
    712     "a",
    713     TagCardinality::OptionalOne,
    714     TagSemantic::AddressableCoordinate,
    715     TagValueType::AddressableCoordinate,
    716     true,
    717 );
    718 const TAG_NIP22_E_PARENT: TagContract = tag(
    719     "e",
    720     TagCardinality::RequiredOne,
    721     TagSemantic::EventPointer,
    722     TagValueType::EventId,
    723     true,
    724 );
    725 const TAG_NIP22_K_PARENT: TagContract = tag(
    726     "k",
    727     TagCardinality::RequiredOne,
    728     TagSemantic::Kind,
    729     TagValueType::Kind,
    730     true,
    731 );
    732 const TAG_NIP22_P_PARENT: TagContract = tag(
    733     "p",
    734     TagCardinality::RequiredMany,
    735     TagSemantic::Participant,
    736     TagValueType::PublicKey,
    737     true,
    738 );
    739 const TAG_KIND: TagContract = tag(
    740     "k",
    741     TagCardinality::OptionalOne,
    742     TagSemantic::Kind,
    743     TagValueType::Kind,
    744     true,
    745 );
    746 const TAG_RELAY: TagContract = tag(
    747     "relay",
    748     TagCardinality::OptionalMany,
    749     TagSemantic::Relay,
    750     TagValueType::RelayUrl,
    751     false,
    752 );
    753 const TAG_GROUP: TagContract = tag(
    754     "h",
    755     TagCardinality::RequiredOne,
    756     TagSemantic::GroupId,
    757     TagValueType::DTag,
    758     true,
    759 );
    760 const TAG_TITLE: TagContract = tag(
    761     "title",
    762     TagCardinality::OptionalOne,
    763     TagSemantic::Title,
    764     TagValueType::Text,
    765     false,
    766 );
    767 const TAG_CALENDAR_TITLE: TagContract = tag(
    768     "title",
    769     TagCardinality::RequiredOne,
    770     TagSemantic::Title,
    771     TagValueType::Text,
    772     false,
    773 );
    774 const TAG_CALENDAR_LEGACY_NAME: TagContract = tag(
    775     "name",
    776     TagCardinality::OptionalOne,
    777     TagSemantic::Title,
    778     TagValueType::Text,
    779     false,
    780 );
    781 const TAG_CALENDAR_UID: TagContract = tag(
    782     "d",
    783     TagCardinality::RequiredOne,
    784     TagSemantic::Identifier,
    785     TagValueType::CalendarUid,
    786     true,
    787 );
    788 const TAG_CALENDAR_COLLECTION_DESCRIPTION: TagContract = tag(
    789     "description",
    790     TagCardinality::OptionalOne,
    791     TagSemantic::ListDescription,
    792     TagValueType::Text,
    793     false,
    794 );
    795 const TAG_CALENDAR_COLLECTION_EVENT: TagContract = tag(
    796     "a",
    797     TagCardinality::OptionalMany,
    798     TagSemantic::CalendarEventReference,
    799     TagValueType::CalendarEventCoordinate,
    800     true,
    801 );
    802 const TAG_CALENDAR_RSVP_EVENT: TagContract = tag(
    803     "a",
    804     TagCardinality::RequiredOne,
    805     TagSemantic::CalendarEventReference,
    806     TagValueType::CalendarEventCoordinate,
    807     true,
    808 );
    809 const TAG_CALENDAR_RSVP_REVISION: TagContract = tag(
    810     "e",
    811     TagCardinality::OptionalOne,
    812     TagSemantic::CalendarEventRevision,
    813     TagValueType::EventId,
    814     true,
    815 );
    816 const TAG_CALENDAR_RSVP_STATUS: TagContract = tag(
    817     "status",
    818     TagCardinality::RequiredOne,
    819     TagSemantic::Status,
    820     TagValueType::CalendarRsvpStatus,
    821     false,
    822 );
    823 const TAG_CALENDAR_RSVP_FREE_BUSY: TagContract = tag(
    824     "fb",
    825     TagCardinality::OptionalOne,
    826     TagSemantic::FreeBusy,
    827     TagValueType::CalendarFreeBusy,
    828     false,
    829 );
    830 const TAG_CALENDAR_RSVP_AUTHOR: TagContract = tag(
    831     "p",
    832     TagCardinality::OptionalOne,
    833     TagSemantic::CalendarEventAuthor,
    834     TagValueType::PublicKey,
    835     true,
    836 );
    837 const TAG_CALENDAR_DATE_START: TagContract = tag(
    838     "start",
    839     TagCardinality::RequiredOne,
    840     TagSemantic::CalendarStart,
    841     TagValueType::CalendarDate,
    842     false,
    843 );
    844 const TAG_CALENDAR_DATE_END: TagContract = tag(
    845     "end",
    846     TagCardinality::OptionalOne,
    847     TagSemantic::CalendarEnd,
    848     TagValueType::CalendarDate,
    849     false,
    850 );
    851 const TAG_CALENDAR_TIME_START: TagContract = tag(
    852     "start",
    853     TagCardinality::RequiredOne,
    854     TagSemantic::CalendarStart,
    855     TagValueType::UnixTimestamp,
    856     false,
    857 );
    858 const TAG_CALENDAR_TIME_END: TagContract = tag(
    859     "end",
    860     TagCardinality::OptionalOne,
    861     TagSemantic::CalendarEnd,
    862     TagValueType::UnixTimestamp,
    863     false,
    864 );
    865 const TAG_CALENDAR_COVERED_UTC_DAY: TagContract = tag(
    866     "D",
    867     TagCardinality::RequiredMany,
    868     TagSemantic::UtcDayCoverage,
    869     TagValueType::UtcDayIndex,
    870     true,
    871 );
    872 const TAG_CALENDAR_START_TZID: TagContract = tag(
    873     "start_tzid",
    874     TagCardinality::OptionalOne,
    875     TagSemantic::TimeZone,
    876     TagValueType::IanaTimeZoneId,
    877     false,
    878 );
    879 const TAG_CALENDAR_END_TZID: TagContract = tag(
    880     "end_tzid",
    881     TagCardinality::OptionalOne,
    882     TagSemantic::TimeZone,
    883     TagValueType::IanaTimeZoneId,
    884     false,
    885 );
    886 const TAG_SUMMARY: TagContract = tag(
    887     "summary",
    888     TagCardinality::OptionalOne,
    889     TagSemantic::Summary,
    890     TagValueType::Text,
    891     false,
    892 );
    893 const TAG_PUBLISHED_AT: TagContract = tag(
    894     "published_at",
    895     TagCardinality::OptionalOne,
    896     TagSemantic::PublishedAt,
    897     TagValueType::UnixTimestamp,
    898     false,
    899 );
    900 const TAG_LOCATION: TagContract = tag(
    901     "location",
    902     TagCardinality::OptionalMany,
    903     TagSemantic::Location,
    904     TagValueType::Text,
    905     false,
    906 );
    907 const TAG_CALENDAR_LOCATION: TagContract = tag(
    908     "location",
    909     TagCardinality::OptionalMany,
    910     TagSemantic::Location,
    911     TagValueType::Text,
    912     false,
    913 );
    914 const TAG_PRICE: TagContract = tag(
    915     "price",
    916     TagCardinality::OptionalMany,
    917     TagSemantic::Price,
    918     TagValueType::Text,
    919     false,
    920 );
    921 const TAG_STATUS: TagContract = tag(
    922     "status",
    923     TagCardinality::OptionalOne,
    924     TagSemantic::Status,
    925     TagValueType::Text,
    926     false,
    927 );
    928 const TAG_IMAGE: TagContract = tag(
    929     "image",
    930     TagCardinality::OptionalMany,
    931     TagSemantic::Image,
    932     TagValueType::Url,
    933     false,
    934 );
    935 const TAG_FOOD_TITLE: TagContract = tag(
    936     "title",
    937     TagCardinality::RequiredOne,
    938     TagSemantic::Title,
    939     TagValueType::Text,
    940     false,
    941 );
    942 const TAG_FOOD_SUMMARY: TagContract = tag(
    943     "summary",
    944     TagCardinality::RequiredOne,
    945     TagSemantic::Summary,
    946     TagValueType::Text,
    947     false,
    948 );
    949 const TAG_FOOD_PUBLISHED_AT: TagContract = tag(
    950     "published_at",
    951     TagCardinality::RequiredOne,
    952     TagSemantic::PublishedAt,
    953     TagValueType::UnixTimestamp,
    954     false,
    955 );
    956 const TAG_FOOD_LOCATION: TagContract = tag(
    957     "location",
    958     TagCardinality::RequiredOne,
    959     TagSemantic::Location,
    960     TagValueType::Text,
    961     false,
    962 );
    963 const TAG_FOOD_PRICE: TagContract = tag(
    964     "price",
    965     TagCardinality::RequiredOne,
    966     TagSemantic::Price,
    967     TagValueType::Text,
    968     false,
    969 );
    970 const TAG_FOOD_PRICE_UNIT: TagContract = tag(
    971     TAG_RADROOTS_PRICE_UNIT,
    972     TagCardinality::RequiredOne,
    973     TagSemantic::Price,
    974     TagValueType::Text,
    975     false,
    976 );
    977 const TAG_FOOD_QUANTITY: TagContract = tag(
    978     TAG_RADROOTS_QUANTITY,
    979     TagCardinality::OptionalOne,
    980     TagSemantic::Price,
    981     TagValueType::Text,
    982     false,
    983 );
    984 const TAG_FOOD_STATUS: TagContract = tag(
    985     "status",
    986     TagCardinality::RequiredOne,
    987     TagSemantic::Status,
    988     TagValueType::Text,
    989     false,
    990 );
    991 const TAG_OPERATIONAL_LISTING_FARM: TagContract = tag(
    992     "a",
    993     TagCardinality::RequiredOne,
    994     TagSemantic::AddressableCoordinate,
    995     TagValueType::AddressableCoordinate,
    996     true,
    997 );
    998 const TAG_OPERATIONAL_LISTING_PRODUCT_KEY: TagContract = tag(
    999     "key",
   1000     TagCardinality::RequiredOne,
   1001     TagSemantic::Category,
   1002     TagValueType::Text,
   1003     false,
   1004 );
   1005 const TAG_OPERATIONAL_LISTING_TITLE: TagContract = tag(
   1006     "title",
   1007     TagCardinality::RequiredOne,
   1008     TagSemantic::Title,
   1009     TagValueType::Text,
   1010     false,
   1011 );
   1012 const TAG_OPERATIONAL_LISTING_CATEGORY: TagContract = tag(
   1013     "category",
   1014     TagCardinality::RequiredOne,
   1015     TagSemantic::Category,
   1016     TagValueType::Text,
   1017     false,
   1018 );
   1019 const TAG_OPERATIONAL_LISTING_PRIMARY_BIN: TagContract = tag(
   1020     "radroots:primary_bin",
   1021     TagCardinality::RequiredOne,
   1022     TagSemantic::OperationalListingSnapshot,
   1023     TagValueType::Text,
   1024     false,
   1025 );
   1026 const TAG_OPERATIONAL_LISTING_BIN: TagContract = tag(
   1027     "radroots:bin",
   1028     TagCardinality::RequiredMany,
   1029     TagSemantic::OperationalListingSnapshot,
   1030     TagValueType::Text,
   1031     false,
   1032 );
   1033 const TAG_OPERATIONAL_LISTING_PRICE: TagContract = tag(
   1034     "radroots:price",
   1035     TagCardinality::RequiredMany,
   1036     TagSemantic::Price,
   1037     TagValueType::Text,
   1038     false,
   1039 );
   1040 const TAG_OPERATIONAL_LISTING_DISCOUNT: TagContract = tag(
   1041     "radroots:discount",
   1042     TagCardinality::OptionalMany,
   1043     TagSemantic::Price,
   1044     TagValueType::Text,
   1045     false,
   1046 );
   1047 const TAG_OPERATIONAL_LISTING_RESOURCE_AREA: TagContract = tag(
   1048     "radroots:resource_area",
   1049     TagCardinality::OptionalOne,
   1050     TagSemantic::AddressableCoordinate,
   1051     TagValueType::AddressableCoordinate,
   1052     false,
   1053 );
   1054 const TAG_OPERATIONAL_LISTING_PLOT: TagContract = tag(
   1055     "radroots:plot",
   1056     TagCardinality::OptionalOne,
   1057     TagSemantic::AddressableCoordinate,
   1058     TagValueType::AddressableCoordinate,
   1059     false,
   1060 );
   1061 const TAG_OPERATIONAL_LISTING_INVENTORY: TagContract = tag(
   1062     "inventory",
   1063     TagCardinality::OptionalOne,
   1064     TagSemantic::OperationalListingSnapshot,
   1065     TagValueType::Text,
   1066     false,
   1067 );
   1068 const TAG_OPERATIONAL_LISTING_AVAILABILITY_START: TagContract = tag(
   1069     "radroots:availability_start",
   1070     TagCardinality::OptionalOne,
   1071     TagSemantic::Status,
   1072     TagValueType::UnixTimestamp,
   1073     false,
   1074 );
   1075 const TAG_OPERATIONAL_LISTING_EXPIRES_AT: TagContract = tag(
   1076     "expires_at",
   1077     TagCardinality::OptionalOne,
   1078     TagSemantic::Status,
   1079     TagValueType::UnixTimestamp,
   1080     false,
   1081 );
   1082 const TAG_OPERATIONAL_LISTING_DELIVERY: TagContract = tag(
   1083     "delivery",
   1084     TagCardinality::OptionalOne,
   1085     TagSemantic::Reference,
   1086     TagValueType::Text,
   1087     false,
   1088 );
   1089 const TAG_OPERATIONAL_LISTING_PROCESS: TagContract = tag(
   1090     "process",
   1091     TagCardinality::OptionalOne,
   1092     TagSemantic::Category,
   1093     TagValueType::Text,
   1094     false,
   1095 );
   1096 const TAG_OPERATIONAL_LISTING_LOT: TagContract = tag(
   1097     "lot",
   1098     TagCardinality::OptionalOne,
   1099     TagSemantic::Reference,
   1100     TagValueType::Text,
   1101     false,
   1102 );
   1103 const TAG_OPERATIONAL_LISTING_PROFILE: TagContract = tag(
   1104     "profile",
   1105     TagCardinality::OptionalOne,
   1106     TagSemantic::Category,
   1107     TagValueType::Text,
   1108     false,
   1109 );
   1110 const TAG_OPERATIONAL_LISTING_YEAR: TagContract = tag(
   1111     "year",
   1112     TagCardinality::OptionalOne,
   1113     TagSemantic::Category,
   1114     TagValueType::Text,
   1115     false,
   1116 );
   1117 const TAG_CALENDAR_IMAGE: TagContract = tag(
   1118     "image",
   1119     TagCardinality::OptionalOne,
   1120     TagSemantic::Image,
   1121     TagValueType::Url,
   1122     false,
   1123 );
   1124 const TAG_URL: TagContract = tag(
   1125     "url",
   1126     TagCardinality::OptionalOne,
   1127     TagSemantic::Url,
   1128     TagValueType::Url,
   1129     false,
   1130 );
   1131 const TAG_CONTRACT_REQUIRED: TagContract = tag(
   1132     "contract",
   1133     TagCardinality::RequiredOne,
   1134     TagSemantic::Contract,
   1135     TagValueType::ContractId,
   1136     false,
   1137 );
   1138 const TAG_TOPIC_MANY: TagContract = tag(
   1139     "t",
   1140     TagCardinality::OptionalMany,
   1141     TagSemantic::Topic,
   1142     TagValueType::Text,
   1143     true,
   1144 );
   1145 const TAG_ASK_MARKER: TagContract = tag(
   1146     "t",
   1147     TagCardinality::RequiredOne,
   1148     TagSemantic::Topic,
   1149     TagValueType::Text,
   1150     true,
   1151 );
   1152 const TAG_IMETA_REQUIRED_MANY: TagContract = tag(
   1153     "imeta",
   1154     TagCardinality::RequiredMany,
   1155     TagSemantic::Image,
   1156     TagValueType::Text,
   1157     false,
   1158 );
   1159 const TAG_IMETA_OPTIONAL_MANY: TagContract = tag(
   1160     "imeta",
   1161     TagCardinality::OptionalMany,
   1162     TagSemantic::Image,
   1163     TagValueType::Text,
   1164     false,
   1165 );
   1166 const TAG_CALENDAR_REFERENCE: TagContract = tag(
   1167     "r",
   1168     TagCardinality::OptionalMany,
   1169     TagSemantic::Reference,
   1170     TagValueType::Uri,
   1171     true,
   1172 );
   1173 const TAG_GEOHASH_OPTIONAL: TagContract = tag(
   1174     "g",
   1175     TagCardinality::OptionalOne,
   1176     TagSemantic::Geohash,
   1177     TagValueType::Geohash,
   1178     true,
   1179 );
   1180 const TAG_SOURCE_MANY: TagContract = tag(
   1181     "source",
   1182     TagCardinality::OptionalMany,
   1183     TagSemantic::Source,
   1184     TagValueType::EventPointer,
   1185     false,
   1186 );
   1187 const TAG_CITATION_MANY: TagContract = tag(
   1188     "citation",
   1189     TagCardinality::OptionalMany,
   1190     TagSemantic::Citation,
   1191     TagValueType::Sha256,
   1192     false,
   1193 );
   1194 const TAG_REVIEW_TARGET_REQUIRED: TagContract = tag(
   1195     "review_target",
   1196     TagCardinality::RequiredOne,
   1197     TagSemantic::ReviewTarget,
   1198     TagValueType::EventPointer,
   1199     false,
   1200 );
   1201 const TAG_EVIDENCE_MANY: TagContract = tag(
   1202     "evidence",
   1203     TagCardinality::OptionalMany,
   1204     TagSemantic::Evidence,
   1205     TagValueType::EventPointer,
   1206     false,
   1207 );
   1208 
   1209 const NO_TAGS: &[TagContract] = &[];
   1210 const D_TAGS: &[TagContract] = &[TAG_D];
   1211 const P_TAGS: &[TagContract] = &[TAG_P_MANY];
   1212 const EVENT_POINTER_TAGS: &[TagContract] = &[TAG_E_MANY, TAG_P_MANY, TAG_KIND];
   1213 const NIP09_DELETION_TAGS: &[TagContract] =
   1214     &[TAG_NIP09_E_TARGET, TAG_NIP09_A_TARGET, TAG_NIP09_K_ADVISORY];
   1215 const NIP22_COMMENT_TAGS: &[TagContract] = &[
   1216     TAG_NIP22_E_ROOT,
   1217     TAG_NIP22_A_ROOT,
   1218     TAG_NIP22_K_ROOT,
   1219     TAG_NIP22_P_ROOT,
   1220     TAG_NIP22_A_PARENT,
   1221     TAG_NIP22_E_PARENT,
   1222     TAG_NIP22_K_PARENT,
   1223     TAG_NIP22_P_PARENT,
   1224 ];
   1225 const LIST_TAGS: &[TagContract] = &[TAG_E_MANY, TAG_A_OPTIONAL, TAG_P_MANY, TAG_RELAY];
   1226 const LIST_SET_TAGS: &[TagContract] = &[TAG_D, TAG_E_MANY, TAG_A_OPTIONAL, TAG_P_MANY];
   1227 const PROFILE_TAGS: &[TagContract] = &[TAG_P_MANY];
   1228 const GROUP_ACTION_TAGS: &[TagContract] = &[TAG_GROUP, TAG_P_MANY, TAG_E_MANY];
   1229 const GROUP_STATE_TAGS: &[TagContract] = &[TAG_D, TAG_P_MANY, TAG_E_MANY];
   1230 const FILE_METADATA_TAGS: &[TagContract] = &[TAG_URL, TAG_IMAGE];
   1231 const ARTICLE_TAGS: &[TagContract] = &[TAG_D, TAG_TITLE, TAG_SUMMARY, TAG_PUBLISHED_AT];
   1232 const WIKI_ARTICLE_TAGS: &[TagContract] = &[
   1233     TAG_D,
   1234     TAG_TITLE,
   1235     TAG_SUMMARY,
   1236     TAG_PUBLISHED_AT,
   1237     TAG_TOPIC_MANY,
   1238     TAG_SOURCE_MANY,
   1239     TAG_A_MANY,
   1240     TAG_E_MANY,
   1241 ];
   1242 const WIKI_REDIRECT_TAGS: &[TagContract] = &[TAG_D, TAG_A_ADDRESS_REQUIRED];
   1243 const WIKI_MERGE_REQUEST_TAGS: &[TagContract] = &[
   1244     TAG_A_ADDRESS_REQUIRED,
   1245     TAG_P_REQUIRED,
   1246     TAG_E_SOURCE_VERSION,
   1247     TAG_E_BASE_VERSION,
   1248 ];
   1249 const CALENDAR_DATE_EVENT_TAGS: &[TagContract] = &[
   1250     TAG_D,
   1251     TAG_CALENDAR_TITLE,
   1252     TAG_CALENDAR_LEGACY_NAME,
   1253     TAG_CALENDAR_DATE_START,
   1254     TAG_CALENDAR_DATE_END,
   1255     TAG_CALENDAR_LOCATION,
   1256     TAG_GEOHASH_OPTIONAL,
   1257     TAG_SUMMARY,
   1258     TAG_CALENDAR_IMAGE,
   1259     TAG_CALENDAR_PARTICIPANT,
   1260     TAG_TOPIC_MANY,
   1261     TAG_CALENDAR_REFERENCE,
   1262     TAG_CALENDAR_INCLUSION_REQUEST,
   1263 ];
   1264 const CALENDAR_TIME_EVENT_TAGS: &[TagContract] = &[
   1265     TAG_D,
   1266     TAG_CALENDAR_TITLE,
   1267     TAG_CALENDAR_LEGACY_NAME,
   1268     TAG_CALENDAR_TIME_START,
   1269     TAG_CALENDAR_TIME_END,
   1270     TAG_CALENDAR_COVERED_UTC_DAY,
   1271     TAG_CALENDAR_START_TZID,
   1272     TAG_CALENDAR_END_TZID,
   1273     TAG_CALENDAR_LOCATION,
   1274     TAG_GEOHASH_OPTIONAL,
   1275     TAG_SUMMARY,
   1276     TAG_CALENDAR_IMAGE,
   1277     TAG_CALENDAR_PARTICIPANT,
   1278     TAG_TOPIC_MANY,
   1279     TAG_CALENDAR_REFERENCE,
   1280     TAG_CALENDAR_INCLUSION_REQUEST,
   1281 ];
   1282 const CALENDAR_COLLECTION_TAGS: &[TagContract] = &[
   1283     TAG_CALENDAR_UID,
   1284     TAG_CALENDAR_TITLE,
   1285     TAG_CALENDAR_COLLECTION_DESCRIPTION,
   1286     TAG_CALENDAR_IMAGE,
   1287     TAG_CALENDAR_COLLECTION_EVENT,
   1288 ];
   1289 const CALENDAR_RSVP_TAGS: &[TagContract] = &[
   1290     TAG_CALENDAR_UID,
   1291     TAG_CALENDAR_RSVP_EVENT,
   1292     TAG_CALENDAR_RSVP_REVISION,
   1293     TAG_CALENDAR_RSVP_STATUS,
   1294     TAG_CALENDAR_RSVP_FREE_BUSY,
   1295     TAG_CALENDAR_RSVP_AUTHOR,
   1296 ];
   1297 const FARM_TAGS: &[TagContract] = &[TAG_D, TAG_TITLE, TAG_LOCATION, TAG_IMAGE];
   1298 const FOOD_AVAILABILITY_TAGS: &[TagContract] = &[
   1299     TAG_D,
   1300     TAG_FOOD_TITLE,
   1301     TAG_FOOD_SUMMARY,
   1302     TAG_FOOD_PUBLISHED_AT,
   1303     TAG_FOOD_LOCATION,
   1304     TAG_FOOD_PRICE,
   1305     TAG_FOOD_PRICE_UNIT,
   1306     TAG_FOOD_QUANTITY,
   1307     TAG_FOOD_STATUS,
   1308     TAG_IMAGE,
   1309 ];
   1310 const OPERATIONAL_LISTING_TAGS: &[TagContract] = &[
   1311     TAG_D,
   1312     TAG_P_REQUIRED,
   1313     TAG_OPERATIONAL_LISTING_FARM,
   1314     TAG_OPERATIONAL_LISTING_PRODUCT_KEY,
   1315     TAG_OPERATIONAL_LISTING_TITLE,
   1316     TAG_OPERATIONAL_LISTING_CATEGORY,
   1317     TAG_SUMMARY,
   1318     TAG_PUBLISHED_AT,
   1319     TAG_OPERATIONAL_LISTING_PROCESS,
   1320     TAG_OPERATIONAL_LISTING_LOT,
   1321     TAG_OPERATIONAL_LISTING_PROFILE,
   1322     TAG_OPERATIONAL_LISTING_YEAR,
   1323     TAG_LOCATION,
   1324     TAG_PRICE,
   1325     TAG_STATUS,
   1326     TAG_IMAGE,
   1327     TAG_GEOHASH_OPTIONAL,
   1328     TAG_OPERATIONAL_LISTING_PRIMARY_BIN,
   1329     TAG_OPERATIONAL_LISTING_BIN,
   1330     TAG_OPERATIONAL_LISTING_PRICE,
   1331     TAG_OPERATIONAL_LISTING_DISCOUNT,
   1332     TAG_OPERATIONAL_LISTING_RESOURCE_AREA,
   1333     TAG_OPERATIONAL_LISTING_PLOT,
   1334     TAG_OPERATIONAL_LISTING_INVENTORY,
   1335     TAG_OPERATIONAL_LISTING_AVAILABILITY_START,
   1336     TAG_OPERATIONAL_LISTING_EXPIRES_AT,
   1337     TAG_OPERATIONAL_LISTING_DELIVERY,
   1338 ];
   1339 const TRADE_MUTATION_TAGS: &[TagContract] = &[
   1340     TAG_CONTRACT_REQUIRED,
   1341     TAG_D,
   1342     TAG_X_TRADE_MUTATIONS,
   1343     TAG_P_TRADE_PARTIES,
   1344 ];
   1345 const RHI_EVIDENCE_ATTESTATION_TAGS: &[TagContract] = &[
   1346     TAG_CONTRACT_REQUIRED,
   1347     TAG_D,
   1348     TAG_X_RHI_ATTESTATION_REFERENCES,
   1349     TAG_T_RHI_ATTESTATION_OUTCOME,
   1350     TAG_E_RHI_SUPERSEDES_EVENT,
   1351 ];
   1352 const KNOWLEDGE_SOURCE_TAGS: &[TagContract] = &[
   1353     TAG_D,
   1354     TAG_CONTRACT_REQUIRED,
   1355     TAG_TOPIC_MANY,
   1356     TAG_SOURCE_MANY,
   1357 ];
   1358 const KNOWLEDGE_CLAIM_TAGS: &[TagContract] = &[
   1359     TAG_CONTRACT_REQUIRED,
   1360     TAG_TOPIC_MANY,
   1361     TAG_SOURCE_MANY,
   1362     TAG_CITATION_MANY,
   1363 ];
   1364 const KNOWLEDGE_RELATION_TAGS: &[TagContract] =
   1365     &[TAG_CONTRACT_REQUIRED, TAG_TOPIC_MANY, TAG_SOURCE_MANY];
   1366 const KNOWLEDGE_REVIEW_TAGS: &[TagContract] = &[
   1367     TAG_CONTRACT_REQUIRED,
   1368     TAG_REVIEW_TARGET_REQUIRED,
   1369     TAG_EVIDENCE_MANY,
   1370 ];
   1371 const KNOWLEDGE_FIELD_REPORT_TAGS: &[TagContract] = &[
   1372     TAG_CONTRACT_REQUIRED,
   1373     TAG_TOPIC_MANY,
   1374     TAG_GEOHASH_OPTIONAL,
   1375     TAG_EVIDENCE_MANY,
   1376 ];
   1377 const KNOWLEDGE_CHANGE_PROPOSAL_TAGS: &[TagContract] = &[TAG_CONTRACT_REQUIRED, TAG_EVIDENCE_MANY];
   1378 const KNOWLEDGE_CONTRIBUTION_TAGS: &[TagContract] = &[TAG_CONTRACT_REQUIRED, TAG_EVIDENCE_MANY];
   1379 const EVIDENCE_BOUNTY_TAGS: &[TagContract] = &[
   1380     TAG_D,
   1381     TAG_CONTRACT_REQUIRED,
   1382     TAG_TOPIC_MANY,
   1383     TAG_EVIDENCE_MANY,
   1384 ];
   1385 
   1386 const SOCIAL_REDUCERS: &[Reducer] = &[Reducer::SocialProjection];
   1387 const PHOTO_UPDATE_TAGS: &[TagContract] = &[TAG_IMETA_REQUIRED_MANY];
   1388 const ASK_TAGS: &[TagContract] = &[TAG_ASK_MARKER, TAG_IMETA_OPTIONAL_MANY];
   1389 const NIP10_REPLY_TAGS: &[TagContract] = &[TAG_NIP10_E_REQUIRED, TAG_NIP10_P_OPTIONAL];
   1390 const PROFILE_REDUCERS: &[Reducer] = &[Reducer::ProfileProjection];
   1391 const FARM_OPS_REDUCERS: &[Reducer] = &[Reducer::FarmOpsProjection];
   1392 const GROUP_REDUCERS: &[Reducer] = &[Reducer::GroupProjection];
   1393 const CALENDAR_REDUCERS: &[Reducer] = &[Reducer::CalendarProjection];
   1394 const OPERATIONAL_LISTING_REDUCERS: &[Reducer] = &[
   1395     Reducer::OperationalListingProjection,
   1396     Reducer::MarketProjection,
   1397     Reducer::OperationalListingInventoryAccounting,
   1398 ];
   1399 const FOOD_AVAILABILITY_REDUCERS: &[Reducer] = &[Reducer::MarketProjection];
   1400 const TRADE_MUTATION_REDUCERS: &[Reducer] = &[
   1401     Reducer::TradeProjection,
   1402     Reducer::OperationalListingInventoryAccounting,
   1403 ];
   1404 const TRADE_VALIDATION_REDUCERS: &[Reducer] = &[Reducer::TradeValidation];
   1405 const RELAY_REDUCERS: &[Reducer] = &[Reducer::NostrRelayPolicyProjection];
   1406 const KNOWLEDGE_REDUCERS: &[Reducer] = &[Reducer::KnowledgeProjection];
   1407 
   1408 const FARM_MEMBERS_LIST_DISCRIMINATOR: &[EventDiscriminator] = &[
   1409     EventDiscriminator::DTagPrefix("farm:"),
   1410     EventDiscriminator::DTagSuffix(":members"),
   1411 ];
   1412 const FARM_OWNERS_LIST_DISCRIMINATOR: &[EventDiscriminator] = &[
   1413     EventDiscriminator::DTagPrefix("farm:"),
   1414     EventDiscriminator::DTagSuffix(":members.owners"),
   1415 ];
   1416 const FARM_WORKERS_LIST_DISCRIMINATOR: &[EventDiscriminator] = &[
   1417     EventDiscriminator::DTagPrefix("farm:"),
   1418     EventDiscriminator::DTagSuffix(":members.workers"),
   1419 ];
   1420 const FARM_PLOTS_LIST_DISCRIMINATOR: &[EventDiscriminator] = &[
   1421     EventDiscriminator::DTagPrefix("farm:"),
   1422     EventDiscriminator::DTagSuffix(":plots"),
   1423 ];
   1424 const FARM_LISTINGS_LIST_DISCRIMINATOR: &[EventDiscriminator] = &[
   1425     EventDiscriminator::DTagPrefix("farm:"),
   1426     EventDiscriminator::DTagSuffix(":listings"),
   1427 ];
   1428 
   1429 macro_rules! kind_contract {
   1430     ($kind:expr, $constant:literal, $name:literal, $class:expr, $standard:expr, [$($contract:literal),+ $(,)?]) => {
   1431         KindContract {
   1432             kind: $kind,
   1433             canonical_constant: $constant,
   1434             name: $name,
   1435             class: $class,
   1436             standard: $standard,
   1437             accepted_event_contracts: &[$($contract),+],
   1438         }
   1439     };
   1440 }
   1441 
   1442 macro_rules! event_contract_with_stability {
   1443     (
   1444         $id:literal,
   1445         $kind:expr,
   1446         $name:literal,
   1447         $payload_type:literal,
   1448         $class:expr,
   1449         $standard_privacy:expr,
   1450         $required_author_role:expr,
   1451         $content_schema:expr,
   1452         $discriminator:expr,
   1453         $tags:expr,
   1454         $reducers:expr,
   1455         $stability:expr $(,)?
   1456     ) => {
   1457         EventContract {
   1458             id: $id,
   1459             kind: $kind,
   1460             name: $name,
   1461             payload_type: $payload_type,
   1462             class: $class,
   1463             stability: $stability,
   1464             privacy: $standard_privacy,
   1465             required_author_role: $required_author_role,
   1466             content_schema: $content_schema,
   1467             authoring_policy: EventAuthoringPolicy::GenericDraft,
   1468             discriminator: $discriminator,
   1469             tags: $tags,
   1470             reducers: $reducers,
   1471         }
   1472     };
   1473 }
   1474 
   1475 macro_rules! event_contract_with_authoring_policy {
   1476     (
   1477         $id:literal,
   1478         $kind:expr,
   1479         $name:literal,
   1480         $payload_type:literal,
   1481         $class:expr,
   1482         $standard_privacy:expr,
   1483         $required_author_role:expr,
   1484         $content_schema:expr,
   1485         $authoring_policy:expr,
   1486         $discriminator:expr,
   1487         $tags:expr,
   1488         $reducers:expr $(,)?
   1489     ) => {
   1490         EventContract {
   1491             authoring_policy: $authoring_policy,
   1492             ..event_contract!(
   1493                 $id,
   1494                 $kind,
   1495                 $name,
   1496                 $payload_type,
   1497                 $class,
   1498                 $standard_privacy,
   1499                 $required_author_role,
   1500                 $content_schema,
   1501                 $discriminator,
   1502                 $tags,
   1503                 $reducers
   1504             )
   1505         }
   1506     };
   1507 }
   1508 
   1509 macro_rules! event_contract {
   1510     (
   1511         $id:literal,
   1512         $kind:expr,
   1513         $name:literal,
   1514         $payload_type:literal,
   1515         $class:expr,
   1516         $standard_privacy:expr,
   1517         $required_author_role:expr,
   1518         $content_schema:expr,
   1519         $discriminator:expr,
   1520         $tags:expr,
   1521         $reducers:expr $(,)?
   1522     ) => {
   1523         event_contract_with_stability!(
   1524             $id,
   1525             $kind,
   1526             $name,
   1527             $payload_type,
   1528             $class,
   1529             $standard_privacy,
   1530             $required_author_role,
   1531             $content_schema,
   1532             $discriminator,
   1533             $tags,
   1534             $reducers,
   1535             EventStability::Stable
   1536         )
   1537     };
   1538 }
   1539 
   1540 macro_rules! experimental_event_contract {
   1541     (
   1542         $id:literal,
   1543         $kind:expr,
   1544         $name:literal,
   1545         $payload_type:literal,
   1546         $class:expr,
   1547         $standard_privacy:expr,
   1548         $required_author_role:expr,
   1549         $content_schema:expr,
   1550         $discriminator:expr,
   1551         $tags:expr,
   1552         $reducers:expr $(,)?
   1553     ) => {
   1554         event_contract_with_stability!(
   1555             $id,
   1556             $kind,
   1557             $name,
   1558             $payload_type,
   1559             $class,
   1560             $standard_privacy,
   1561             $required_author_role,
   1562             $content_schema,
   1563             $discriminator,
   1564             $tags,
   1565             $reducers,
   1566             EventStability::Experimental
   1567         )
   1568     };
   1569 }
   1570 
   1571 static KIND_CONTRACTS_REGISTRY_V7: &[KindContract] = &[
   1572     kind_contract!(
   1573         KIND_PROFILE,
   1574         "KIND_PROFILE",
   1575         "Profile Metadata",
   1576         EventClass::Replaceable,
   1577         NostrStandard::Nip01,
   1578         ["radroots.profile.metadata.v1"]
   1579     ),
   1580     kind_contract!(
   1581         KIND_POST,
   1582         "KIND_POST",
   1583         "Short Text Note",
   1584         EventClass::Regular,
   1585         NostrStandard::Nip01,
   1586         [
   1587             "radroots.social.post.v1",
   1588             "radroots.social.update.v1",
   1589             "radroots.social.photo_update.v1",
   1590             "radroots.social.ask.v1",
   1591             "radroots.social.reply.v1"
   1592         ]
   1593     ),
   1594     kind_contract!(
   1595         KIND_FOLLOW,
   1596         "KIND_FOLLOW",
   1597         "Contact List",
   1598         EventClass::Replaceable,
   1599         NostrStandard::Nip01,
   1600         ["radroots.social.follow_list.v1"]
   1601     ),
   1602     kind_contract!(
   1603         KIND_DELETION_REQUEST,
   1604         "KIND_DELETION_REQUEST",
   1605         "Deletion Request",
   1606         EventClass::Regular,
   1607         NostrStandard::Nip09,
   1608         ["radroots.social.deletion_request.v1"]
   1609     ),
   1610     kind_contract!(
   1611         KIND_REPOST,
   1612         "KIND_REPOST",
   1613         "Repost",
   1614         EventClass::Regular,
   1615         NostrStandard::Nip18,
   1616         ["radroots.social.repost.v1"]
   1617     ),
   1618     kind_contract!(
   1619         KIND_REACTION,
   1620         "KIND_REACTION",
   1621         "Reaction",
   1622         EventClass::Regular,
   1623         NostrStandard::Nip25,
   1624         ["radroots.social.reaction.v1"]
   1625     ),
   1626     kind_contract!(
   1627         KIND_SEAL,
   1628         "KIND_SEAL",
   1629         "Seal",
   1630         EventClass::Regular,
   1631         NostrStandard::Nip17,
   1632         ["radroots.message.seal.v1"]
   1633     ),
   1634     kind_contract!(
   1635         KIND_MESSAGE,
   1636         "KIND_MESSAGE",
   1637         "Direct Message",
   1638         EventClass::Regular,
   1639         NostrStandard::Nip17,
   1640         ["radroots.message.private.v1"]
   1641     ),
   1642     kind_contract!(
   1643         KIND_MESSAGE_FILE,
   1644         "KIND_MESSAGE_FILE",
   1645         "Direct Message File",
   1646         EventClass::Regular,
   1647         NostrStandard::Nip17,
   1648         ["radroots.message.file.v1"]
   1649     ),
   1650     kind_contract!(
   1651         KIND_GENERIC_REPOST,
   1652         "KIND_GENERIC_REPOST",
   1653         "Generic Repost",
   1654         EventClass::Regular,
   1655         NostrStandard::Nip18,
   1656         ["radroots.social.generic_repost.v1"]
   1657     ),
   1658     kind_contract!(
   1659         KIND_FARM_CRDT_CHANGE,
   1660         "KIND_FARM_CRDT_CHANGE",
   1661         "Farm CRDT Change",
   1662         EventClass::Regular,
   1663         NostrStandard::Radroots,
   1664         ["radroots.farm.crdt_change.v1"]
   1665     ),
   1666     kind_contract!(
   1667         KIND_GIFT_WRAP,
   1668         "KIND_GIFT_WRAP",
   1669         "Gift Wrap",
   1670         EventClass::Regular,
   1671         NostrStandard::Nip17,
   1672         ["radroots.message.gift_wrap.v1"]
   1673     ),
   1674     kind_contract!(
   1675         KIND_FILE_METADATA,
   1676         "KIND_FILE_METADATA",
   1677         "File Metadata",
   1678         EventClass::Regular,
   1679         NostrStandard::Nip94,
   1680         ["radroots.file.metadata.v1"]
   1681     ),
   1682     kind_contract!(
   1683         KIND_COMMENT,
   1684         "KIND_COMMENT",
   1685         "Comment",
   1686         EventClass::Regular,
   1687         NostrStandard::Nip22,
   1688         ["radroots.social.comment.v1"]
   1689     ),
   1690     kind_contract!(
   1691         KIND_REPORT,
   1692         "KIND_REPORT",
   1693         "Report",
   1694         EventClass::Regular,
   1695         NostrStandard::Nip56,
   1696         ["radroots.social.report.v1"]
   1697     ),
   1698     kind_contract!(
   1699         KIND_GROUP_PUT_USER,
   1700         "KIND_GROUP_PUT_USER",
   1701         "Group Put User",
   1702         EventClass::Regular,
   1703         NostrStandard::Nip29,
   1704         ["radroots.group.put_user.v1"]
   1705     ),
   1706     kind_contract!(
   1707         KIND_GROUP_REMOVE_USER,
   1708         "KIND_GROUP_REMOVE_USER",
   1709         "Group Remove User",
   1710         EventClass::Regular,
   1711         NostrStandard::Nip29,
   1712         ["radroots.group.remove_user.v1"]
   1713     ),
   1714     kind_contract!(
   1715         KIND_GROUP_EDIT_METADATA,
   1716         "KIND_GROUP_EDIT_METADATA",
   1717         "Group Edit Metadata",
   1718         EventClass::Regular,
   1719         NostrStandard::Nip29,
   1720         ["radroots.group.edit_metadata.v1"]
   1721     ),
   1722     kind_contract!(
   1723         KIND_GROUP_DELETE_EVENT,
   1724         "KIND_GROUP_DELETE_EVENT",
   1725         "Group Delete Event",
   1726         EventClass::Regular,
   1727         NostrStandard::Nip29,
   1728         ["radroots.group.delete_event.v1"]
   1729     ),
   1730     kind_contract!(
   1731         KIND_GROUP_CREATE_GROUP,
   1732         "KIND_GROUP_CREATE_GROUP",
   1733         "Group Create Group",
   1734         EventClass::Regular,
   1735         NostrStandard::Nip29,
   1736         ["radroots.group.create_group.v1"]
   1737     ),
   1738     kind_contract!(
   1739         KIND_GROUP_DELETE_GROUP,
   1740         "KIND_GROUP_DELETE_GROUP",
   1741         "Group Delete Group",
   1742         EventClass::Regular,
   1743         NostrStandard::Nip29,
   1744         ["radroots.group.delete_group.v1"]
   1745     ),
   1746     kind_contract!(
   1747         KIND_GROUP_CREATE_INVITE,
   1748         "KIND_GROUP_CREATE_INVITE",
   1749         "Group Create Invite",
   1750         EventClass::Regular,
   1751         NostrStandard::Nip29,
   1752         ["radroots.group.create_invite.v1"]
   1753     ),
   1754     kind_contract!(
   1755         KIND_GROUP_JOIN_REQUEST,
   1756         "KIND_GROUP_JOIN_REQUEST",
   1757         "Group Join Request",
   1758         EventClass::Regular,
   1759         NostrStandard::Nip29,
   1760         ["radroots.group.join_request.v1"]
   1761     ),
   1762     kind_contract!(
   1763         KIND_GROUP_LEAVE_REQUEST,
   1764         "KIND_GROUP_LEAVE_REQUEST",
   1765         "Group Leave Request",
   1766         EventClass::Regular,
   1767         NostrStandard::Nip29,
   1768         ["radroots.group.leave_request.v1"]
   1769     ),
   1770     kind_contract!(
   1771         KIND_GEOCHAT,
   1772         "KIND_GEOCHAT",
   1773         "Geochat",
   1774         EventClass::Ephemeral,
   1775         NostrStandard::Nip28,
   1776         ["radroots.social.geochat.v1"]
   1777     ),
   1778     kind_contract!(
   1779         KIND_RELAY_AUTH,
   1780         "KIND_RELAY_AUTH",
   1781         "Relay Auth",
   1782         EventClass::Ephemeral,
   1783         NostrStandard::Nip42,
   1784         ["radroots.relay.auth.v1"]
   1785     ),
   1786     kind_contract!(
   1787         KIND_HTTP_AUTH,
   1788         "KIND_HTTP_AUTH",
   1789         "HTTP Auth",
   1790         EventClass::Ephemeral,
   1791         NostrStandard::Nip98,
   1792         ["radroots.http.auth.v1"]
   1793     ),
   1794     kind_contract!(
   1795         KIND_LIST_MUTE,
   1796         "KIND_LIST_MUTE",
   1797         "Mute List",
   1798         EventClass::Replaceable,
   1799         NostrStandard::Nip51,
   1800         ["radroots.list.mute.v1"]
   1801     ),
   1802     kind_contract!(
   1803         KIND_LIST_PINNED_NOTES,
   1804         "KIND_LIST_PINNED_NOTES",
   1805         "Pinned Notes List",
   1806         EventClass::Replaceable,
   1807         NostrStandard::Nip51,
   1808         ["radroots.list.pinned_notes.v1"]
   1809     ),
   1810     kind_contract!(
   1811         KIND_LIST_READ_WRITE_RELAYS,
   1812         "KIND_LIST_READ_WRITE_RELAYS",
   1813         "Read Write Relays List",
   1814         EventClass::Replaceable,
   1815         NostrStandard::Nip51,
   1816         ["radroots.list.read_write_relays.v1"]
   1817     ),
   1818     kind_contract!(
   1819         KIND_LIST_BOOKMARKS,
   1820         "KIND_LIST_BOOKMARKS",
   1821         "Bookmarks List",
   1822         EventClass::Replaceable,
   1823         NostrStandard::Nip51,
   1824         ["radroots.list.bookmarks.v1"]
   1825     ),
   1826     kind_contract!(
   1827         KIND_LIST_COMMUNITIES,
   1828         "KIND_LIST_COMMUNITIES",
   1829         "Communities List",
   1830         EventClass::Replaceable,
   1831         NostrStandard::Nip51,
   1832         ["radroots.list.communities.v1"]
   1833     ),
   1834     kind_contract!(
   1835         KIND_LIST_PUBLIC_CHATS,
   1836         "KIND_LIST_PUBLIC_CHATS",
   1837         "Public Chats List",
   1838         EventClass::Replaceable,
   1839         NostrStandard::Nip51,
   1840         ["radroots.list.public_chats.v1"]
   1841     ),
   1842     kind_contract!(
   1843         KIND_LIST_BLOCKED_RELAYS,
   1844         "KIND_LIST_BLOCKED_RELAYS",
   1845         "Blocked Relays List",
   1846         EventClass::Replaceable,
   1847         NostrStandard::Nip51,
   1848         ["radroots.list.blocked_relays.v1"]
   1849     ),
   1850     kind_contract!(
   1851         KIND_LIST_SEARCH_RELAYS,
   1852         "KIND_LIST_SEARCH_RELAYS",
   1853         "Search Relays List",
   1854         EventClass::Replaceable,
   1855         NostrStandard::Nip51,
   1856         ["radroots.list.search_relays.v1"]
   1857     ),
   1858     kind_contract!(
   1859         KIND_LIST_SIMPLE_GROUPS,
   1860         "KIND_LIST_SIMPLE_GROUPS",
   1861         "Simple Groups List",
   1862         EventClass::Replaceable,
   1863         NostrStandard::Nip51,
   1864         ["radroots.list.simple_groups.v1"]
   1865     ),
   1866     kind_contract!(
   1867         KIND_LIST_RELAY_FEEDS,
   1868         "KIND_LIST_RELAY_FEEDS",
   1869         "Relay Feeds List",
   1870         EventClass::Replaceable,
   1871         NostrStandard::Nip51,
   1872         ["radroots.list.relay_feeds.v1"]
   1873     ),
   1874     kind_contract!(
   1875         KIND_LIST_INTERESTS,
   1876         "KIND_LIST_INTERESTS",
   1877         "Interests List",
   1878         EventClass::Replaceable,
   1879         NostrStandard::Nip51,
   1880         ["radroots.list.interests.v1"]
   1881     ),
   1882     kind_contract!(
   1883         KIND_LIST_MEDIA_FOLLOWS,
   1884         "KIND_LIST_MEDIA_FOLLOWS",
   1885         "Media Follows List",
   1886         EventClass::Replaceable,
   1887         NostrStandard::Nip51,
   1888         ["radroots.list.media_follows.v1"]
   1889     ),
   1890     kind_contract!(
   1891         KIND_LIST_EMOJIS,
   1892         "KIND_LIST_EMOJIS",
   1893         "Emojis List",
   1894         EventClass::Replaceable,
   1895         NostrStandard::Nip51,
   1896         ["radroots.list.emojis.v1"]
   1897     ),
   1898     kind_contract!(
   1899         KIND_LIST_DM_RELAYS,
   1900         "KIND_LIST_DM_RELAYS",
   1901         "DM Relays List",
   1902         EventClass::Replaceable,
   1903         NostrStandard::Nip51,
   1904         ["radroots.list.dm_relays.v1"]
   1905     ),
   1906     kind_contract!(
   1907         KIND_LIST_GOOD_WIKI_AUTHORS,
   1908         "KIND_LIST_GOOD_WIKI_AUTHORS",
   1909         "Good Wiki Authors List",
   1910         EventClass::Replaceable,
   1911         NostrStandard::Nip51,
   1912         ["radroots.list.good_wiki_authors.v1"]
   1913     ),
   1914     kind_contract!(
   1915         KIND_LIST_GOOD_WIKI_RELAYS,
   1916         "KIND_LIST_GOOD_WIKI_RELAYS",
   1917         "Good Wiki Relays List",
   1918         EventClass::Replaceable,
   1919         NostrStandard::Nip51,
   1920         ["radroots.list.good_wiki_relays.v1"]
   1921     ),
   1922     kind_contract!(
   1923         KIND_LIST_SET_FOLLOW,
   1924         "KIND_LIST_SET_FOLLOW",
   1925         "Follow Set",
   1926         EventClass::Addressable,
   1927         NostrStandard::Nip51,
   1928         ["radroots.list_set.follow.v1"]
   1929     ),
   1930     kind_contract!(
   1931         KIND_LIST_SET_GENERIC,
   1932         "KIND_LIST_SET_GENERIC",
   1933         "Generic List Set",
   1934         EventClass::Addressable,
   1935         NostrStandard::Nip51,
   1936         [
   1937             "radroots.list_set.farm.members.v1",
   1938             "radroots.list_set.farm.members.owners.v1",
   1939             "radroots.list_set.farm.members.workers.v1",
   1940             "radroots.list_set.farm.plots.v1",
   1941             "radroots.list_set.farm.listings.v1",
   1942             "radroots.list_set.member_of.farms.v1"
   1943         ]
   1944     ),
   1945     kind_contract!(
   1946         KIND_LIST_SET_RELAY,
   1947         "KIND_LIST_SET_RELAY",
   1948         "Relay Set",
   1949         EventClass::Addressable,
   1950         NostrStandard::Nip51,
   1951         ["radroots.list_set.relay.v1"]
   1952     ),
   1953     kind_contract!(
   1954         KIND_LIST_SET_BOOKMARK,
   1955         "KIND_LIST_SET_BOOKMARK",
   1956         "Bookmark Set",
   1957         EventClass::Addressable,
   1958         NostrStandard::Nip51,
   1959         ["radroots.list_set.bookmark.v1"]
   1960     ),
   1961     kind_contract!(
   1962         KIND_LIST_SET_CURATION,
   1963         "KIND_LIST_SET_CURATION",
   1964         "Curation Set",
   1965         EventClass::Addressable,
   1966         NostrStandard::Nip51,
   1967         ["radroots.list_set.curation.v1"]
   1968     ),
   1969     kind_contract!(
   1970         KIND_LIST_SET_VIDEO,
   1971         "KIND_LIST_SET_VIDEO",
   1972         "Video Set",
   1973         EventClass::Addressable,
   1974         NostrStandard::Nip51,
   1975         ["radroots.list_set.video.v1"]
   1976     ),
   1977     kind_contract!(
   1978         KIND_LIST_SET_PICTURE,
   1979         "KIND_LIST_SET_PICTURE",
   1980         "Picture Set",
   1981         EventClass::Addressable,
   1982         NostrStandard::Nip51,
   1983         ["radroots.list_set.picture.v1"]
   1984     ),
   1985     kind_contract!(
   1986         KIND_LIST_SET_KIND_MUTE,
   1987         "KIND_LIST_SET_KIND_MUTE",
   1988         "Kind Mute Set",
   1989         EventClass::Addressable,
   1990         NostrStandard::Nip51,
   1991         ["radroots.list_set.kind_mute.v1"]
   1992     ),
   1993     kind_contract!(
   1994         KIND_LIST_SET_INTEREST,
   1995         "KIND_LIST_SET_INTEREST",
   1996         "Interest Set",
   1997         EventClass::Addressable,
   1998         NostrStandard::Nip51,
   1999         ["radroots.list_set.interest.v1"]
   2000     ),
   2001     kind_contract!(
   2002         KIND_LIST_SET_EMOJI,
   2003         "KIND_LIST_SET_EMOJI",
   2004         "Emoji Set",
   2005         EventClass::Addressable,
   2006         NostrStandard::Nip51,
   2007         ["radroots.list_set.emoji.v1"]
   2008     ),
   2009     kind_contract!(
   2010         KIND_LIST_SET_RELEASE_ARTIFACT,
   2011         "KIND_LIST_SET_RELEASE_ARTIFACT",
   2012         "Release Artifact Set",
   2013         EventClass::Addressable,
   2014         NostrStandard::Nip51,
   2015         ["radroots.list_set.release_artifact.v1"]
   2016     ),
   2017     kind_contract!(
   2018         KIND_LIST_SET_APP_CURATION,
   2019         "KIND_LIST_SET_APP_CURATION",
   2020         "App Curation Set",
   2021         EventClass::Addressable,
   2022         NostrStandard::Nip51,
   2023         ["radroots.list_set.app_curation.v1"]
   2024     ),
   2025     kind_contract!(
   2026         KIND_ARTICLE,
   2027         "KIND_ARTICLE",
   2028         "Long Form Article",
   2029         EventClass::Addressable,
   2030         NostrStandard::Nip23,
   2031         ["radroots.social.article.v1"]
   2032     ),
   2033     kind_contract!(
   2034         KIND_WIKI_MERGE_REQUEST,
   2035         "KIND_WIKI_MERGE_REQUEST",
   2036         "Wiki Merge Request",
   2037         EventClass::Regular,
   2038         NostrStandard::Nip54,
   2039         ["radroots.wiki.merge_request.v1"]
   2040     ),
   2041     kind_contract!(
   2042         KIND_WIKI_ARTICLE,
   2043         "KIND_WIKI_ARTICLE",
   2044         "Wiki Article",
   2045         EventClass::Addressable,
   2046         NostrStandard::Nip54,
   2047         ["radroots.wiki.article.v1"]
   2048     ),
   2049     kind_contract!(
   2050         KIND_WIKI_REDIRECT,
   2051         "KIND_WIKI_REDIRECT",
   2052         "Wiki Redirect",
   2053         EventClass::Addressable,
   2054         NostrStandard::Nip54,
   2055         ["radroots.wiki.redirect.v1"]
   2056     ),
   2057     kind_contract!(
   2058         KIND_CALENDAR_DATE_EVENT,
   2059         "KIND_CALENDAR_DATE_EVENT",
   2060         "Calendar Date Event",
   2061         EventClass::Addressable,
   2062         NostrStandard::Nip52,
   2063         ["radroots.calendar.date_event.v1"]
   2064     ),
   2065     kind_contract!(
   2066         KIND_CALENDAR_TIME_EVENT,
   2067         "KIND_CALENDAR_TIME_EVENT",
   2068         "Calendar Time Event",
   2069         EventClass::Addressable,
   2070         NostrStandard::Nip52,
   2071         ["radroots.calendar.time_event.v1"]
   2072     ),
   2073     kind_contract!(
   2074         KIND_CALENDAR,
   2075         "KIND_CALENDAR",
   2076         "Calendar Collection",
   2077         EventClass::Addressable,
   2078         NostrStandard::Nip52,
   2079         ["radroots.calendar.collection.v1"]
   2080     ),
   2081     kind_contract!(
   2082         KIND_CALENDAR_EVENT_RSVP,
   2083         "KIND_CALENDAR_EVENT_RSVP",
   2084         "Calendar RSVP",
   2085         EventClass::Addressable,
   2086         NostrStandard::Nip52,
   2087         ["radroots.calendar.rsvp.v1"]
   2088     ),
   2089     kind_contract!(
   2090         KIND_LIST_SET_STARTER_PACK,
   2091         "KIND_LIST_SET_STARTER_PACK",
   2092         "Starter Pack Set",
   2093         EventClass::Addressable,
   2094         NostrStandard::Nip51,
   2095         ["radroots.list_set.starter_pack.v1"]
   2096     ),
   2097     kind_contract!(
   2098         KIND_LIST_SET_MEDIA_STARTER_PACK,
   2099         "KIND_LIST_SET_MEDIA_STARTER_PACK",
   2100         "Media Starter Pack Set",
   2101         EventClass::Addressable,
   2102         NostrStandard::Nip51,
   2103         ["radroots.list_set.media_starter_pack.v1"]
   2104     ),
   2105     kind_contract!(
   2106         KIND_FARM,
   2107         "KIND_FARM",
   2108         "Farm",
   2109         EventClass::Addressable,
   2110         NostrStandard::Radroots,
   2111         ["radroots.farm.profile.v1"]
   2112     ),
   2113     kind_contract!(
   2114         KIND_PLOT,
   2115         "KIND_PLOT",
   2116         "Plot",
   2117         EventClass::Addressable,
   2118         NostrStandard::Radroots,
   2119         ["radroots.farm.plot.v1"]
   2120     ),
   2121     kind_contract!(
   2122         KIND_COOP,
   2123         "KIND_COOP",
   2124         "Coop",
   2125         EventClass::Addressable,
   2126         NostrStandard::Radroots,
   2127         ["radroots.farm.coop.v1"]
   2128     ),
   2129     kind_contract!(
   2130         KIND_DOCUMENT,
   2131         "KIND_DOCUMENT",
   2132         "Document",
   2133         EventClass::Addressable,
   2134         NostrStandard::Radroots,
   2135         ["radroots.farm.document.v1"]
   2136     ),
   2137     kind_contract!(
   2138         KIND_RESOURCE_AREA,
   2139         "KIND_RESOURCE_AREA",
   2140         "Resource Area",
   2141         EventClass::Addressable,
   2142         NostrStandard::Radroots,
   2143         ["radroots.farm.resource_area.v1"]
   2144     ),
   2145     kind_contract!(
   2146         KIND_RESOURCE_HARVEST_CAP,
   2147         "KIND_RESOURCE_HARVEST_CAP",
   2148         "Resource Harvest Capacity",
   2149         EventClass::Addressable,
   2150         NostrStandard::Radroots,
   2151         ["radroots.farm.resource_harvest_cap.v1"]
   2152     ),
   2153     kind_contract!(
   2154         KIND_ACCOUNT_CLAIM,
   2155         "KIND_ACCOUNT_CLAIM",
   2156         "Account Claim",
   2157         EventClass::Addressable,
   2158         NostrStandard::Radroots,
   2159         ["radroots.account.claim.v1"]
   2160     ),
   2161     kind_contract!(
   2162         KIND_FARM_WORKSPACE_MANIFEST,
   2163         "KIND_FARM_WORKSPACE_MANIFEST",
   2164         "Farm Workspace Manifest",
   2165         EventClass::Addressable,
   2166         NostrStandard::Nip78,
   2167         ["radroots.farm.workspace_manifest.v1"]
   2168     ),
   2169     kind_contract!(
   2170         KIND_CLASSIFIED_LISTING,
   2171         "KIND_CLASSIFIED_LISTING",
   2172         "Classified Listing",
   2173         EventClass::Addressable,
   2174         NostrStandard::Nip99,
   2175         [
   2176             "radroots.operational_listing.published.v1",
   2177             "radroots.food.availability.v1"
   2178         ]
   2179     ),
   2180     kind_contract!(
   2181         KIND_KNOWLEDGE_SOURCE,
   2182         "KIND_KNOWLEDGE_SOURCE",
   2183         "Knowledge Source",
   2184         EventClass::Addressable,
   2185         NostrStandard::Radroots,
   2186         ["radroots.knowledge.source.v1"]
   2187     ),
   2188     kind_contract!(
   2189         KIND_EVIDENCE_BOUNTY,
   2190         "KIND_EVIDENCE_BOUNTY",
   2191         "Evidence Bounty",
   2192         EventClass::Addressable,
   2193         NostrStandard::Radroots,
   2194         ["radroots.knowledge.evidence_bounty.v1"]
   2195     ),
   2196     kind_contract!(
   2197         KIND_KNOWLEDGE_CLAIM,
   2198         "KIND_KNOWLEDGE_CLAIM",
   2199         "Knowledge Claim",
   2200         EventClass::Regular,
   2201         NostrStandard::Radroots,
   2202         ["radroots.knowledge.claim.v1"]
   2203     ),
   2204     kind_contract!(
   2205         KIND_KNOWLEDGE_RELATION,
   2206         "KIND_KNOWLEDGE_RELATION",
   2207         "Knowledge Relation",
   2208         EventClass::Regular,
   2209         NostrStandard::Radroots,
   2210         ["radroots.knowledge.relation.v1"]
   2211     ),
   2212     kind_contract!(
   2213         KIND_KNOWLEDGE_REVIEW,
   2214         "KIND_KNOWLEDGE_REVIEW",
   2215         "Knowledge Review",
   2216         EventClass::Regular,
   2217         NostrStandard::Radroots,
   2218         ["radroots.knowledge.review.v1"]
   2219     ),
   2220     kind_contract!(
   2221         KIND_KNOWLEDGE_FIELD_REPORT,
   2222         "KIND_KNOWLEDGE_FIELD_REPORT",
   2223         "Knowledge Field Report",
   2224         EventClass::Regular,
   2225         NostrStandard::Radroots,
   2226         ["radroots.knowledge.field_report.v1"]
   2227     ),
   2228     kind_contract!(
   2229         KIND_KNOWLEDGE_CHANGE_PROPOSAL,
   2230         "KIND_KNOWLEDGE_CHANGE_PROPOSAL",
   2231         "Knowledge Change Proposal",
   2232         EventClass::Regular,
   2233         NostrStandard::Radroots,
   2234         ["radroots.knowledge.change_proposal.v1"]
   2235     ),
   2236     kind_contract!(
   2237         KIND_CONTRIBUTION_ATTESTATION,
   2238         "KIND_CONTRIBUTION_ATTESTATION",
   2239         "Contribution Attestation",
   2240         EventClass::Regular,
   2241         NostrStandard::Radroots,
   2242         ["radroots.knowledge.contribution_attestation.v1"]
   2243     ),
   2244     kind_contract!(
   2245         KIND_APPLICATION_HANDLER,
   2246         "KIND_APPLICATION_HANDLER",
   2247         "Application Handler",
   2248         EventClass::Addressable,
   2249         NostrStandard::Radroots,
   2250         ["radroots.application.handler.v1"]
   2251     ),
   2252     kind_contract!(
   2253         KIND_GROUP_METADATA,
   2254         "KIND_GROUP_METADATA",
   2255         "Group Metadata",
   2256         EventClass::Addressable,
   2257         NostrStandard::Nip29,
   2258         ["radroots.group.metadata.v1"]
   2259     ),
   2260     kind_contract!(
   2261         KIND_GROUP_ADMINS,
   2262         "KIND_GROUP_ADMINS",
   2263         "Group Admins",
   2264         EventClass::Addressable,
   2265         NostrStandard::Nip29,
   2266         ["radroots.group.admins.v1"]
   2267     ),
   2268     kind_contract!(
   2269         KIND_GROUP_MEMBERS,
   2270         "KIND_GROUP_MEMBERS",
   2271         "Group Members",
   2272         EventClass::Addressable,
   2273         NostrStandard::Nip29,
   2274         ["radroots.group.members.v1"]
   2275     ),
   2276     kind_contract!(
   2277         KIND_GROUP_ROLES,
   2278         "KIND_GROUP_ROLES",
   2279         "Group Roles",
   2280         EventClass::Addressable,
   2281         NostrStandard::Nip29,
   2282         ["radroots.group.roles.v1"]
   2283     ),
   2284     kind_contract!(
   2285         KIND_TRADE_PROPOSAL,
   2286         "KIND_TRADE_PROPOSAL",
   2287         "Trade Proposal",
   2288         EventClass::Regular,
   2289         NostrStandard::Radroots,
   2290         ["radroots.trade.proposal.v1"]
   2291     ),
   2292     kind_contract!(
   2293         KIND_TRADE_DECISION,
   2294         "KIND_TRADE_DECISION",
   2295         "Trade Decision",
   2296         EventClass::Regular,
   2297         NostrStandard::Radroots,
   2298         ["radroots.trade.decision.v1"]
   2299     ),
   2300     kind_contract!(
   2301         KIND_TRADE_REVISION_PROPOSAL,
   2302         "KIND_TRADE_REVISION_PROPOSAL",
   2303         "Trade Revision Proposal",
   2304         EventClass::Regular,
   2305         NostrStandard::Radroots,
   2306         ["radroots.trade.revision_proposal.v1"]
   2307     ),
   2308     kind_contract!(
   2309         KIND_TRADE_REVISION_DECISION,
   2310         "KIND_TRADE_REVISION_DECISION",
   2311         "Trade Revision Decision",
   2312         EventClass::Regular,
   2313         NostrStandard::Radroots,
   2314         ["radroots.trade.revision_decision.v1"]
   2315     ),
   2316     kind_contract!(
   2317         KIND_TRADE_CANCELLATION,
   2318         "KIND_TRADE_CANCELLATION",
   2319         "Trade Cancellation",
   2320         EventClass::Regular,
   2321         NostrStandard::Radroots,
   2322         ["radroots.trade.cancellation.v1"]
   2323     ),
   2324     kind_contract!(
   2325         KIND_RHI_EVIDENCE_ATTESTATION,
   2326         "KIND_RHI_EVIDENCE_ATTESTATION",
   2327         "RHI Evidence Attestation",
   2328         EventClass::Regular,
   2329         NostrStandard::Radroots,
   2330         ["radroots.rhi.evidence_attestation.v1"]
   2331     ),
   2332 ];
   2333 
   2334 static EVENT_CONTRACTS_REGISTRY_V7: &[EventContract] = &[
   2335     event_contract_with_authoring_policy!(
   2336         "radroots.profile.metadata.v1",
   2337         KIND_PROFILE,
   2338         "Profile Metadata",
   2339         "RadrootsAuthoredProfile / RadrootsInboundProfileMetadata",
   2340         EventClass::Replaceable,
   2341         EventPrivacy::Public,
   2342         AuthorRole::Any,
   2343         ContentSchema::JsonObject,
   2344         EventAuthoringPolicy::TypedOnly,
   2345         EventDiscriminator::KindOnly,
   2346         PROFILE_TAGS,
   2347         PROFILE_REDUCERS
   2348     ),
   2349     event_contract_with_authoring_policy!(
   2350         "radroots.social.post.v1",
   2351         KIND_POST,
   2352         "Short Text Note",
   2353         "RadrootsPost",
   2354         EventClass::Regular,
   2355         EventPrivacy::Public,
   2356         AuthorRole::Any,
   2357         ContentSchema::PlainText,
   2358         EventAuthoringPolicy::ReadOnly,
   2359         EventDiscriminator::KindOnly,
   2360         NO_TAGS,
   2361         SOCIAL_REDUCERS
   2362     ),
   2363     event_contract_with_authoring_policy!(
   2364         "radroots.social.update.v1",
   2365         KIND_POST,
   2366         "Root Text Update",
   2367         "RadrootsAuthoredUpdate / RadrootsInboundPostProjection",
   2368         EventClass::Regular,
   2369         EventPrivacy::Public,
   2370         AuthorRole::Any,
   2371         ContentSchema::PlainText,
   2372         EventAuthoringPolicy::TypedOnly,
   2373         EventDiscriminator::AdmissionOnly,
   2374         NO_TAGS,
   2375         SOCIAL_REDUCERS
   2376     ),
   2377     event_contract_with_authoring_policy!(
   2378         "radroots.social.photo_update.v1",
   2379         KIND_POST,
   2380         "NIP-92 Photo Update",
   2381         "RadrootsAuthoredPhotoUpdate / RadrootsInboundPostProjection",
   2382         EventClass::Regular,
   2383         EventPrivacy::Public,
   2384         AuthorRole::Any,
   2385         ContentSchema::PlainText,
   2386         EventAuthoringPolicy::TypedOnly,
   2387         EventDiscriminator::AdmissionOnly,
   2388         PHOTO_UPDATE_TAGS,
   2389         SOCIAL_REDUCERS
   2390     ),
   2391     event_contract_with_authoring_policy!(
   2392         "radroots.social.ask.v1",
   2393         KIND_POST,
   2394         "Root Ask",
   2395         "RadrootsAuthoredAsk / RadrootsInboundPostProjection",
   2396         EventClass::Regular,
   2397         EventPrivacy::Public,
   2398         AuthorRole::Any,
   2399         ContentSchema::PlainText,
   2400         EventAuthoringPolicy::TypedOnly,
   2401         EventDiscriminator::AdmissionOnly,
   2402         ASK_TAGS,
   2403         SOCIAL_REDUCERS
   2404     ),
   2405     event_contract_with_authoring_policy!(
   2406         "radroots.social.reply.v1",
   2407         KIND_POST,
   2408         "NIP-10 Reply",
   2409         "RadrootsAuthoredNip10Reply / RadrootsInboundNip10ReplyProjection",
   2410         EventClass::Regular,
   2411         EventPrivacy::Public,
   2412         AuthorRole::Any,
   2413         ContentSchema::PlainText,
   2414         EventAuthoringPolicy::TypedOnly,
   2415         EventDiscriminator::AdmissionOnly,
   2416         NIP10_REPLY_TAGS,
   2417         SOCIAL_REDUCERS
   2418     ),
   2419     event_contract!(
   2420         "radroots.social.follow_list.v1",
   2421         KIND_FOLLOW,
   2422         "Contact List",
   2423         "RadrootsFollowList",
   2424         EventClass::Replaceable,
   2425         EventPrivacy::Public,
   2426         AuthorRole::Any,
   2427         ContentSchema::JsonObject,
   2428         EventDiscriminator::KindOnly,
   2429         P_TAGS,
   2430         PROFILE_REDUCERS
   2431     ),
   2432     event_contract_with_authoring_policy!(
   2433         "radroots.social.deletion_request.v1",
   2434         KIND_DELETION_REQUEST,
   2435         "Deletion Request",
   2436         "RadrootsAuthoredNip09DeletionRequest / RadrootsInboundNip09DeletionProjection",
   2437         EventClass::Regular,
   2438         EventPrivacy::Public,
   2439         AuthorRole::Any,
   2440         ContentSchema::PlainText,
   2441         EventAuthoringPolicy::TypedOnly,
   2442         EventDiscriminator::AdmissionOnly,
   2443         NIP09_DELETION_TAGS,
   2444         SOCIAL_REDUCERS
   2445     ),
   2446     event_contract!(
   2447         "radroots.social.repost.v1",
   2448         KIND_REPOST,
   2449         "Repost",
   2450         "RadrootsRepost",
   2451         EventClass::Regular,
   2452         EventPrivacy::Public,
   2453         AuthorRole::Any,
   2454         ContentSchema::JsonObject,
   2455         EventDiscriminator::KindOnly,
   2456         EVENT_POINTER_TAGS,
   2457         SOCIAL_REDUCERS
   2458     ),
   2459     event_contract!(
   2460         "radroots.social.reaction.v1",
   2461         KIND_REACTION,
   2462         "Reaction",
   2463         "RadrootsReaction",
   2464         EventClass::Regular,
   2465         EventPrivacy::Public,
   2466         AuthorRole::Any,
   2467         ContentSchema::PlainText,
   2468         EventDiscriminator::KindOnly,
   2469         EVENT_POINTER_TAGS,
   2470         SOCIAL_REDUCERS
   2471     ),
   2472     event_contract!(
   2473         "radroots.message.seal.v1",
   2474         KIND_SEAL,
   2475         "Seal",
   2476         "RadrootsSeal",
   2477         EventClass::Regular,
   2478         EventPrivacy::Encrypted,
   2479         AuthorRole::Any,
   2480         ContentSchema::Encrypted,
   2481         EventDiscriminator::KindOnly,
   2482         NO_TAGS,
   2483         SOCIAL_REDUCERS
   2484     ),
   2485     event_contract!(
   2486         "radroots.message.private.v1",
   2487         KIND_MESSAGE,
   2488         "Direct Message",
   2489         "RadrootsMessage",
   2490         EventClass::Regular,
   2491         EventPrivacy::Encrypted,
   2492         AuthorRole::Any,
   2493         ContentSchema::Encrypted,
   2494         EventDiscriminator::KindOnly,
   2495         P_TAGS,
   2496         SOCIAL_REDUCERS
   2497     ),
   2498     event_contract!(
   2499         "radroots.message.file.v1",
   2500         KIND_MESSAGE_FILE,
   2501         "Direct Message File",
   2502         "RadrootsMessageFile",
   2503         EventClass::Regular,
   2504         EventPrivacy::Encrypted,
   2505         AuthorRole::Any,
   2506         ContentSchema::Encrypted,
   2507         EventDiscriminator::KindOnly,
   2508         P_TAGS,
   2509         SOCIAL_REDUCERS
   2510     ),
   2511     event_contract!(
   2512         "radroots.social.generic_repost.v1",
   2513         KIND_GENERIC_REPOST,
   2514         "Generic Repost",
   2515         "RadrootsGenericRepost",
   2516         EventClass::Regular,
   2517         EventPrivacy::Public,
   2518         AuthorRole::Any,
   2519         ContentSchema::JsonObject,
   2520         EventDiscriminator::KindOnly,
   2521         EVENT_POINTER_TAGS,
   2522         SOCIAL_REDUCERS
   2523     ),
   2524     event_contract!(
   2525         "radroots.farm.crdt_change.v1",
   2526         KIND_FARM_CRDT_CHANGE,
   2527         "Farm CRDT Change",
   2528         "RadrootsFarmCrdtChange",
   2529         EventClass::Regular,
   2530         EventPrivacy::Encrypted,
   2531         AuthorRole::Farmer,
   2532         ContentSchema::JsonObject,
   2533         EventDiscriminator::KindOnly,
   2534         NO_TAGS,
   2535         FARM_OPS_REDUCERS
   2536     ),
   2537     event_contract!(
   2538         "radroots.message.gift_wrap.v1",
   2539         KIND_GIFT_WRAP,
   2540         "Gift Wrap",
   2541         "RadrootsGiftWrap",
   2542         EventClass::Regular,
   2543         EventPrivacy::Encrypted,
   2544         AuthorRole::Any,
   2545         ContentSchema::Encrypted,
   2546         EventDiscriminator::KindOnly,
   2547         P_TAGS,
   2548         SOCIAL_REDUCERS
   2549     ),
   2550     event_contract!(
   2551         "radroots.file.metadata.v1",
   2552         KIND_FILE_METADATA,
   2553         "File Metadata",
   2554         "RadrootsFileMetadata",
   2555         EventClass::Regular,
   2556         EventPrivacy::Public,
   2557         AuthorRole::Any,
   2558         ContentSchema::JsonObject,
   2559         EventDiscriminator::KindOnly,
   2560         FILE_METADATA_TAGS,
   2561         SOCIAL_REDUCERS
   2562     ),
   2563     event_contract_with_authoring_policy!(
   2564         "radroots.social.comment.v1",
   2565         KIND_COMMENT,
   2566         "Comment",
   2567         "RadrootsAuthoredNip22Comment / RadrootsInboundNip22CommentProjection",
   2568         EventClass::Regular,
   2569         EventPrivacy::Public,
   2570         AuthorRole::Any,
   2571         ContentSchema::PlainText,
   2572         EventAuthoringPolicy::TypedOnly,
   2573         EventDiscriminator::AdmissionOnly,
   2574         NIP22_COMMENT_TAGS,
   2575         SOCIAL_REDUCERS
   2576     ),
   2577     event_contract!(
   2578         "radroots.social.report.v1",
   2579         KIND_REPORT,
   2580         "Report",
   2581         "RadrootsReport",
   2582         EventClass::Regular,
   2583         EventPrivacy::Public,
   2584         AuthorRole::Moderator,
   2585         ContentSchema::PlainText,
   2586         EventDiscriminator::KindOnly,
   2587         EVENT_POINTER_TAGS,
   2588         SOCIAL_REDUCERS
   2589     ),
   2590     event_contract!(
   2591         "radroots.group.put_user.v1",
   2592         KIND_GROUP_PUT_USER,
   2593         "Group Put User",
   2594         "RadrootsGroupPutUser",
   2595         EventClass::Regular,
   2596         EventPrivacy::Public,
   2597         AuthorRole::Moderator,
   2598         ContentSchema::JsonObject,
   2599         EventDiscriminator::KindOnly,
   2600         GROUP_ACTION_TAGS,
   2601         GROUP_REDUCERS
   2602     ),
   2603     event_contract!(
   2604         "radroots.group.remove_user.v1",
   2605         KIND_GROUP_REMOVE_USER,
   2606         "Group Remove User",
   2607         "RadrootsGroupRemoveUser",
   2608         EventClass::Regular,
   2609         EventPrivacy::Public,
   2610         AuthorRole::Moderator,
   2611         ContentSchema::JsonObject,
   2612         EventDiscriminator::KindOnly,
   2613         GROUP_ACTION_TAGS,
   2614         GROUP_REDUCERS
   2615     ),
   2616     event_contract!(
   2617         "radroots.group.edit_metadata.v1",
   2618         KIND_GROUP_EDIT_METADATA,
   2619         "Group Edit Metadata",
   2620         "RadrootsGroupEditMetadata",
   2621         EventClass::Regular,
   2622         EventPrivacy::Public,
   2623         AuthorRole::Moderator,
   2624         ContentSchema::JsonObject,
   2625         EventDiscriminator::KindOnly,
   2626         GROUP_ACTION_TAGS,
   2627         GROUP_REDUCERS
   2628     ),
   2629     event_contract!(
   2630         "radroots.group.delete_event.v1",
   2631         KIND_GROUP_DELETE_EVENT,
   2632         "Group Delete Event",
   2633         "RadrootsGroupDeleteEvent",
   2634         EventClass::Regular,
   2635         EventPrivacy::Public,
   2636         AuthorRole::Moderator,
   2637         ContentSchema::JsonObject,
   2638         EventDiscriminator::KindOnly,
   2639         GROUP_ACTION_TAGS,
   2640         GROUP_REDUCERS
   2641     ),
   2642     event_contract!(
   2643         "radroots.group.create_group.v1",
   2644         KIND_GROUP_CREATE_GROUP,
   2645         "Group Create Group",
   2646         "RadrootsGroupCreateGroup",
   2647         EventClass::Regular,
   2648         EventPrivacy::Public,
   2649         AuthorRole::Moderator,
   2650         ContentSchema::JsonObject,
   2651         EventDiscriminator::KindOnly,
   2652         GROUP_ACTION_TAGS,
   2653         GROUP_REDUCERS
   2654     ),
   2655     event_contract!(
   2656         "radroots.group.delete_group.v1",
   2657         KIND_GROUP_DELETE_GROUP,
   2658         "Group Delete Group",
   2659         "RadrootsGroupDeleteGroup",
   2660         EventClass::Regular,
   2661         EventPrivacy::Public,
   2662         AuthorRole::Moderator,
   2663         ContentSchema::JsonObject,
   2664         EventDiscriminator::KindOnly,
   2665         GROUP_ACTION_TAGS,
   2666         GROUP_REDUCERS
   2667     ),
   2668     event_contract!(
   2669         "radroots.group.create_invite.v1",
   2670         KIND_GROUP_CREATE_INVITE,
   2671         "Group Create Invite",
   2672         "RadrootsGroupCreateInvite",
   2673         EventClass::Regular,
   2674         EventPrivacy::Public,
   2675         AuthorRole::Moderator,
   2676         ContentSchema::JsonObject,
   2677         EventDiscriminator::KindOnly,
   2678         GROUP_ACTION_TAGS,
   2679         GROUP_REDUCERS
   2680     ),
   2681     event_contract!(
   2682         "radroots.group.join_request.v1",
   2683         KIND_GROUP_JOIN_REQUEST,
   2684         "Group Join Request",
   2685         "RadrootsGroupJoinRequest",
   2686         EventClass::Regular,
   2687         EventPrivacy::Public,
   2688         AuthorRole::Member,
   2689         ContentSchema::JsonObject,
   2690         EventDiscriminator::KindOnly,
   2691         GROUP_ACTION_TAGS,
   2692         GROUP_REDUCERS
   2693     ),
   2694     event_contract!(
   2695         "radroots.group.leave_request.v1",
   2696         KIND_GROUP_LEAVE_REQUEST,
   2697         "Group Leave Request",
   2698         "RadrootsGroupLeaveRequest",
   2699         EventClass::Regular,
   2700         EventPrivacy::Public,
   2701         AuthorRole::Member,
   2702         ContentSchema::JsonObject,
   2703         EventDiscriminator::KindOnly,
   2704         GROUP_ACTION_TAGS,
   2705         GROUP_REDUCERS
   2706     ),
   2707     event_contract!(
   2708         "radroots.social.geochat.v1",
   2709         KIND_GEOCHAT,
   2710         "Geochat",
   2711         "RadrootsGeochat",
   2712         EventClass::Ephemeral,
   2713         EventPrivacy::Public,
   2714         AuthorRole::Any,
   2715         ContentSchema::PlainText,
   2716         EventDiscriminator::KindOnly,
   2717         NO_TAGS,
   2718         SOCIAL_REDUCERS
   2719     ),
   2720     event_contract!(
   2721         "radroots.relay.auth.v1",
   2722         KIND_RELAY_AUTH,
   2723         "Relay Auth",
   2724         "RadrootsRelayAuth",
   2725         EventClass::Ephemeral,
   2726         EventPrivacy::Public,
   2727         AuthorRole::Relay,
   2728         ContentSchema::JsonObject,
   2729         EventDiscriminator::KindOnly,
   2730         NO_TAGS,
   2731         RELAY_REDUCERS
   2732     ),
   2733     event_contract!(
   2734         "radroots.http.auth.v1",
   2735         KIND_HTTP_AUTH,
   2736         "HTTP Auth",
   2737         "RadrootsHttpAuth",
   2738         EventClass::Ephemeral,
   2739         EventPrivacy::Public,
   2740         AuthorRole::Application,
   2741         ContentSchema::JsonObject,
   2742         EventDiscriminator::KindOnly,
   2743         NO_TAGS,
   2744         RELAY_REDUCERS
   2745     ),
   2746     event_contract!(
   2747         "radroots.list.mute.v1",
   2748         KIND_LIST_MUTE,
   2749         "Mute List",
   2750         "RadrootsList",
   2751         EventClass::Replaceable,
   2752         EventPrivacy::Public,
   2753         AuthorRole::Any,
   2754         ContentSchema::JsonObject,
   2755         EventDiscriminator::KindOnly,
   2756         LIST_TAGS,
   2757         SOCIAL_REDUCERS
   2758     ),
   2759     event_contract!(
   2760         "radroots.list.pinned_notes.v1",
   2761         KIND_LIST_PINNED_NOTES,
   2762         "Pinned Notes List",
   2763         "RadrootsList",
   2764         EventClass::Replaceable,
   2765         EventPrivacy::Public,
   2766         AuthorRole::Any,
   2767         ContentSchema::JsonObject,
   2768         EventDiscriminator::KindOnly,
   2769         LIST_TAGS,
   2770         SOCIAL_REDUCERS
   2771     ),
   2772     event_contract!(
   2773         "radroots.list.read_write_relays.v1",
   2774         KIND_LIST_READ_WRITE_RELAYS,
   2775         "Read Write Relays List",
   2776         "RadrootsList",
   2777         EventClass::Replaceable,
   2778         EventPrivacy::Public,
   2779         AuthorRole::Any,
   2780         ContentSchema::JsonObject,
   2781         EventDiscriminator::KindOnly,
   2782         LIST_TAGS,
   2783         SOCIAL_REDUCERS
   2784     ),
   2785     event_contract!(
   2786         "radroots.list.bookmarks.v1",
   2787         KIND_LIST_BOOKMARKS,
   2788         "Bookmarks List",
   2789         "RadrootsList",
   2790         EventClass::Replaceable,
   2791         EventPrivacy::Public,
   2792         AuthorRole::Any,
   2793         ContentSchema::JsonObject,
   2794         EventDiscriminator::KindOnly,
   2795         LIST_TAGS,
   2796         SOCIAL_REDUCERS
   2797     ),
   2798     event_contract!(
   2799         "radroots.list.communities.v1",
   2800         KIND_LIST_COMMUNITIES,
   2801         "Communities List",
   2802         "RadrootsList",
   2803         EventClass::Replaceable,
   2804         EventPrivacy::Public,
   2805         AuthorRole::Any,
   2806         ContentSchema::JsonObject,
   2807         EventDiscriminator::KindOnly,
   2808         LIST_TAGS,
   2809         SOCIAL_REDUCERS
   2810     ),
   2811     event_contract!(
   2812         "radroots.list.public_chats.v1",
   2813         KIND_LIST_PUBLIC_CHATS,
   2814         "Public Chats List",
   2815         "RadrootsList",
   2816         EventClass::Replaceable,
   2817         EventPrivacy::Public,
   2818         AuthorRole::Any,
   2819         ContentSchema::JsonObject,
   2820         EventDiscriminator::KindOnly,
   2821         LIST_TAGS,
   2822         SOCIAL_REDUCERS
   2823     ),
   2824     event_contract!(
   2825         "radroots.list.blocked_relays.v1",
   2826         KIND_LIST_BLOCKED_RELAYS,
   2827         "Blocked Relays List",
   2828         "RadrootsList",
   2829         EventClass::Replaceable,
   2830         EventPrivacy::Public,
   2831         AuthorRole::Any,
   2832         ContentSchema::JsonObject,
   2833         EventDiscriminator::KindOnly,
   2834         LIST_TAGS,
   2835         SOCIAL_REDUCERS
   2836     ),
   2837     event_contract!(
   2838         "radroots.list.search_relays.v1",
   2839         KIND_LIST_SEARCH_RELAYS,
   2840         "Search Relays List",
   2841         "RadrootsList",
   2842         EventClass::Replaceable,
   2843         EventPrivacy::Public,
   2844         AuthorRole::Any,
   2845         ContentSchema::JsonObject,
   2846         EventDiscriminator::KindOnly,
   2847         LIST_TAGS,
   2848         SOCIAL_REDUCERS
   2849     ),
   2850     event_contract!(
   2851         "radroots.list.simple_groups.v1",
   2852         KIND_LIST_SIMPLE_GROUPS,
   2853         "Simple Groups List",
   2854         "RadrootsList",
   2855         EventClass::Replaceable,
   2856         EventPrivacy::Public,
   2857         AuthorRole::Any,
   2858         ContentSchema::JsonObject,
   2859         EventDiscriminator::KindOnly,
   2860         LIST_TAGS,
   2861         SOCIAL_REDUCERS
   2862     ),
   2863     event_contract!(
   2864         "radroots.list.relay_feeds.v1",
   2865         KIND_LIST_RELAY_FEEDS,
   2866         "Relay Feeds List",
   2867         "RadrootsList",
   2868         EventClass::Replaceable,
   2869         EventPrivacy::Public,
   2870         AuthorRole::Any,
   2871         ContentSchema::JsonObject,
   2872         EventDiscriminator::KindOnly,
   2873         LIST_TAGS,
   2874         SOCIAL_REDUCERS
   2875     ),
   2876     event_contract!(
   2877         "radroots.list.interests.v1",
   2878         KIND_LIST_INTERESTS,
   2879         "Interests List",
   2880         "RadrootsList",
   2881         EventClass::Replaceable,
   2882         EventPrivacy::Public,
   2883         AuthorRole::Any,
   2884         ContentSchema::JsonObject,
   2885         EventDiscriminator::KindOnly,
   2886         LIST_TAGS,
   2887         SOCIAL_REDUCERS
   2888     ),
   2889     event_contract!(
   2890         "radroots.list.media_follows.v1",
   2891         KIND_LIST_MEDIA_FOLLOWS,
   2892         "Media Follows List",
   2893         "RadrootsList",
   2894         EventClass::Replaceable,
   2895         EventPrivacy::Public,
   2896         AuthorRole::Any,
   2897         ContentSchema::JsonObject,
   2898         EventDiscriminator::KindOnly,
   2899         LIST_TAGS,
   2900         SOCIAL_REDUCERS
   2901     ),
   2902     event_contract!(
   2903         "radroots.list.emojis.v1",
   2904         KIND_LIST_EMOJIS,
   2905         "Emojis List",
   2906         "RadrootsList",
   2907         EventClass::Replaceable,
   2908         EventPrivacy::Public,
   2909         AuthorRole::Any,
   2910         ContentSchema::JsonObject,
   2911         EventDiscriminator::KindOnly,
   2912         LIST_TAGS,
   2913         SOCIAL_REDUCERS
   2914     ),
   2915     event_contract!(
   2916         "radroots.list.dm_relays.v1",
   2917         KIND_LIST_DM_RELAYS,
   2918         "DM Relays List",
   2919         "RadrootsList",
   2920         EventClass::Replaceable,
   2921         EventPrivacy::Public,
   2922         AuthorRole::Any,
   2923         ContentSchema::JsonObject,
   2924         EventDiscriminator::KindOnly,
   2925         LIST_TAGS,
   2926         SOCIAL_REDUCERS
   2927     ),
   2928     event_contract!(
   2929         "radroots.list.good_wiki_authors.v1",
   2930         KIND_LIST_GOOD_WIKI_AUTHORS,
   2931         "Good Wiki Authors List",
   2932         "RadrootsList",
   2933         EventClass::Replaceable,
   2934         EventPrivacy::Public,
   2935         AuthorRole::Any,
   2936         ContentSchema::JsonObject,
   2937         EventDiscriminator::KindOnly,
   2938         LIST_TAGS,
   2939         SOCIAL_REDUCERS
   2940     ),
   2941     event_contract!(
   2942         "radroots.list.good_wiki_relays.v1",
   2943         KIND_LIST_GOOD_WIKI_RELAYS,
   2944         "Good Wiki Relays List",
   2945         "RadrootsList",
   2946         EventClass::Replaceable,
   2947         EventPrivacy::Public,
   2948         AuthorRole::Any,
   2949         ContentSchema::JsonObject,
   2950         EventDiscriminator::KindOnly,
   2951         LIST_TAGS,
   2952         SOCIAL_REDUCERS
   2953     ),
   2954     event_contract!(
   2955         "radroots.list_set.follow.v1",
   2956         KIND_LIST_SET_FOLLOW,
   2957         "Follow Set",
   2958         "RadrootsListSet",
   2959         EventClass::Addressable,
   2960         EventPrivacy::Public,
   2961         AuthorRole::Any,
   2962         ContentSchema::JsonObject,
   2963         EventDiscriminator::KindOnly,
   2964         LIST_SET_TAGS,
   2965         SOCIAL_REDUCERS
   2966     ),
   2967     event_contract!(
   2968         "radroots.list_set.farm.members.v1",
   2969         KIND_LIST_SET_GENERIC,
   2970         "Farm Members List Set",
   2971         "RadrootsListSet",
   2972         EventClass::Addressable,
   2973         EventPrivacy::Public,
   2974         AuthorRole::Farmer,
   2975         ContentSchema::JsonObject,
   2976         EventDiscriminator::Composite(FARM_MEMBERS_LIST_DISCRIMINATOR),
   2977         LIST_SET_TAGS,
   2978         FARM_OPS_REDUCERS
   2979     ),
   2980     event_contract!(
   2981         "radroots.list_set.farm.members.owners.v1",
   2982         KIND_LIST_SET_GENERIC,
   2983         "Farm Owners List Set",
   2984         "RadrootsListSet",
   2985         EventClass::Addressable,
   2986         EventPrivacy::Public,
   2987         AuthorRole::Farmer,
   2988         ContentSchema::JsonObject,
   2989         EventDiscriminator::Composite(FARM_OWNERS_LIST_DISCRIMINATOR),
   2990         LIST_SET_TAGS,
   2991         FARM_OPS_REDUCERS
   2992     ),
   2993     event_contract!(
   2994         "radroots.list_set.farm.members.workers.v1",
   2995         KIND_LIST_SET_GENERIC,
   2996         "Farm Workers List Set",
   2997         "RadrootsListSet",
   2998         EventClass::Addressable,
   2999         EventPrivacy::Public,
   3000         AuthorRole::Farmer,
   3001         ContentSchema::JsonObject,
   3002         EventDiscriminator::Composite(FARM_WORKERS_LIST_DISCRIMINATOR),
   3003         LIST_SET_TAGS,
   3004         FARM_OPS_REDUCERS
   3005     ),
   3006     event_contract!(
   3007         "radroots.list_set.farm.plots.v1",
   3008         KIND_LIST_SET_GENERIC,
   3009         "Farm Plots List Set",
   3010         "RadrootsListSet",
   3011         EventClass::Addressable,
   3012         EventPrivacy::Public,
   3013         AuthorRole::Farmer,
   3014         ContentSchema::JsonObject,
   3015         EventDiscriminator::Composite(FARM_PLOTS_LIST_DISCRIMINATOR),
   3016         LIST_SET_TAGS,
   3017         FARM_OPS_REDUCERS
   3018     ),
   3019     event_contract!(
   3020         "radroots.list_set.farm.listings.v1",
   3021         KIND_LIST_SET_GENERIC,
   3022         "Farm Listings List Set",
   3023         "RadrootsListSet",
   3024         EventClass::Addressable,
   3025         EventPrivacy::Public,
   3026         AuthorRole::Farmer,
   3027         ContentSchema::JsonObject,
   3028         EventDiscriminator::Composite(FARM_LISTINGS_LIST_DISCRIMINATOR),
   3029         LIST_SET_TAGS,
   3030         FARM_OPS_REDUCERS
   3031     ),
   3032     event_contract!(
   3033         "radroots.list_set.member_of.farms.v1",
   3034         KIND_LIST_SET_GENERIC,
   3035         "Member Of Farms List Set",
   3036         "RadrootsListSet",
   3037         EventClass::Addressable,
   3038         EventPrivacy::Public,
   3039         AuthorRole::Member,
   3040         ContentSchema::JsonObject,
   3041         EventDiscriminator::DTagExact("member_of.farms"),
   3042         LIST_SET_TAGS,
   3043         FARM_OPS_REDUCERS
   3044     ),
   3045     event_contract!(
   3046         "radroots.list_set.relay.v1",
   3047         KIND_LIST_SET_RELAY,
   3048         "Relay Set",
   3049         "RadrootsListSet",
   3050         EventClass::Addressable,
   3051         EventPrivacy::Public,
   3052         AuthorRole::Any,
   3053         ContentSchema::JsonObject,
   3054         EventDiscriminator::KindOnly,
   3055         LIST_SET_TAGS,
   3056         RELAY_REDUCERS
   3057     ),
   3058     event_contract!(
   3059         "radroots.list_set.bookmark.v1",
   3060         KIND_LIST_SET_BOOKMARK,
   3061         "Bookmark Set",
   3062         "RadrootsListSet",
   3063         EventClass::Addressable,
   3064         EventPrivacy::Public,
   3065         AuthorRole::Any,
   3066         ContentSchema::JsonObject,
   3067         EventDiscriminator::KindOnly,
   3068         LIST_SET_TAGS,
   3069         SOCIAL_REDUCERS
   3070     ),
   3071     event_contract!(
   3072         "radroots.list_set.curation.v1",
   3073         KIND_LIST_SET_CURATION,
   3074         "Curation Set",
   3075         "RadrootsListSet",
   3076         EventClass::Addressable,
   3077         EventPrivacy::Public,
   3078         AuthorRole::Any,
   3079         ContentSchema::JsonObject,
   3080         EventDiscriminator::KindOnly,
   3081         LIST_SET_TAGS,
   3082         SOCIAL_REDUCERS
   3083     ),
   3084     event_contract!(
   3085         "radroots.list_set.video.v1",
   3086         KIND_LIST_SET_VIDEO,
   3087         "Video Set",
   3088         "RadrootsListSet",
   3089         EventClass::Addressable,
   3090         EventPrivacy::Public,
   3091         AuthorRole::Any,
   3092         ContentSchema::JsonObject,
   3093         EventDiscriminator::KindOnly,
   3094         LIST_SET_TAGS,
   3095         SOCIAL_REDUCERS
   3096     ),
   3097     event_contract!(
   3098         "radroots.list_set.picture.v1",
   3099         KIND_LIST_SET_PICTURE,
   3100         "Picture Set",
   3101         "RadrootsListSet",
   3102         EventClass::Addressable,
   3103         EventPrivacy::Public,
   3104         AuthorRole::Any,
   3105         ContentSchema::JsonObject,
   3106         EventDiscriminator::KindOnly,
   3107         LIST_SET_TAGS,
   3108         SOCIAL_REDUCERS
   3109     ),
   3110     event_contract!(
   3111         "radroots.list_set.kind_mute.v1",
   3112         KIND_LIST_SET_KIND_MUTE,
   3113         "Kind Mute Set",
   3114         "RadrootsListSet",
   3115         EventClass::Addressable,
   3116         EventPrivacy::Public,
   3117         AuthorRole::Any,
   3118         ContentSchema::JsonObject,
   3119         EventDiscriminator::KindOnly,
   3120         LIST_SET_TAGS,
   3121         SOCIAL_REDUCERS
   3122     ),
   3123     event_contract!(
   3124         "radroots.list_set.interest.v1",
   3125         KIND_LIST_SET_INTEREST,
   3126         "Interest Set",
   3127         "RadrootsListSet",
   3128         EventClass::Addressable,
   3129         EventPrivacy::Public,
   3130         AuthorRole::Any,
   3131         ContentSchema::JsonObject,
   3132         EventDiscriminator::KindOnly,
   3133         LIST_SET_TAGS,
   3134         SOCIAL_REDUCERS
   3135     ),
   3136     event_contract!(
   3137         "radroots.list_set.emoji.v1",
   3138         KIND_LIST_SET_EMOJI,
   3139         "Emoji Set",
   3140         "RadrootsListSet",
   3141         EventClass::Addressable,
   3142         EventPrivacy::Public,
   3143         AuthorRole::Any,
   3144         ContentSchema::JsonObject,
   3145         EventDiscriminator::KindOnly,
   3146         LIST_SET_TAGS,
   3147         SOCIAL_REDUCERS
   3148     ),
   3149     event_contract!(
   3150         "radroots.list_set.release_artifact.v1",
   3151         KIND_LIST_SET_RELEASE_ARTIFACT,
   3152         "Release Artifact Set",
   3153         "RadrootsListSet",
   3154         EventClass::Addressable,
   3155         EventPrivacy::Public,
   3156         AuthorRole::Any,
   3157         ContentSchema::JsonObject,
   3158         EventDiscriminator::KindOnly,
   3159         LIST_SET_TAGS,
   3160         SOCIAL_REDUCERS
   3161     ),
   3162     event_contract!(
   3163         "radroots.list_set.app_curation.v1",
   3164         KIND_LIST_SET_APP_CURATION,
   3165         "App Curation Set",
   3166         "RadrootsListSet",
   3167         EventClass::Addressable,
   3168         EventPrivacy::Public,
   3169         AuthorRole::Any,
   3170         ContentSchema::JsonObject,
   3171         EventDiscriminator::KindOnly,
   3172         LIST_SET_TAGS,
   3173         SOCIAL_REDUCERS
   3174     ),
   3175     event_contract!(
   3176         "radroots.social.article.v1",
   3177         KIND_ARTICLE,
   3178         "Long Form Article",
   3179         "RadrootsArticle",
   3180         EventClass::Addressable,
   3181         EventPrivacy::Public,
   3182         AuthorRole::Any,
   3183         ContentSchema::Markdown,
   3184         EventDiscriminator::KindOnly,
   3185         ARTICLE_TAGS,
   3186         SOCIAL_REDUCERS
   3187     ),
   3188     experimental_event_contract!(
   3189         "radroots.wiki.merge_request.v1",
   3190         KIND_WIKI_MERGE_REQUEST,
   3191         "Wiki Merge Request",
   3192         "RadrootsWikiMergeRequest",
   3193         EventClass::Regular,
   3194         EventPrivacy::Public,
   3195         AuthorRole::Any,
   3196         ContentSchema::PlainText,
   3197         EventDiscriminator::KindOnly,
   3198         WIKI_MERGE_REQUEST_TAGS,
   3199         KNOWLEDGE_REDUCERS
   3200     ),
   3201     experimental_event_contract!(
   3202         "radroots.wiki.article.v1",
   3203         KIND_WIKI_ARTICLE,
   3204         "Wiki Article",
   3205         "RadrootsWikiArticle",
   3206         EventClass::Addressable,
   3207         EventPrivacy::Public,
   3208         AuthorRole::Any,
   3209         ContentSchema::Djot,
   3210         EventDiscriminator::KindOnly,
   3211         WIKI_ARTICLE_TAGS,
   3212         KNOWLEDGE_REDUCERS
   3213     ),
   3214     experimental_event_contract!(
   3215         "radroots.wiki.redirect.v1",
   3216         KIND_WIKI_REDIRECT,
   3217         "Wiki Redirect",
   3218         "RadrootsWikiRedirect",
   3219         EventClass::Addressable,
   3220         EventPrivacy::Public,
   3221         AuthorRole::Any,
   3222         ContentSchema::Empty,
   3223         EventDiscriminator::KindOnly,
   3224         WIKI_REDIRECT_TAGS,
   3225         KNOWLEDGE_REDUCERS
   3226     ),
   3227     event_contract_with_authoring_policy!(
   3228         "radroots.calendar.date_event.v1",
   3229         KIND_CALENDAR_DATE_EVENT,
   3230         "Calendar Date Event",
   3231         "RadrootsAuthoredCalendarDateEvent / RadrootsParsedNip52CalendarDateEvent / RadrootsAdmittedCalendarDateEvent",
   3232         EventClass::Addressable,
   3233         EventPrivacy::Public,
   3234         AuthorRole::Any,
   3235         ContentSchema::PlainText,
   3236         EventAuthoringPolicy::TypedOnly,
   3237         EventDiscriminator::KindOnly,
   3238         CALENDAR_DATE_EVENT_TAGS,
   3239         CALENDAR_REDUCERS
   3240     ),
   3241     event_contract_with_authoring_policy!(
   3242         "radroots.calendar.time_event.v1",
   3243         KIND_CALENDAR_TIME_EVENT,
   3244         "Calendar Time Event",
   3245         "RadrootsAuthoredCalendarTimeEvent / RadrootsParsedNip52CalendarTimeEvent / RadrootsAdmittedCalendarTimeEvent",
   3246         EventClass::Addressable,
   3247         EventPrivacy::Public,
   3248         AuthorRole::Any,
   3249         ContentSchema::PlainText,
   3250         EventAuthoringPolicy::TypedOnly,
   3251         EventDiscriminator::KindOnly,
   3252         CALENDAR_TIME_EVENT_TAGS,
   3253         CALENDAR_REDUCERS
   3254     ),
   3255     event_contract!(
   3256         "radroots.calendar.collection.v1",
   3257         KIND_CALENDAR,
   3258         "Calendar Collection",
   3259         "RadrootsAdmittedCalendar",
   3260         EventClass::Addressable,
   3261         EventPrivacy::Public,
   3262         AuthorRole::Any,
   3263         ContentSchema::PlainText,
   3264         EventDiscriminator::KindOnly,
   3265         CALENDAR_COLLECTION_TAGS,
   3266         CALENDAR_REDUCERS
   3267     ),
   3268     event_contract!(
   3269         "radroots.calendar.rsvp.v1",
   3270         KIND_CALENDAR_EVENT_RSVP,
   3271         "Calendar RSVP",
   3272         "RadrootsAdmittedCalendarEventRsvp",
   3273         EventClass::Addressable,
   3274         EventPrivacy::Public,
   3275         AuthorRole::Any,
   3276         ContentSchema::PlainText,
   3277         EventDiscriminator::KindOnly,
   3278         CALENDAR_RSVP_TAGS,
   3279         CALENDAR_REDUCERS
   3280     ),
   3281     event_contract!(
   3282         "radroots.list_set.starter_pack.v1",
   3283         KIND_LIST_SET_STARTER_PACK,
   3284         "Starter Pack Set",
   3285         "RadrootsListSet",
   3286         EventClass::Addressable,
   3287         EventPrivacy::Public,
   3288         AuthorRole::Any,
   3289         ContentSchema::JsonObject,
   3290         EventDiscriminator::KindOnly,
   3291         LIST_SET_TAGS,
   3292         SOCIAL_REDUCERS
   3293     ),
   3294     event_contract!(
   3295         "radroots.list_set.media_starter_pack.v1",
   3296         KIND_LIST_SET_MEDIA_STARTER_PACK,
   3297         "Media Starter Pack Set",
   3298         "RadrootsListSet",
   3299         EventClass::Addressable,
   3300         EventPrivacy::Public,
   3301         AuthorRole::Any,
   3302         ContentSchema::JsonObject,
   3303         EventDiscriminator::KindOnly,
   3304         LIST_SET_TAGS,
   3305         SOCIAL_REDUCERS
   3306     ),
   3307     event_contract!(
   3308         "radroots.farm.profile.v1",
   3309         KIND_FARM,
   3310         "Farm",
   3311         "RadrootsFarm",
   3312         EventClass::Addressable,
   3313         EventPrivacy::Public,
   3314         AuthorRole::Farmer,
   3315         ContentSchema::JsonObject,
   3316         EventDiscriminator::KindOnly,
   3317         FARM_TAGS,
   3318         FARM_OPS_REDUCERS
   3319     ),
   3320     event_contract!(
   3321         "radroots.farm.plot.v1",
   3322         KIND_PLOT,
   3323         "Plot",
   3324         "RadrootsPlot",
   3325         EventClass::Addressable,
   3326         EventPrivacy::Public,
   3327         AuthorRole::Farmer,
   3328         ContentSchema::JsonObject,
   3329         EventDiscriminator::KindOnly,
   3330         FARM_TAGS,
   3331         FARM_OPS_REDUCERS
   3332     ),
   3333     event_contract!(
   3334         "radroots.farm.coop.v1",
   3335         KIND_COOP,
   3336         "Coop",
   3337         "RadrootsCoop",
   3338         EventClass::Addressable,
   3339         EventPrivacy::Public,
   3340         AuthorRole::Farmer,
   3341         ContentSchema::JsonObject,
   3342         EventDiscriminator::KindOnly,
   3343         FARM_TAGS,
   3344         FARM_OPS_REDUCERS
   3345     ),
   3346     event_contract!(
   3347         "radroots.farm.document.v1",
   3348         KIND_DOCUMENT,
   3349         "Document",
   3350         "RadrootsDocument",
   3351         EventClass::Addressable,
   3352         EventPrivacy::Public,
   3353         AuthorRole::Farmer,
   3354         ContentSchema::JsonObject,
   3355         EventDiscriminator::KindOnly,
   3356         D_TAGS,
   3357         FARM_OPS_REDUCERS
   3358     ),
   3359     event_contract!(
   3360         "radroots.farm.resource_area.v1",
   3361         KIND_RESOURCE_AREA,
   3362         "Resource Area",
   3363         "RadrootsResourceArea",
   3364         EventClass::Addressable,
   3365         EventPrivacy::Public,
   3366         AuthorRole::Farmer,
   3367         ContentSchema::JsonObject,
   3368         EventDiscriminator::KindOnly,
   3369         FARM_TAGS,
   3370         FARM_OPS_REDUCERS
   3371     ),
   3372     event_contract!(
   3373         "radroots.farm.resource_harvest_cap.v1",
   3374         KIND_RESOURCE_HARVEST_CAP,
   3375         "Resource Harvest Capacity",
   3376         "RadrootsResourceHarvestCap",
   3377         EventClass::Addressable,
   3378         EventPrivacy::Public,
   3379         AuthorRole::Farmer,
   3380         ContentSchema::JsonObject,
   3381         EventDiscriminator::KindOnly,
   3382         FARM_TAGS,
   3383         FARM_OPS_REDUCERS
   3384     ),
   3385     event_contract!(
   3386         "radroots.account.claim.v1",
   3387         KIND_ACCOUNT_CLAIM,
   3388         "Account Claim",
   3389         "RadrootsAccountClaim",
   3390         EventClass::Addressable,
   3391         EventPrivacy::Public,
   3392         AuthorRole::Any,
   3393         ContentSchema::JsonObject,
   3394         EventDiscriminator::KindOnly,
   3395         D_TAGS,
   3396         PROFILE_REDUCERS
   3397     ),
   3398     event_contract!(
   3399         "radroots.farm.workspace_manifest.v1",
   3400         KIND_FARM_WORKSPACE_MANIFEST,
   3401         "Farm Workspace Manifest",
   3402         "RadrootsFarmWorkspaceManifest",
   3403         EventClass::Addressable,
   3404         EventPrivacy::Encrypted,
   3405         AuthorRole::Farmer,
   3406         ContentSchema::JsonObject,
   3407         EventDiscriminator::KindOnly,
   3408         D_TAGS,
   3409         FARM_OPS_REDUCERS
   3410     ),
   3411     event_contract!(
   3412         "radroots.operational_listing.published.v1",
   3413         KIND_CLASSIFIED_LISTING,
   3414         "Operational Listing",
   3415         "RadrootsOperationalListing",
   3416         EventClass::Addressable,
   3417         EventPrivacy::Public,
   3418         AuthorRole::Seller,
   3419         ContentSchema::Markdown,
   3420         EventDiscriminator::ClassifiedListingPartition(
   3421             ClassifiedListingPartition::OperationalListing,
   3422         ),
   3423         OPERATIONAL_LISTING_TAGS,
   3424         OPERATIONAL_LISTING_REDUCERS
   3425     ),
   3426     event_contract_with_authoring_policy!(
   3427         "radroots.food.availability.v1",
   3428         KIND_CLASSIFIED_LISTING,
   3429         "Food Availability",
   3430         "RadrootsFoodAvailabilityDetails / RadrootsInboundFoodAvailabilityProjection",
   3431         EventClass::Addressable,
   3432         EventPrivacy::Public,
   3433         AuthorRole::Seller,
   3434         ContentSchema::Markdown,
   3435         EventAuthoringPolicy::TypedOnly,
   3436         EventDiscriminator::AdmissionOnly,
   3437         FOOD_AVAILABILITY_TAGS,
   3438         FOOD_AVAILABILITY_REDUCERS
   3439     ),
   3440     experimental_event_contract!(
   3441         "radroots.knowledge.source.v1",
   3442         KIND_KNOWLEDGE_SOURCE,
   3443         "Knowledge Source",
   3444         "RadrootsKnowledgeSource",
   3445         EventClass::Addressable,
   3446         EventPrivacy::Public,
   3447         AuthorRole::Any,
   3448         ContentSchema::JsonObject,
   3449         EventDiscriminator::TagEquals {
   3450             name: "contract",
   3451             value: "radroots.knowledge.source.v1",
   3452         },
   3453         KNOWLEDGE_SOURCE_TAGS,
   3454         KNOWLEDGE_REDUCERS
   3455     ),
   3456     experimental_event_contract!(
   3457         "radroots.knowledge.evidence_bounty.v1",
   3458         KIND_EVIDENCE_BOUNTY,
   3459         "Evidence Bounty",
   3460         "RadrootsEvidenceBounty",
   3461         EventClass::Addressable,
   3462         EventPrivacy::Public,
   3463         AuthorRole::Any,
   3464         ContentSchema::JsonObject,
   3465         EventDiscriminator::TagEquals {
   3466             name: "contract",
   3467             value: "radroots.knowledge.evidence_bounty.v1",
   3468         },
   3469         EVIDENCE_BOUNTY_TAGS,
   3470         KNOWLEDGE_REDUCERS
   3471     ),
   3472     experimental_event_contract!(
   3473         "radroots.knowledge.claim.v1",
   3474         KIND_KNOWLEDGE_CLAIM,
   3475         "Knowledge Claim",
   3476         "RadrootsKnowledgeClaim",
   3477         EventClass::Regular,
   3478         EventPrivacy::Public,
   3479         AuthorRole::Any,
   3480         ContentSchema::JsonObject,
   3481         EventDiscriminator::TagEquals {
   3482             name: "contract",
   3483             value: "radroots.knowledge.claim.v1",
   3484         },
   3485         KNOWLEDGE_CLAIM_TAGS,
   3486         KNOWLEDGE_REDUCERS
   3487     ),
   3488     experimental_event_contract!(
   3489         "radroots.knowledge.relation.v1",
   3490         KIND_KNOWLEDGE_RELATION,
   3491         "Knowledge Relation",
   3492         "RadrootsKnowledgeRelation",
   3493         EventClass::Regular,
   3494         EventPrivacy::Public,
   3495         AuthorRole::Any,
   3496         ContentSchema::JsonObject,
   3497         EventDiscriminator::TagEquals {
   3498             name: "contract",
   3499             value: "radroots.knowledge.relation.v1",
   3500         },
   3501         KNOWLEDGE_RELATION_TAGS,
   3502         KNOWLEDGE_REDUCERS
   3503     ),
   3504     experimental_event_contract!(
   3505         "radroots.knowledge.review.v1",
   3506         KIND_KNOWLEDGE_REVIEW,
   3507         "Knowledge Review",
   3508         "RadrootsKnowledgeReview",
   3509         EventClass::Regular,
   3510         EventPrivacy::Public,
   3511         AuthorRole::Any,
   3512         ContentSchema::JsonObject,
   3513         EventDiscriminator::TagEquals {
   3514             name: "contract",
   3515             value: "radroots.knowledge.review.v1",
   3516         },
   3517         KNOWLEDGE_REVIEW_TAGS,
   3518         KNOWLEDGE_REDUCERS
   3519     ),
   3520     experimental_event_contract!(
   3521         "radroots.knowledge.field_report.v1",
   3522         KIND_KNOWLEDGE_FIELD_REPORT,
   3523         "Knowledge Field Report",
   3524         "RadrootsKnowledgeFieldReport",
   3525         EventClass::Regular,
   3526         EventPrivacy::Public,
   3527         AuthorRole::Any,
   3528         ContentSchema::JsonObject,
   3529         EventDiscriminator::TagEquals {
   3530             name: "contract",
   3531             value: "radroots.knowledge.field_report.v1",
   3532         },
   3533         KNOWLEDGE_FIELD_REPORT_TAGS,
   3534         KNOWLEDGE_REDUCERS
   3535     ),
   3536     experimental_event_contract!(
   3537         "radroots.knowledge.change_proposal.v1",
   3538         KIND_KNOWLEDGE_CHANGE_PROPOSAL,
   3539         "Knowledge Change Proposal",
   3540         "RadrootsKnowledgeChangeProposal",
   3541         EventClass::Regular,
   3542         EventPrivacy::Public,
   3543         AuthorRole::Any,
   3544         ContentSchema::JsonObject,
   3545         EventDiscriminator::TagEquals {
   3546             name: "contract",
   3547             value: "radroots.knowledge.change_proposal.v1",
   3548         },
   3549         KNOWLEDGE_CHANGE_PROPOSAL_TAGS,
   3550         KNOWLEDGE_REDUCERS
   3551     ),
   3552     experimental_event_contract!(
   3553         "radroots.knowledge.contribution_attestation.v1",
   3554         KIND_CONTRIBUTION_ATTESTATION,
   3555         "Contribution Attestation",
   3556         "RadrootsContributionAttestation",
   3557         EventClass::Regular,
   3558         EventPrivacy::Public,
   3559         AuthorRole::Any,
   3560         ContentSchema::JsonObject,
   3561         EventDiscriminator::TagEquals {
   3562             name: "contract",
   3563             value: "radroots.knowledge.contribution_attestation.v1",
   3564         },
   3565         KNOWLEDGE_CONTRIBUTION_TAGS,
   3566         KNOWLEDGE_REDUCERS
   3567     ),
   3568     event_contract!(
   3569         "radroots.application.handler.v1",
   3570         KIND_APPLICATION_HANDLER,
   3571         "Application Handler",
   3572         "RadrootsApplicationHandler",
   3573         EventClass::Addressable,
   3574         EventPrivacy::Public,
   3575         AuthorRole::Application,
   3576         ContentSchema::JsonObject,
   3577         EventDiscriminator::KindOnly,
   3578         D_TAGS,
   3579         SOCIAL_REDUCERS
   3580     ),
   3581     event_contract!(
   3582         "radroots.group.metadata.v1",
   3583         KIND_GROUP_METADATA,
   3584         "Group Metadata",
   3585         "RadrootsGroupMetadata",
   3586         EventClass::Addressable,
   3587         EventPrivacy::Public,
   3588         AuthorRole::Moderator,
   3589         ContentSchema::JsonObject,
   3590         EventDiscriminator::KindOnly,
   3591         GROUP_STATE_TAGS,
   3592         GROUP_REDUCERS
   3593     ),
   3594     event_contract!(
   3595         "radroots.group.admins.v1",
   3596         KIND_GROUP_ADMINS,
   3597         "Group Admins",
   3598         "RadrootsGroupAdmins",
   3599         EventClass::Addressable,
   3600         EventPrivacy::Public,
   3601         AuthorRole::Moderator,
   3602         ContentSchema::JsonObject,
   3603         EventDiscriminator::KindOnly,
   3604         GROUP_STATE_TAGS,
   3605         GROUP_REDUCERS
   3606     ),
   3607     event_contract!(
   3608         "radroots.group.members.v1",
   3609         KIND_GROUP_MEMBERS,
   3610         "Group Members",
   3611         "RadrootsGroupMembers",
   3612         EventClass::Addressable,
   3613         EventPrivacy::Public,
   3614         AuthorRole::Moderator,
   3615         ContentSchema::JsonObject,
   3616         EventDiscriminator::KindOnly,
   3617         GROUP_STATE_TAGS,
   3618         GROUP_REDUCERS
   3619     ),
   3620     event_contract!(
   3621         "radroots.group.roles.v1",
   3622         KIND_GROUP_ROLES,
   3623         "Group Roles",
   3624         "RadrootsGroupRoles",
   3625         EventClass::Addressable,
   3626         EventPrivacy::Public,
   3627         AuthorRole::Moderator,
   3628         ContentSchema::JsonObject,
   3629         EventDiscriminator::KindOnly,
   3630         GROUP_STATE_TAGS,
   3631         GROUP_REDUCERS
   3632     ),
   3633     event_contract_with_authoring_policy!(
   3634         "radroots.trade.proposal.v1",
   3635         KIND_TRADE_PROPOSAL,
   3636         "Trade Proposal",
   3637         "RadrootsTradeMutationEnvelopeV1",
   3638         EventClass::Regular,
   3639         EventPrivacy::Public,
   3640         AuthorRole::Buyer,
   3641         ContentSchema::JsonObject,
   3642         EventAuthoringPolicy::TypedOnly,
   3643         EventDiscriminator::ContentJsonFieldEquals {
   3644             field: "contract_id",
   3645             value: "radroots.trade.proposal.v1",
   3646         },
   3647         TRADE_MUTATION_TAGS,
   3648         TRADE_MUTATION_REDUCERS
   3649     ),
   3650     event_contract_with_authoring_policy!(
   3651         "radroots.trade.decision.v1",
   3652         KIND_TRADE_DECISION,
   3653         "Trade Decision",
   3654         "RadrootsTradeMutationEnvelopeV1",
   3655         EventClass::Regular,
   3656         EventPrivacy::Public,
   3657         AuthorRole::Seller,
   3658         ContentSchema::JsonObject,
   3659         EventAuthoringPolicy::TypedOnly,
   3660         EventDiscriminator::ContentJsonFieldEquals {
   3661             field: "contract_id",
   3662             value: "radroots.trade.decision.v1",
   3663         },
   3664         TRADE_MUTATION_TAGS,
   3665         TRADE_MUTATION_REDUCERS
   3666     ),
   3667     event_contract_with_authoring_policy!(
   3668         "radroots.trade.revision_proposal.v1",
   3669         KIND_TRADE_REVISION_PROPOSAL,
   3670         "Trade Revision Proposal",
   3671         "RadrootsTradeMutationEnvelopeV1",
   3672         EventClass::Regular,
   3673         EventPrivacy::Public,
   3674         AuthorRole::Any,
   3675         ContentSchema::JsonObject,
   3676         EventAuthoringPolicy::TypedOnly,
   3677         EventDiscriminator::ContentJsonFieldEquals {
   3678             field: "contract_id",
   3679             value: "radroots.trade.revision_proposal.v1",
   3680         },
   3681         TRADE_MUTATION_TAGS,
   3682         TRADE_MUTATION_REDUCERS
   3683     ),
   3684     event_contract_with_authoring_policy!(
   3685         "radroots.trade.revision_decision.v1",
   3686         KIND_TRADE_REVISION_DECISION,
   3687         "Trade Revision Decision",
   3688         "RadrootsTradeMutationEnvelopeV1",
   3689         EventClass::Regular,
   3690         EventPrivacy::Public,
   3691         AuthorRole::Any,
   3692         ContentSchema::JsonObject,
   3693         EventAuthoringPolicy::TypedOnly,
   3694         EventDiscriminator::ContentJsonFieldEquals {
   3695             field: "contract_id",
   3696             value: "radroots.trade.revision_decision.v1",
   3697         },
   3698         TRADE_MUTATION_TAGS,
   3699         TRADE_MUTATION_REDUCERS
   3700     ),
   3701     event_contract_with_authoring_policy!(
   3702         "radroots.trade.cancellation.v1",
   3703         KIND_TRADE_CANCELLATION,
   3704         "Trade Cancellation",
   3705         "RadrootsTradeMutationEnvelopeV1",
   3706         EventClass::Regular,
   3707         EventPrivacy::Public,
   3708         AuthorRole::Buyer,
   3709         ContentSchema::JsonObject,
   3710         EventAuthoringPolicy::TypedOnly,
   3711         EventDiscriminator::ContentJsonFieldEquals {
   3712             field: "contract_id",
   3713             value: "radroots.trade.cancellation.v1",
   3714         },
   3715         TRADE_MUTATION_TAGS,
   3716         TRADE_MUTATION_REDUCERS
   3717     ),
   3718     event_contract_with_authoring_policy!(
   3719         "radroots.rhi.evidence_attestation.v1",
   3720         KIND_RHI_EVIDENCE_ATTESTATION,
   3721         "RHI Evidence Attestation",
   3722         "RadrootsRhiEvidenceAttestationV1",
   3723         EventClass::Regular,
   3724         EventPrivacy::Public,
   3725         AuthorRole::Service,
   3726         ContentSchema::JsonObject,
   3727         EventAuthoringPolicy::TypedOnly,
   3728         EventDiscriminator::KindOnly,
   3729         RHI_EVIDENCE_ATTESTATION_TAGS,
   3730         TRADE_VALIDATION_REDUCERS
   3731     ),
   3732 ];
   3733 
   3734 pub fn all_kind_contracts() -> &'static [KindContract] {
   3735     all_kind_contracts_registry_v7()
   3736 }
   3737 
   3738 /// Returns the immutable kind-contract inventory for registry v7.
   3739 pub const fn all_kind_contracts_registry_v7() -> &'static [KindContract] {
   3740     KIND_CONTRACTS_REGISTRY_V7
   3741 }
   3742 
   3743 pub fn all_event_contracts() -> &'static [EventContract] {
   3744     all_event_contracts_registry_v7()
   3745 }
   3746 
   3747 /// Returns the immutable event-contract inventory for registry v7.
   3748 pub const fn all_event_contracts_registry_v7() -> &'static [EventContract] {
   3749     EVENT_CONTRACTS_REGISTRY_V7
   3750 }
   3751 
   3752 pub fn contract_families() -> &'static [ContractFamilyMetadata] {
   3753     CONTRACT_FAMILIES
   3754 }
   3755 
   3756 pub fn event_contract_family(contract: &EventContract) -> Option<ContractFamily> {
   3757     contract_family_for_id(contract.id)
   3758 }
   3759 
   3760 pub fn kind_contract_family(contract: &KindContract) -> Option<ContractFamily> {
   3761     Some(match contract.kind {
   3762         KIND_PROFILE | KIND_FOLLOW | KIND_ACCOUNT_CLAIM => ContractFamily::Profile,
   3763         KIND_SEAL | KIND_MESSAGE | KIND_MESSAGE_FILE | KIND_GIFT_WRAP => ContractFamily::Message,
   3764         KIND_COMMENT
   3765         | KIND_DELETION_REQUEST
   3766         | KIND_GEOCHAT
   3767         | KIND_POST
   3768         | KIND_REACTION
   3769         | KIND_REPOST
   3770         | KIND_GENERIC_REPOST
   3771         | KIND_ARTICLE
   3772         | KIND_FILE_METADATA => ContractFamily::Social,
   3773         KIND_RELAY_AUTH | KIND_HTTP_AUTH => ContractFamily::Relay,
   3774         KIND_GROUP_PUT_USER
   3775         | KIND_GROUP_REMOVE_USER
   3776         | KIND_GROUP_EDIT_METADATA
   3777         | KIND_GROUP_DELETE_EVENT
   3778         | KIND_GROUP_CREATE_GROUP
   3779         | KIND_GROUP_DELETE_GROUP
   3780         | KIND_GROUP_CREATE_INVITE
   3781         | KIND_GROUP_JOIN_REQUEST
   3782         | KIND_GROUP_LEAVE_REQUEST
   3783         | KIND_GROUP_METADATA
   3784         | KIND_GROUP_ADMINS
   3785         | KIND_GROUP_MEMBERS
   3786         | KIND_GROUP_ROLES => ContractFamily::Group,
   3787         KIND_LIST_MUTE
   3788         | KIND_LIST_PINNED_NOTES
   3789         | KIND_LIST_READ_WRITE_RELAYS
   3790         | KIND_LIST_BOOKMARKS
   3791         | KIND_LIST_COMMUNITIES
   3792         | KIND_LIST_PUBLIC_CHATS
   3793         | KIND_LIST_BLOCKED_RELAYS
   3794         | KIND_LIST_SEARCH_RELAYS
   3795         | KIND_LIST_SIMPLE_GROUPS
   3796         | KIND_LIST_RELAY_FEEDS
   3797         | KIND_LIST_INTERESTS
   3798         | KIND_LIST_MEDIA_FOLLOWS
   3799         | KIND_LIST_EMOJIS
   3800         | KIND_LIST_DM_RELAYS
   3801         | KIND_LIST_GOOD_WIKI_AUTHORS
   3802         | KIND_LIST_GOOD_WIKI_RELAYS
   3803         | KIND_LIST_SET_FOLLOW
   3804         | KIND_LIST_SET_GENERIC
   3805         | KIND_LIST_SET_RELAY
   3806         | KIND_LIST_SET_BOOKMARK
   3807         | KIND_LIST_SET_CURATION
   3808         | KIND_LIST_SET_VIDEO
   3809         | KIND_LIST_SET_PICTURE
   3810         | KIND_LIST_SET_KIND_MUTE
   3811         | KIND_LIST_SET_INTEREST
   3812         | KIND_LIST_SET_EMOJI
   3813         | KIND_LIST_SET_RELEASE_ARTIFACT
   3814         | KIND_LIST_SET_APP_CURATION
   3815         | KIND_LIST_SET_STARTER_PACK
   3816         | KIND_LIST_SET_MEDIA_STARTER_PACK => ContractFamily::List,
   3817         KIND_CALENDAR_DATE_EVENT
   3818         | KIND_CALENDAR_TIME_EVENT
   3819         | KIND_CALENDAR
   3820         | KIND_CALENDAR_EVENT_RSVP => ContractFamily::Calendar,
   3821         KIND_FARM
   3822         | KIND_PLOT
   3823         | KIND_COOP
   3824         | KIND_DOCUMENT
   3825         | KIND_RESOURCE_AREA
   3826         | KIND_RESOURCE_HARVEST_CAP
   3827         | KIND_FARM_WORKSPACE_MANIFEST
   3828         | KIND_FARM_CRDT_CHANGE => ContractFamily::Farm,
   3829         KIND_CLASSIFIED_LISTING => ContractFamily::Market,
   3830         KIND_RHI_EVIDENCE_ATTESTATION
   3831         | KIND_TRADE_PROPOSAL
   3832         | KIND_TRADE_DECISION
   3833         | KIND_TRADE_REVISION_PROPOSAL
   3834         | KIND_TRADE_REVISION_DECISION
   3835         | KIND_TRADE_CANCELLATION => ContractFamily::Trade,
   3836         KIND_WIKI_MERGE_REQUEST
   3837         | KIND_WIKI_ARTICLE
   3838         | KIND_WIKI_REDIRECT
   3839         | KIND_KNOWLEDGE_SOURCE
   3840         | KIND_EVIDENCE_BOUNTY
   3841         | KIND_KNOWLEDGE_CLAIM
   3842         | KIND_KNOWLEDGE_RELATION
   3843         | KIND_KNOWLEDGE_REVIEW
   3844         | KIND_KNOWLEDGE_FIELD_REPORT
   3845         | KIND_KNOWLEDGE_CHANGE_PROPOSAL
   3846         | KIND_CONTRIBUTION_ATTESTATION => ContractFamily::Knowledge,
   3847         KIND_JOB_FEEDBACK => ContractFamily::Job,
   3848         _ if [
   3849             is_request_kind(contract.kind),
   3850             is_result_kind(contract.kind),
   3851         ]
   3852         .contains(&true) =>
   3853         {
   3854             ContractFamily::Job
   3855         }
   3856         _ => return None,
   3857     })
   3858 }
   3859 
   3860 pub fn kind_contract(kind: u32) -> Option<&'static KindContract> {
   3861     kind_contract_registry_v7(kind)
   3862 }
   3863 
   3864 /// Resolves a kind contract from the immutable registry-v7 inventory.
   3865 pub fn kind_contract_registry_v7(kind: u32) -> Option<&'static KindContract> {
   3866     KIND_CONTRACTS_REGISTRY_V7
   3867         .iter()
   3868         .find(|contract| contract.kind == kind)
   3869 }
   3870 
   3871 pub fn event_contract(id: &str) -> Option<&'static EventContract> {
   3872     event_contract_registry_v7(id)
   3873 }
   3874 
   3875 /// Resolves an event contract from the immutable registry-v7 inventory.
   3876 ///
   3877 /// Event-store reconciliation v1 depends on this historical entry point.
   3878 /// Later registries must retain it and add a new versioned lookup.
   3879 pub fn event_contract_registry_v7(id: &str) -> Option<&'static EventContract> {
   3880     EVENT_CONTRACTS_REGISTRY_V7
   3881         .iter()
   3882         .find(|contract| contract.id == id)
   3883 }
   3884 
   3885 pub fn event_contracts_for_kind(kind: u32) -> impl Iterator<Item = &'static EventContract> {
   3886     EVENT_CONTRACTS_REGISTRY_V7
   3887         .iter()
   3888         .filter(move |contract| contract.kind == kind)
   3889 }
   3890 
   3891 pub fn identify_event_contract(
   3892     kind: u32,
   3893     tags: &[Vec<String>],
   3894     content: &str,
   3895 ) -> Result<&'static EventContract, ContractMatchError> {
   3896     identify_event_contract_in_registry(
   3897         kind,
   3898         tags,
   3899         content,
   3900         KIND_CONTRACTS_REGISTRY_V7,
   3901         EVENT_CONTRACTS_REGISTRY_V7,
   3902     )
   3903 }
   3904 
   3905 fn identify_event_contract_in_registry(
   3906     kind: u32,
   3907     tags: &[Vec<String>],
   3908     content: &str,
   3909     kind_contracts: &'static [KindContract],
   3910     event_contracts: &'static [EventContract],
   3911 ) -> Result<&'static EventContract, ContractMatchError> {
   3912     crate::require_invariant(
   3913         kind_contracts.iter().any(|contract| contract.kind == kind),
   3914         &|| ContractMatchError::UnsupportedKind(kind),
   3915     )?;
   3916     identify_from_contracts(
   3917         event_contracts
   3918             .iter()
   3919             .filter(move |contract| contract.kind == kind),
   3920         kind,
   3921         tags,
   3922         content,
   3923     )
   3924 }
   3925 
   3926 pub fn validate_event_contract(
   3927     event: &EventEnvelope,
   3928 ) -> Result<&'static EventContract, ContractValidationError> {
   3929     validate_event_contract_in_registry(
   3930         event,
   3931         KIND_CONTRACTS_REGISTRY_V7,
   3932         EVENT_CONTRACTS_REGISTRY_V7,
   3933     )
   3934 }
   3935 
   3936 /// Validates against the immutable event-contract inventory used by registry 7.
   3937 ///
   3938 /// Event-store migration 0002 depends on this historical entry point. Later
   3939 /// registries must retain it and add a new versioned validator.
   3940 pub fn validate_event_contract_registry_v7(
   3941     event: &EventEnvelope,
   3942 ) -> Result<&'static EventContract, ContractValidationError> {
   3943     validate_event_contract_in_registry(
   3944         event,
   3945         KIND_CONTRACTS_REGISTRY_V7,
   3946         EVENT_CONTRACTS_REGISTRY_V7,
   3947     )
   3948 }
   3949 
   3950 fn validate_event_contract_in_registry(
   3951     event: &EventEnvelope,
   3952     kind_contracts: &'static [KindContract],
   3953     event_contracts: &'static [EventContract],
   3954 ) -> Result<&'static EventContract, ContractValidationError> {
   3955     let tags = event.tags_as_vec();
   3956     let contract = identify_event_contract_in_registry(
   3957         event.kind_u32(),
   3958         &tags,
   3959         event.content(),
   3960         kind_contracts,
   3961         event_contracts,
   3962     )
   3963     .map_err(|error| ContractValidationError::ContractMatch { error })?;
   3964     validate_event_contract_parts_in_registry(
   3965         event.kind_u32(),
   3966         &tags,
   3967         event.content(),
   3968         contract,
   3969         event_contracts,
   3970         false,
   3971     )?;
   3972     Ok(contract)
   3973 }
   3974 
   3975 pub fn validate_event_contract_shape(
   3976     event: &EventEnvelope,
   3977     contract_id: &str,
   3978 ) -> Result<(), ContractValidationError> {
   3979     let tags = event.tags_as_vec();
   3980     validate_event_contract_parts(event.kind_u32(), &tags, event.content(), contract_id)
   3981 }
   3982 
   3983 /// Validates a contract selected explicitly by an admission boundary.
   3984 ///
   3985 /// This is the required path for contracts whose discriminator is
   3986 /// [`EventDiscriminator::AdmissionOnly`]. The selected contract's kind,
   3987 /// content, tags, and custom invariants are still validated in full.
   3988 pub fn validate_event_contract_for_admission(
   3989     event: &EventEnvelope,
   3990     contract_id: &str,
   3991 ) -> Result<&'static EventContract, ContractValidationError> {
   3992     let contract =
   3993         event_contract(contract_id).ok_or_else(|| ContractValidationError::UnknownContract {
   3994             contract_id: contract_id.to_owned(),
   3995         })?;
   3996     let tags = event.tags_as_vec();
   3997     validate_event_contract_parts_in_registry(
   3998         event.kind_u32(),
   3999         &tags,
   4000         event.content(),
   4001         contract,
   4002         EVENT_CONTRACTS_REGISTRY_V7,
   4003         true,
   4004     )?;
   4005     Ok(contract)
   4006 }
   4007 
   4008 pub fn validate_event_contract_parts(
   4009     kind: u32,
   4010     tags: &[Vec<String>],
   4011     content: &str,
   4012     contract_id: &str,
   4013 ) -> Result<(), ContractValidationError> {
   4014     let contract =
   4015         event_contract(contract_id).ok_or_else(|| ContractValidationError::UnknownContract {
   4016             contract_id: contract_id.to_owned(),
   4017         })?;
   4018     validate_event_contract_parts_in_registry(
   4019         kind,
   4020         tags,
   4021         content,
   4022         contract,
   4023         EVENT_CONTRACTS_REGISTRY_V7,
   4024         false,
   4025     )
   4026 }
   4027 
   4028 fn validate_event_contract_parts_in_registry(
   4029     kind: u32,
   4030     tags: &[Vec<String>],
   4031     content: &str,
   4032     contract: &EventContract,
   4033     event_contracts: &'static [EventContract],
   4034     admission_selected: bool,
   4035 ) -> Result<(), ContractValidationError> {
   4036     crate::require_invariant(kind == contract.kind, &|| {
   4037         ContractValidationError::KindMismatch {
   4038             expected: contract.kind,
   4039             actual: kind,
   4040         }
   4041     })?;
   4042     crate::require_invariant(
   4043         admission_selected || !matches!(contract.discriminator, EventDiscriminator::AdmissionOnly),
   4044         &|| ContractValidationError::AdmissionRequired {
   4045             contract_id: contract.id,
   4046         },
   4047     )?;
   4048     validate_classified_listing_partition_parts(tags, contract)?;
   4049     validate_content_shape_parts(content, contract)?;
   4050     validate_contract_tags_parts_in_registry(tags, contract, event_contracts)?;
   4051     if admission_selected {
   4052         validate_admission_selected_contract_parts(kind, tags, contract)?;
   4053     }
   4054     validate_discriminator_parts(content, contract, admission_selected)?;
   4055     validate_custom_calendar_contract_parts(tags, contract)?;
   4056     validate_custom_knowledge_contract_parts(content, contract)?;
   4057     Ok(())
   4058 }
   4059 
   4060 fn validate_admission_selected_contract_parts(
   4061     kind: u32,
   4062     tags: &[Vec<String>],
   4063     contract: &EventContract,
   4064 ) -> Result<(), ContractValidationError> {
   4065     if contract.id == "radroots.social.deletion_request.v1"
   4066         && !tags.iter().any(|tag| {
   4067             tag.first()
   4068                 .is_some_and(|name| matches!(name.as_str(), "e" | "a"))
   4069         })
   4070     {
   4071         return Err(ContractValidationError::ContractMatch {
   4072             error: ContractMatchError::UnsupportedShape(kind),
   4073         });
   4074     }
   4075     Ok(())
   4076 }
   4077 
   4078 fn validate_classified_listing_partition_parts(
   4079     tags: &[Vec<String>],
   4080     contract: &EventContract,
   4081 ) -> Result<(), ContractValidationError> {
   4082     let EventDiscriminator::ClassifiedListingPartition(expected) = contract.discriminator else {
   4083         return Ok(());
   4084     };
   4085     if classify_classified_listing_raw_tags_registry_v7(tags) == expected {
   4086         Ok(())
   4087     } else {
   4088         Err(ContractValidationError::ContractMatch {
   4089             error: ContractMatchError::UnsupportedShape(contract.kind),
   4090         })
   4091     }
   4092 }
   4093 
   4094 fn classify_classified_listing_raw_tags_registry_v7(
   4095     tags: &[Vec<String>],
   4096 ) -> ClassifiedListingPartition {
   4097     let mut has_focused_marker = false;
   4098     let mut has_operational_marker = false;
   4099 
   4100     for name in tags
   4101         .iter()
   4102         .filter_map(|tag| tag.first().map(String::as_str))
   4103     {
   4104         match name {
   4105             "radroots:price_unit" | "radroots:quantity" => has_focused_marker = true,
   4106             "radroots:primary_bin" | "radroots:bin" | "radroots:price" => {
   4107                 has_operational_marker = true;
   4108             }
   4109             _ => {}
   4110         }
   4111 
   4112         if has_focused_marker && has_operational_marker {
   4113             return ClassifiedListingPartition::Ambiguous;
   4114         }
   4115     }
   4116 
   4117     match (has_focused_marker, has_operational_marker) {
   4118         (true, false) => ClassifiedListingPartition::FocusedFoodAvailability,
   4119         (false, true) => ClassifiedListingPartition::OperationalListing,
   4120         (false, false) => ClassifiedListingPartition::GenericNip99,
   4121         (true, true) => ClassifiedListingPartition::Ambiguous,
   4122     }
   4123 }
   4124 
   4125 fn identify_from_contracts<'a, I>(
   4126     contracts: I,
   4127     kind: u32,
   4128     tags: &[Vec<String>],
   4129     content: &str,
   4130 ) -> Result<&'a EventContract, ContractMatchError>
   4131 where
   4132     I: IntoIterator<Item = &'a EventContract>,
   4133 {
   4134     let mut matched = None;
   4135     let mut matched_count = 0;
   4136 
   4137     for contract in contracts {
   4138         if discriminator_matches(&contract.discriminator, tags, content) {
   4139             matched = Some(contract);
   4140             matched_count += 1;
   4141         }
   4142     }
   4143 
   4144     match (matched, matched_count) {
   4145         (Some(contract), 1) => Ok(contract),
   4146         (None, _) => Err(ContractMatchError::UnsupportedShape(kind)),
   4147         (Some(_), _) => Err(ContractMatchError::AmbiguousShape(kind)),
   4148     }
   4149 }
   4150 
   4151 fn contract_family_for_id(id: &str) -> Option<ContractFamily> {
   4152     const PREFIX_FAMILIES: [(&str, ContractFamily); 18] = [
   4153         ("radroots.account.", ContractFamily::Account),
   4154         ("radroots.application.", ContractFamily::Application),
   4155         ("radroots.calendar.", ContractFamily::Calendar),
   4156         ("radroots.farm.", ContractFamily::Farm),
   4157         ("radroots.group.", ContractFamily::Group),
   4158         ("radroots.http.", ContractFamily::Http),
   4159         ("radroots.job.", ContractFamily::Job),
   4160         ("radroots.knowledge.", ContractFamily::Knowledge),
   4161         ("radroots.wiki.", ContractFamily::Knowledge),
   4162         ("radroots.list.", ContractFamily::List),
   4163         ("radroots.list_set.", ContractFamily::List),
   4164         ("radroots.operational_listing.", ContractFamily::Market),
   4165         ("radroots.food.", ContractFamily::Market),
   4166         ("radroots.message.", ContractFamily::Message),
   4167         ("radroots.profile.", ContractFamily::Profile),
   4168         ("radroots.relay.", ContractFamily::Relay),
   4169         ("radroots.social.", ContractFamily::Social),
   4170         ("radroots.trade.", ContractFamily::Trade),
   4171     ];
   4172 
   4173     PREFIX_FAMILIES
   4174         .iter()
   4175         .find_map(|(prefix, family)| id.starts_with(prefix).then_some(*family))
   4176 }
   4177 
   4178 fn validate_content_shape_parts(
   4179     content: &str,
   4180     contract: &EventContract,
   4181 ) -> Result<(), ContractValidationError> {
   4182     match contract.content_schema {
   4183         ContentSchema::Empty => {
   4184             if content.is_empty() {
   4185                 Ok(())
   4186             } else {
   4187                 Err(ContractValidationError::ContentMustBeEmpty {
   4188                     contract_id: contract.id,
   4189                 })
   4190             }
   4191         }
   4192         ContentSchema::JsonObject => parse_content_object(content, contract.id).map(|_| ()),
   4193         _ => Ok(()),
   4194     }
   4195 }
   4196 
   4197 #[cfg(test)]
   4198 fn validate_contract_tags_parts(
   4199     tags: &[Vec<String>],
   4200     contract: &EventContract,
   4201 ) -> Result<(), ContractValidationError> {
   4202     validate_contract_tags_parts_in_registry(tags, contract, EVENT_CONTRACTS_REGISTRY_V7)
   4203 }
   4204 
   4205 fn validate_contract_tags_parts_in_registry(
   4206     tags: &[Vec<String>],
   4207     contract: &EventContract,
   4208     event_contracts: &'static [EventContract],
   4209 ) -> Result<(), ContractValidationError> {
   4210     for tag_contract in contract.tags {
   4211         let count = tag_count(tags, tag_contract.name);
   4212         let has_multiple_contracts_for_name = contract
   4213             .tags
   4214             .iter()
   4215             .filter(|candidate| candidate.name == tag_contract.name)
   4216             .count()
   4217             > 1;
   4218         match tag_contract.cardinality {
   4219             TagCardinality::RequiredOne => {
   4220                 crate::require_invariant(count != 0, &|| ContractValidationError::MissingTag {
   4221                     contract_id: contract.id,
   4222                     name: tag_contract.name,
   4223                 })?;
   4224                 crate::require_invariant(
   4225                     [count == 1, has_multiple_contracts_for_name].contains(&true),
   4226                     &|| ContractValidationError::TagCardinalityMismatch {
   4227                         contract_id: contract.id,
   4228                         name: tag_contract.name,
   4229                     },
   4230                 )?;
   4231             }
   4232             TagCardinality::RequiredMany => {
   4233                 crate::require_invariant(count != 0, &|| ContractValidationError::MissingTag {
   4234                     contract_id: contract.id,
   4235                     name: tag_contract.name,
   4236                 })?;
   4237             }
   4238             TagCardinality::OptionalOne => {
   4239                 crate::require_invariant(
   4240                     [count <= 1, has_multiple_contracts_for_name].contains(&true),
   4241                     &|| ContractValidationError::TagCardinalityMismatch {
   4242                         contract_id: contract.id,
   4243                         name: tag_contract.name,
   4244                     },
   4245                 )?;
   4246             }
   4247             TagCardinality::OptionalMany => {}
   4248         }
   4249         if tag_contract.name == "contract" {
   4250             let actual = tag_value(tags, "contract").map(ToOwned::to_owned);
   4251             crate::require_invariant(actual.as_deref() == Some(contract.id), &|| {
   4252                 ContractValidationError::TagValueMismatch {
   4253                     contract_id: contract.id,
   4254                     name: "contract",
   4255                     expected: contract.id.to_owned(),
   4256                     actual: actual.clone(),
   4257                 }
   4258             })?;
   4259         }
   4260         validate_contract_tag_values_in_registry(tags, contract, tag_contract, event_contracts)?;
   4261     }
   4262     Ok(())
   4263 }
   4264 
   4265 fn validate_contract_tag_values_in_registry(
   4266     tags: &[Vec<String>],
   4267     contract: &EventContract,
   4268     tag_contract: &TagContract,
   4269     event_contracts: &'static [EventContract],
   4270 ) -> Result<(), ContractValidationError> {
   4271     for tag in tags
   4272         .iter()
   4273         .filter(|tag| tag.first().map(|value| value.as_str()) == Some(tag_contract.name))
   4274     {
   4275         crate::require_invariant(
   4276             tag_value_is_valid_in_registry(tag, tag_contract.value_type, event_contracts),
   4277             &|| ContractValidationError::TagValueMismatch {
   4278                 contract_id: contract.id,
   4279                 name: tag_contract.name,
   4280                 expected: tag_value_type_expectation(tag_contract.value_type).to_owned(),
   4281                 actual: tag.get(1).cloned(),
   4282             },
   4283         )?;
   4284     }
   4285     Ok(())
   4286 }
   4287 
   4288 #[cfg(test)]
   4289 fn tag_value_is_valid(tag: &[String], value_type: TagValueType) -> bool {
   4290     tag_value_is_valid_in_registry(tag, value_type, EVENT_CONTRACTS_REGISTRY_V7)
   4291 }
   4292 
   4293 fn tag_value_is_valid_in_registry(
   4294     tag: &[String],
   4295     value_type: TagValueType,
   4296     event_contracts: &'static [EventContract],
   4297 ) -> bool {
   4298     let Some(value) = tag.get(1).map(String::as_str) else {
   4299         return false;
   4300     };
   4301     match value_type {
   4302         TagValueType::AddressableCoordinate => AddressableCoordinate::parse(value).is_ok(),
   4303         TagValueType::CalendarDate => CalendarDate::parse(value).is_ok(),
   4304         TagValueType::CalendarEventCoordinate => {
   4305             canonical_calendar_event_coordinate_is_valid(value)
   4306         }
   4307         TagValueType::CalendarFreeBusy => matches!(value, "free" | "busy"),
   4308         TagValueType::CalendarRsvpStatus => {
   4309             matches!(value, "accepted" | "declined" | "tentative")
   4310         }
   4311         TagValueType::CalendarUid => CalendarUid::parse(value).is_ok(),
   4312         TagValueType::ContractId => event_contracts.iter().any(|contract| contract.id == value),
   4313         TagValueType::DTag => DTag::parse(value).is_ok(),
   4314         TagValueType::EventId | TagValueType::Sha256 => EventId::parse(value).is_ok(),
   4315         TagValueType::EventPointer => event_pointer_tag_is_valid(tag),
   4316         TagValueType::Geohash => geohash_is_valid(value),
   4317         TagValueType::IanaTimeZoneId => IanaTimeZoneId::parse(value).is_ok(),
   4318         TagValueType::Kind => value.parse::<u32>().is_ok(),
   4319         TagValueType::MutationId => {
   4320             MutationId::parse(value).is_ok_and(|identifier| identifier.to_hex() == value)
   4321         }
   4322         TagValueType::Nip01Coordinate => Nip01Coordinate::parse(value).is_ok(),
   4323         TagValueType::PublicKey => parse_public_key(value).is_ok(),
   4324         TagValueType::RelayUrl => relay_url_is_valid(value),
   4325         TagValueType::Text => visible_text_is_valid(value),
   4326         TagValueType::UnixTimestamp => value.parse::<u64>().is_ok(),
   4327         TagValueType::Uri => CalendarUri::parse(value).is_ok(),
   4328         TagValueType::Url => url_is_valid(value),
   4329         TagValueType::UtcDayIndex => canonical_u64(value).is_some(),
   4330         TagValueType::Uuid => uuid_is_valid(value),
   4331     }
   4332 }
   4333 
   4334 fn event_pointer_tag_is_valid(tag: &[String]) -> bool {
   4335     if tag.len() < 5 {
   4336         return false;
   4337     }
   4338     let id = tag[1].as_str();
   4339     let author = tag[2].as_str();
   4340     let kind = tag[3].as_str();
   4341     let d_tag = tag[4].as_str();
   4342     [
   4343         EventId::parse(id).is_ok(),
   4344         parse_public_key(author).is_ok(),
   4345         kind.parse::<u32>().is_ok(),
   4346         [d_tag.is_empty(), DTag::parse(d_tag).is_ok()].contains(&true),
   4347         tag.iter()
   4348             .skip(5)
   4349             .all(|relay| relay_url_is_valid(relay.as_str())),
   4350     ] == [true; 5]
   4351 }
   4352 
   4353 fn visible_text_is_valid(value: &str) -> bool {
   4354     [
   4355         !value.trim().is_empty(),
   4356         !value.chars().any(char::is_control),
   4357     ] == [true; 2]
   4358 }
   4359 
   4360 fn url_is_valid(value: &str) -> bool {
   4361     [
   4362         value
   4363             .strip_prefix("https://")
   4364             .or_else(|| value.strip_prefix("http://"))
   4365             .is_some_and(|remainder| !remainder.is_empty()),
   4366         value.trim() == value,
   4367         !value.chars().any(char::is_control),
   4368     ] == [true; 3]
   4369 }
   4370 
   4371 fn geohash_is_valid(value: &str) -> bool {
   4372     [
   4373         !value.is_empty(),
   4374         value.len() <= 12,
   4375         value
   4376             .bytes()
   4377             .all(|byte| matches!(byte.to_ascii_lowercase(), b'0'..=b'9' | b'b'..=b'h' | b'j'..=b'k' | b'm'..=b'n' | b'p'..=b'z')),
   4378     ] == [true; 3]
   4379 }
   4380 
   4381 fn uuid_is_valid(value: &str) -> bool {
   4382     let bytes = value.as_bytes();
   4383     [
   4384         bytes.len() == 36,
   4385         bytes.iter().enumerate().all(|(index, byte)| match index {
   4386             8 | 13 | 18 | 23 => *byte == b'-',
   4387             _ => byte.is_ascii_hexdigit(),
   4388         }),
   4389     ] == [true; 2]
   4390 }
   4391 
   4392 fn tag_value_type_expectation(value_type: TagValueType) -> &'static str {
   4393     match value_type {
   4394         TagValueType::AddressableCoordinate => "addressable_coordinate",
   4395         TagValueType::CalendarDate => "calendar_date_yyyy_mm_dd",
   4396         TagValueType::CalendarEventCoordinate => "canonical_kind_31922_or_31923_coordinate",
   4397         TagValueType::CalendarFreeBusy => "calendar_free_or_busy",
   4398         TagValueType::CalendarRsvpStatus => "calendar_rsvp_status",
   4399         TagValueType::CalendarUid => "canonical_128_bit_base64url_calendar_uid",
   4400         TagValueType::ContractId => "contract_id",
   4401         TagValueType::DTag => "d_tag",
   4402         TagValueType::EventId => "event_id",
   4403         TagValueType::EventPointer => "event_pointer",
   4404         TagValueType::Geohash => "geohash",
   4405         TagValueType::IanaTimeZoneId => "canonical_iana_time_zone_id",
   4406         TagValueType::Kind => "kind",
   4407         TagValueType::MutationId => "lowercase_64_hex_mutation_id",
   4408         TagValueType::Nip01Coordinate => "nip01_coordinate",
   4409         TagValueType::PublicKey => "public_key",
   4410         TagValueType::RelayUrl => "relay_url",
   4411         TagValueType::Sha256 => "sha256",
   4412         TagValueType::Text => "text",
   4413         TagValueType::UnixTimestamp => "unix_timestamp",
   4414         TagValueType::Uri => "absolute_uri",
   4415         TagValueType::Url => "url",
   4416         TagValueType::UtcDayIndex => "canonical_decimal_utc_day_index",
   4417         TagValueType::Uuid => "uuid",
   4418     }
   4419 }
   4420 
   4421 fn canonical_u64(value: &str) -> Option<u64> {
   4422     let valid = [
   4423         !value.is_empty(),
   4424         [value.len() > 1, value.starts_with('0')] != [true; 2],
   4425         value.bytes().all(|byte| byte.is_ascii_digit()),
   4426     ];
   4427     (valid == [true; 3]).then_some(value.parse().ok()).flatten()
   4428 }
   4429 
   4430 fn validate_custom_calendar_contract_parts(
   4431     tags: &[Vec<String>],
   4432     contract: &EventContract,
   4433 ) -> Result<(), ContractValidationError> {
   4434     match contract.id {
   4435         "radroots.calendar.date_event.v1" => validate_calendar_date_contract(tags, contract),
   4436         "radroots.calendar.time_event.v1" => validate_calendar_time_contract(tags, contract),
   4437         "radroots.calendar.collection.v1" => validate_calendar_collection_contract(tags, contract),
   4438         "radroots.calendar.rsvp.v1" => validate_calendar_rsvp_contract(tags, contract),
   4439         _ => Ok(()),
   4440     }
   4441 }
   4442 
   4443 fn validate_calendar_collection_contract(
   4444     tags: &[Vec<String>],
   4445     contract: &EventContract,
   4446 ) -> Result<(), ContractValidationError> {
   4447     validate_exact_calendar_tags(tags, contract, &["d", "title", "description", "image"])?;
   4448     validate_canonical_calendar_text_tags(tags, contract, &["title", "description"])?;
   4449     validate_calendar_event_reference_tags(tags, contract)?;
   4450     validate_calendar_blossom_image(tags, contract)?;
   4451 
   4452     let event_references = tags
   4453         .iter()
   4454         .filter(|tag| tag.first().map(String::as_str) == Some("a"))
   4455         .collect::<Vec<_>>();
   4456     for (index, reference) in event_references.iter().enumerate() {
   4457         crate::require_invariant(
   4458             !event_references
   4459                 .iter()
   4460                 .skip(index + 1)
   4461                 .any(|candidate| candidate.get(1) == reference.get(1)),
   4462             &|| {
   4463                 calendar_tag_mismatch(
   4464                     contract,
   4465                     "a",
   4466                     "duplicate_free_calendar_event_coordinates",
   4467                     reference.get(1).cloned(),
   4468                 )
   4469             },
   4470         )?;
   4471     }
   4472     Ok(())
   4473 }
   4474 
   4475 fn validate_calendar_rsvp_contract(
   4476     tags: &[Vec<String>],
   4477     contract: &EventContract,
   4478 ) -> Result<(), ContractValidationError> {
   4479     validate_exact_calendar_tags(tags, contract, &["d", "status", "fb"])?;
   4480     validate_calendar_event_reference_tags(tags, contract)?;
   4481     validate_calendar_rsvp_pointer_tag(tags, contract, "e", true)?;
   4482     validate_calendar_rsvp_pointer_tag(tags, contract, "p", false)?;
   4483 
   4484     let event_author = tags
   4485         .iter()
   4486         .find(|tag| tag.first().map(String::as_str) == Some("a"))
   4487         .and_then(|tag| tag.get(1))
   4488         .and_then(|coordinate| crate::id::AddressableCoordinateParts::parse(coordinate).ok())
   4489         .map(|parts| parts.pubkey);
   4490     if let Some(author_hint) = tag_value(tags, "p") {
   4491         let hint = parse_public_key(author_hint).ok();
   4492         crate::require_invariant(
   4493             [
   4494                 hint.as_ref() == event_author.as_ref(),
   4495                 hint.as_ref().is_some_and(|key| key.to_hex() == author_hint),
   4496             ] == [true; 2],
   4497             &|| {
   4498                 calendar_tag_mismatch(
   4499                     contract,
   4500                     "p",
   4501                     "canonical_calendar_event_author_matching_a_coordinate",
   4502                     Some(author_hint.to_owned()),
   4503                 )
   4504             },
   4505         )?;
   4506     }
   4507     Ok(())
   4508 }
   4509 
   4510 fn validate_calendar_event_reference_tags(
   4511     tags: &[Vec<String>],
   4512     contract: &EventContract,
   4513 ) -> Result<(), ContractValidationError> {
   4514     for tag in tags
   4515         .iter()
   4516         .filter(|tag| tag.first().map(String::as_str) == Some("a"))
   4517     {
   4518         let coordinate_is_valid = tag
   4519             .get(1)
   4520             .is_some_and(|value| canonical_calendar_event_coordinate_is_valid(value));
   4521         let relay_is_valid = tag
   4522             .get(2)
   4523             .is_none_or(|relay| !relay.is_empty() && relay_url_is_valid(relay));
   4524         crate::require_invariant(
   4525             [
   4526                 (2..=3).contains(&tag.len()),
   4527                 coordinate_is_valid,
   4528                 relay_is_valid,
   4529             ] == [true; 3],
   4530             &|| {
   4531                 calendar_tag_mismatch(
   4532                     contract,
   4533                     "a",
   4534                     "canonical_kind_31922_or_31923_coordinate_with_optional_relay",
   4535                     tag.get(1).cloned(),
   4536                 )
   4537             },
   4538         )?;
   4539     }
   4540     Ok(())
   4541 }
   4542 
   4543 fn validate_calendar_rsvp_pointer_tag(
   4544     tags: &[Vec<String>],
   4545     contract: &EventContract,
   4546     name: &'static str,
   4547     event_id: bool,
   4548 ) -> Result<(), ContractValidationError> {
   4549     for tag in tags
   4550         .iter()
   4551         .filter(|tag| tag.first().map(String::as_str) == Some(name))
   4552     {
   4553         let value_is_canonical = tag.get(1).is_some_and(|value| {
   4554             if event_id {
   4555                 EventId::parse(value).is_ok_and(|parsed| parsed.to_hex() == *value)
   4556             } else {
   4557                 parse_public_key(value).is_ok_and(|parsed| parsed.to_hex() == *value)
   4558             }
   4559         });
   4560         let relay_is_valid = tag
   4561             .get(2)
   4562             .is_none_or(|relay| !relay.is_empty() && relay_url_is_valid(relay));
   4563         crate::require_invariant(
   4564             [
   4565                 (2..=3).contains(&tag.len()),
   4566                 value_is_canonical,
   4567                 relay_is_valid,
   4568             ] == [true; 3],
   4569             &|| {
   4570                 calendar_tag_mismatch(
   4571                     contract,
   4572                     name,
   4573                     if event_id {
   4574                         "canonical_event_id_with_optional_relay"
   4575                     } else {
   4576                         "canonical_public_key_with_optional_relay"
   4577                     },
   4578                     tag.get(1).cloned(),
   4579                 )
   4580             },
   4581         )?;
   4582     }
   4583     Ok(())
   4584 }
   4585 
   4586 fn validate_canonical_calendar_text_tags(
   4587     tags: &[Vec<String>],
   4588     contract: &EventContract,
   4589     names: &[&'static str],
   4590 ) -> Result<(), ContractValidationError> {
   4591     for name in names {
   4592         for tag in tags
   4593             .iter()
   4594             .filter(|tag| tag.first().map(String::as_str) == Some(*name))
   4595         {
   4596             crate::require_invariant(
   4597                 tag.get(1)
   4598                     .is_some_and(|value| canonical_calendar_tag_text_is_valid(value)),
   4599                 &|| {
   4600                     calendar_tag_mismatch(
   4601                         contract,
   4602                         name,
   4603                         "canonical_visible_calendar_text",
   4604                         tag.get(1).cloned(),
   4605                     )
   4606                 },
   4607             )?;
   4608         }
   4609     }
   4610     Ok(())
   4611 }
   4612 
   4613 fn validate_calendar_blossom_image(
   4614     tags: &[Vec<String>],
   4615     contract: &EventContract,
   4616 ) -> Result<(), ContractValidationError> {
   4617     tag_value(tags, "image")
   4618         .map(|image| {
   4619             crate::require_invariant(BlobUrl::parse(image).is_ok(), &|| {
   4620                 calendar_tag_mismatch(
   4621                     contract,
   4622                     "image",
   4623                     "structural_blossom_hash_path_url",
   4624                     Some(image.to_owned()),
   4625                 )
   4626             })
   4627         })
   4628         .transpose()
   4629         .map(|_| ())
   4630 }
   4631 
   4632 fn validate_calendar_date_contract(
   4633     tags: &[Vec<String>],
   4634     contract: &EventContract,
   4635 ) -> Result<(), ContractValidationError> {
   4636     validate_exact_calendar_tags(
   4637         tags,
   4638         contract,
   4639         &[
   4640             "d", "title", "start", "end", "location", "g", "summary", "image", "t", "r", "name",
   4641         ],
   4642     )?;
   4643     validate_calendar_participant_tags(tags, contract)?;
   4644     validate_calendar_inclusion_request_tags(tags, contract)?;
   4645     validate_canonical_calendar_common_tags(tags, contract)?;
   4646 
   4647     let forbidden_day_tag = tags
   4648         .iter()
   4649         .find(|tag| tag.first().map(String::as_str) == Some("D"));
   4650     crate::require_invariant(forbidden_day_tag.is_none(), &|| {
   4651         calendar_tag_mismatch(
   4652             contract,
   4653             "D",
   4654             "forbidden_on_calendar_date_event",
   4655             forbidden_day_tag.and_then(|tag| tag.get(1)).cloned(),
   4656         )
   4657     })?;
   4658 
   4659     let start = calendar_date_tag(tags, contract, "start")?;
   4660     optional_calendar_date_tag(tags, contract, "end")?
   4661         .map(|end| {
   4662             crate::require_invariant(end > start, &|| {
   4663                 calendar_tag_mismatch(
   4664                     contract,
   4665                     "end",
   4666                     "gregorian_date_later_than_start",
   4667                     Some(end.as_str().to_owned()),
   4668                 )
   4669             })
   4670         })
   4671         .transpose()
   4672         .map(|_| ())
   4673 }
   4674 
   4675 fn validate_calendar_time_contract(
   4676     tags: &[Vec<String>],
   4677     contract: &EventContract,
   4678 ) -> Result<(), ContractValidationError> {
   4679     validate_exact_calendar_tags(
   4680         tags,
   4681         contract,
   4682         &[
   4683             "d",
   4684             "title",
   4685             "start",
   4686             "end",
   4687             "start_tzid",
   4688             "end_tzid",
   4689             "location",
   4690             "g",
   4691             "summary",
   4692             "image",
   4693             "D",
   4694             "t",
   4695             "r",
   4696             "name",
   4697         ],
   4698     )?;
   4699     validate_calendar_participant_tags(tags, contract)?;
   4700     validate_calendar_inclusion_request_tags(tags, contract)?;
   4701     validate_canonical_calendar_common_tags(tags, contract)?;
   4702 
   4703     let start = canonical_calendar_u64_tag(tags, contract, "start")?;
   4704     let end = optional_canonical_calendar_u64_tag(tags, contract, "end")?;
   4705     crate::require_invariant(!end.is_some_and(|end| end <= start), &|| {
   4706         calendar_tag_mismatch(
   4707             contract,
   4708             "end",
   4709             "canonical_unix_seconds_later_than_start",
   4710             tag_value(tags, "end").map(ToOwned::to_owned),
   4711         )
   4712     })?;
   4713 
   4714     let expected_days = covered_utc_days(start, end).map_err(|_| {
   4715         calendar_tag_mismatch(
   4716             contract,
   4717             "D",
   4718             "complete_ascending_utc_day_coverage_with_maximum_366_days",
   4719             None,
   4720         )
   4721     })?;
   4722     let mut expected_days = expected_days.into_iter();
   4723     let mut actual_days = tags
   4724         .iter()
   4725         .filter(|tag| tag.first().map(String::as_str) == Some("D"));
   4726     loop {
   4727         match (expected_days.next(), actual_days.next()) {
   4728             (Some(expected), Some(actual))
   4729                 if actual.get(1).and_then(|value| canonical_u64(value)) == Some(expected) => {}
   4730             (None, None) => break,
   4731             (_, actual) => {
   4732                 return Err(calendar_tag_mismatch(
   4733                     contract,
   4734                     "D",
   4735                     "complete_ascending_utc_day_coverage",
   4736                     actual.and_then(|tag| tag.get(1)).cloned(),
   4737                 ));
   4738             }
   4739         }
   4740     }
   4741     Ok(())
   4742 }
   4743 
   4744 fn validate_exact_calendar_tags(
   4745     tags: &[Vec<String>],
   4746     contract: &EventContract,
   4747     names: &[&'static str],
   4748 ) -> Result<(), ContractValidationError> {
   4749     for name in names {
   4750         for tag in tags
   4751             .iter()
   4752             .filter(|tag| tag.first().map(String::as_str) == Some(*name))
   4753         {
   4754             crate::require_invariant(tag.len() == 2, &|| {
   4755                 calendar_tag_mismatch(contract, name, "exact_two_element_tag", tag.get(1).cloned())
   4756             })?;
   4757         }
   4758     }
   4759     Ok(())
   4760 }
   4761 
   4762 fn validate_calendar_participant_tags(
   4763     tags: &[Vec<String>],
   4764     contract: &EventContract,
   4765 ) -> Result<(), ContractValidationError> {
   4766     crate::require_invariant(
   4767         tag_count(tags, "p") <= RADROOTS_CALENDAR_MAX_PARTICIPANTS,
   4768         &|| calendar_tag_mismatch(contract, "p", "bounded_participant_count", None),
   4769     )?;
   4770     for tag in tags
   4771         .iter()
   4772         .filter(|tag| tag.first().map(String::as_str) == Some("p"))
   4773     {
   4774         let pubkey_is_canonical = tag.get(1).is_some_and(|value| {
   4775             parse_public_key(value).is_ok_and(|pubkey| pubkey.to_hex() == *value)
   4776         });
   4777         let relay_is_valid = tag
   4778             .get(2)
   4779             .map(|relay| relay.is_empty() || relay_url_is_valid(relay))
   4780             .unwrap_or(true);
   4781         let role_is_valid = tag
   4782             .get(3)
   4783             .map(|role| canonical_calendar_tag_text_is_valid(role))
   4784             .unwrap_or(true);
   4785         let placeholder_is_canonical = !(tag.len() == 3 && tag[2].is_empty());
   4786         crate::require_invariant(
   4787             [
   4788                 (2..=4).contains(&tag.len()),
   4789                 pubkey_is_canonical,
   4790                 relay_is_valid,
   4791                 role_is_valid,
   4792                 placeholder_is_canonical,
   4793             ] == [true; 5],
   4794             &|| {
   4795                 calendar_tag_mismatch(
   4796                     contract,
   4797                     "p",
   4798                     "participant_pubkey_with_optional_relay_and_role",
   4799                     tag.get(1).cloned(),
   4800                 )
   4801             },
   4802         )?;
   4803     }
   4804     Ok(())
   4805 }
   4806 
   4807 fn validate_calendar_inclusion_request_tags(
   4808     tags: &[Vec<String>],
   4809     contract: &EventContract,
   4810 ) -> Result<(), ContractValidationError> {
   4811     for tag in tags
   4812         .iter()
   4813         .filter(|tag| tag.first().map(String::as_str) == Some("a"))
   4814     {
   4815         let coordinate_is_calendar = tag
   4816             .get(1)
   4817             .is_some_and(|value| canonical_calendar_coordinate_is_valid(value));
   4818         let relay_is_valid = tag
   4819             .get(2)
   4820             .is_none_or(|relay| !relay.is_empty() && relay_url_is_valid(relay));
   4821         crate::require_invariant(
   4822             [
   4823                 (2..=3).contains(&tag.len()),
   4824                 coordinate_is_calendar,
   4825                 relay_is_valid,
   4826             ] == [true; 3],
   4827             &|| {
   4828                 calendar_tag_mismatch(
   4829                     contract,
   4830                     "a",
   4831                     "kind_31924_coordinate_with_optional_relay",
   4832                     tag.get(1).cloned(),
   4833                 )
   4834             },
   4835         )?;
   4836     }
   4837     Ok(())
   4838 }
   4839 
   4840 fn canonical_calendar_coordinate_is_valid(value: &str) -> bool {
   4841     let Some((kind, remainder)) = value.split_once(':') else {
   4842         return false;
   4843     };
   4844     let Some((pubkey, d_tag)) = remainder.split_once(':') else {
   4845         return false;
   4846     };
   4847     let Ok(parts) = crate::id::AddressableCoordinateParts::parse(value) else {
   4848         return false;
   4849     };
   4850     [
   4851         kind == "31924",
   4852         pubkey == parts.pubkey.to_hex(),
   4853         d_tag == parts.d_tag.as_str(),
   4854     ] == [true; 3]
   4855 }
   4856 
   4857 fn canonical_calendar_event_coordinate_is_valid(value: &str) -> bool {
   4858     let Some((kind, remainder)) = value.split_once(':') else {
   4859         return false;
   4860     };
   4861     let Some((pubkey, d_tag)) = remainder.split_once(':') else {
   4862         return false;
   4863     };
   4864     let Ok(parts) = crate::id::AddressableCoordinateParts::parse(value) else {
   4865         return false;
   4866     };
   4867     [
   4868         matches!(
   4869             parts.kind,
   4870             KIND_CALENDAR_DATE_EVENT | KIND_CALENDAR_TIME_EVENT
   4871         ),
   4872         matches!(kind, "31922" | "31923"),
   4873         pubkey == parts.pubkey.to_hex(),
   4874         d_tag == parts.d_tag.as_str(),
   4875     ] == [true; 4]
   4876 }
   4877 
   4878 fn validate_canonical_calendar_common_tags(
   4879     tags: &[Vec<String>],
   4880     contract: &EventContract,
   4881 ) -> Result<(), ContractValidationError> {
   4882     for name in ["title", "location", "summary", "t", "name"] {
   4883         for tag in tags
   4884             .iter()
   4885             .filter(|tag| tag.first().map(String::as_str) == Some(name))
   4886         {
   4887             crate::require_invariant(
   4888                 tag.get(1)
   4889                     .is_some_and(|value| canonical_calendar_tag_text_is_valid(value)),
   4890                 &|| {
   4891                     calendar_tag_mismatch(
   4892                         contract,
   4893                         name,
   4894                         "canonical_visible_calendar_text",
   4895                         tag.get(1).cloned(),
   4896                     )
   4897                 },
   4898             )?;
   4899         }
   4900     }
   4901     tag_value(tags, "g")
   4902         .map(|geohash| {
   4903             crate::require_invariant(canonical_calendar_geohash_is_valid(geohash), &|| {
   4904                 calendar_tag_mismatch(
   4905                     contract,
   4906                     "g",
   4907                     "canonical_lowercase_geohash",
   4908                     Some(geohash.to_owned()),
   4909                 )
   4910             })
   4911         })
   4912         .transpose()?;
   4913     validate_calendar_blossom_image(tags, contract)?;
   4914     Ok(())
   4915 }
   4916 
   4917 fn calendar_date_tag(
   4918     tags: &[Vec<String>],
   4919     contract: &EventContract,
   4920     name: &'static str,
   4921 ) -> Result<CalendarDate, ContractValidationError> {
   4922     let value = tag_value(tags, name).ok_or(ContractValidationError::MissingTag {
   4923         contract_id: contract.id,
   4924         name,
   4925     })?;
   4926     CalendarDate::parse(value).map_err(|_| {
   4927         calendar_tag_mismatch(
   4928             contract,
   4929             name,
   4930             "calendar_date_yyyy_mm_dd",
   4931             Some(value.to_owned()),
   4932         )
   4933     })
   4934 }
   4935 
   4936 fn optional_calendar_date_tag(
   4937     tags: &[Vec<String>],
   4938     contract: &EventContract,
   4939     name: &'static str,
   4940 ) -> Result<Option<CalendarDate>, ContractValidationError> {
   4941     tag_value(tags, name)
   4942         .map(|_| calendar_date_tag(tags, contract, name))
   4943         .transpose()
   4944 }
   4945 
   4946 fn canonical_calendar_u64_tag(
   4947     tags: &[Vec<String>],
   4948     contract: &EventContract,
   4949     name: &'static str,
   4950 ) -> Result<u64, ContractValidationError> {
   4951     let value = tag_value(tags, name).ok_or(ContractValidationError::MissingTag {
   4952         contract_id: contract.id,
   4953         name,
   4954     })?;
   4955     canonical_u64(value).ok_or_else(|| {
   4956         calendar_tag_mismatch(
   4957             contract,
   4958             name,
   4959             "canonical_decimal_u64",
   4960             Some(value.to_owned()),
   4961         )
   4962     })
   4963 }
   4964 
   4965 fn optional_canonical_calendar_u64_tag(
   4966     tags: &[Vec<String>],
   4967     contract: &EventContract,
   4968     name: &'static str,
   4969 ) -> Result<Option<u64>, ContractValidationError> {
   4970     tag_value(tags, name)
   4971         .map(|_| canonical_calendar_u64_tag(tags, contract, name))
   4972         .transpose()
   4973 }
   4974 
   4975 fn calendar_tag_mismatch(
   4976     contract: &EventContract,
   4977     name: &'static str,
   4978     expected: &'static str,
   4979     actual: Option<String>,
   4980 ) -> ContractValidationError {
   4981     ContractValidationError::TagValueMismatch {
   4982         contract_id: contract.id,
   4983         name,
   4984         expected: expected.to_owned(),
   4985         actual,
   4986     }
   4987 }
   4988 
   4989 fn validate_custom_knowledge_contract_parts(
   4990     content: &str,
   4991     contract: &EventContract,
   4992 ) -> Result<(), ContractValidationError> {
   4993     let Some(expected_schema) = custom_knowledge_schema(contract.id) else {
   4994         return Ok(());
   4995     };
   4996     let object = parse_content_object(content, contract.id)?;
   4997     reject_forbidden_knowledge_fields(&object, contract.id)?;
   4998 
   4999     match object.get("schema").and_then(|value| value.as_str()) {
   5000         Some(actual) if actual == expected_schema => {}
   5001         Some(_) => {
   5002             return Err(ContractValidationError::ContentFieldMismatch {
   5003                 contract_id: contract.id,
   5004                 field: "schema",
   5005                 expected: expected_schema.to_owned(),
   5006             });
   5007         }
   5008         None => {
   5009             return Err(ContractValidationError::MissingContentField {
   5010                 contract_id: contract.id,
   5011                 field: "schema",
   5012             });
   5013         }
   5014     }
   5015 
   5016     match object
   5017         .get("schema_version")
   5018         .and_then(|value| value.as_u64())
   5019     {
   5020         Some(1) => Ok(()),
   5021         Some(_) => Err(ContractValidationError::ContentFieldMismatch {
   5022             contract_id: contract.id,
   5023             field: "schema_version",
   5024             expected: "1".to_owned(),
   5025         }),
   5026         None => Err(ContractValidationError::MissingContentField {
   5027             contract_id: contract.id,
   5028             field: "schema_version",
   5029         }),
   5030     }
   5031 }
   5032 
   5033 fn validate_discriminator_parts(
   5034     content: &str,
   5035     contract: &EventContract,
   5036     admission_selected: bool,
   5037 ) -> Result<(), ContractValidationError> {
   5038     crate::require_invariant(
   5039         admission_selected || !matches!(contract.discriminator, EventDiscriminator::AdmissionOnly),
   5040         &|| ContractValidationError::AdmissionRequired {
   5041             contract_id: contract.id,
   5042         },
   5043     )?;
   5044     let (field, value) = match &contract.discriminator {
   5045         EventDiscriminator::ContentJsonFieldEquals { field, value } => (*field, *value),
   5046         EventDiscriminator::EnvelopeType(value) => ("type", *value),
   5047         _ => return Ok(()),
   5048     };
   5049     let object = parse_content_object(content, contract.id)?;
   5050     match object.get(field).and_then(|actual| actual.as_str()) {
   5051         Some(actual) if actual == value => Ok(()),
   5052         Some(_) => Err(ContractValidationError::ContentFieldMismatch {
   5053             contract_id: contract.id,
   5054             field,
   5055             expected: value.to_owned(),
   5056         }),
   5057         None => Err(ContractValidationError::MissingContentField {
   5058             contract_id: contract.id,
   5059             field,
   5060         }),
   5061     }
   5062 }
   5063 
   5064 fn parse_content_object(
   5065     content: &str,
   5066     contract_id: &'static str,
   5067 ) -> Result<serde_json::Map<String, serde_json::Value>, ContractValidationError> {
   5068     match serde_json::from_str::<serde_json::Value>(content) {
   5069         Ok(serde_json::Value::Object(object)) => Ok(object),
   5070         _ => Err(ContractValidationError::InvalidJsonContent { contract_id }),
   5071     }
   5072 }
   5073 
   5074 fn reject_forbidden_knowledge_fields(
   5075     object: &serde_json::Map<String, serde_json::Value>,
   5076     contract_id: &'static str,
   5077 ) -> Result<(), ContractValidationError> {
   5078     for field in [
   5079         "review_status",
   5080         "canon_status",
   5081         "approved_for_canon",
   5082         "rights_status",
   5083         "trust_status",
   5084         "trusted",
   5085     ] {
   5086         crate::require_invariant(!object.contains_key(field), &|| {
   5087             ContractValidationError::ForbiddenContentField { contract_id, field }
   5088         })?;
   5089     }
   5090     Ok(())
   5091 }
   5092 
   5093 fn custom_knowledge_schema(contract_id: &str) -> Option<&'static str> {
   5094     match contract_id {
   5095         "radroots.knowledge.source.v1" => Some("radroots.knowledge.source.v1"),
   5096         "radroots.knowledge.evidence_bounty.v1" => Some("radroots.knowledge.evidence_bounty.v1"),
   5097         "radroots.knowledge.claim.v1" => Some("radroots.knowledge.claim.v1"),
   5098         "radroots.knowledge.relation.v1" => Some("radroots.knowledge.relation.v1"),
   5099         "radroots.knowledge.review.v1" => Some("radroots.knowledge.review.v1"),
   5100         "radroots.knowledge.field_report.v1" => Some("radroots.knowledge.field_report.v1"),
   5101         "radroots.knowledge.change_proposal.v1" => Some("radroots.knowledge.change_proposal.v1"),
   5102         "radroots.knowledge.contribution_attestation.v1" => {
   5103             Some("radroots.knowledge.contribution_attestation.v1")
   5104         }
   5105         _ => None,
   5106     }
   5107 }
   5108 
   5109 fn discriminator_matches(
   5110     discriminator: &EventDiscriminator,
   5111     tags: &[Vec<String>],
   5112     content: &str,
   5113 ) -> bool {
   5114     match discriminator {
   5115         EventDiscriminator::KindOnly => true,
   5116         EventDiscriminator::AdmissionOnly => false,
   5117         EventDiscriminator::ClassifiedListingPartition(expected) => {
   5118             classify_classified_listing_raw_tags_registry_v7(tags) == *expected
   5119         }
   5120         EventDiscriminator::DTagExact(expected) => tag_value(tags, "d") == Some(*expected),
   5121         EventDiscriminator::DTagPrefix(prefix) => tag_value(tags, "d")
   5122             .map(|value| value.starts_with(prefix))
   5123             .unwrap_or(false),
   5124         EventDiscriminator::DTagSuffix(suffix) => tag_value(tags, "d")
   5125             .map(|value| value.ends_with(suffix))
   5126             .unwrap_or(false),
   5127         EventDiscriminator::TagEquals { name, value } => tag_value(tags, name) == Some(*value),
   5128         EventDiscriminator::ContentJsonFieldEquals { field, value } => {
   5129             content_json_string_field_equals(content, field, value)
   5130         }
   5131         EventDiscriminator::EnvelopeType(expected) => {
   5132             content_json_string_field_equals(content, "type", expected)
   5133         }
   5134         EventDiscriminator::Composite(parts) => parts
   5135             .iter()
   5136             .all(|part| discriminator_matches(part, tags, content)),
   5137     }
   5138 }
   5139 
   5140 fn tag_value<'a>(tags: &'a [Vec<String>], name: &str) -> Option<&'a str> {
   5141     tags.iter().find_map(|tag| {
   5142         if tag.first().map(|value| value.as_str()) == Some(name) {
   5143             tag.get(1).map(|value| value.as_str())
   5144         } else {
   5145             None
   5146         }
   5147     })
   5148 }
   5149 
   5150 fn tag_count(tags: &[Vec<String>], name: &str) -> usize {
   5151     tags.iter()
   5152         .filter(|tag| tag.first().map(|value| value.as_str()) == Some(name))
   5153         .count()
   5154 }
   5155 
   5156 fn content_json_string_field_equals(content: &str, field: &str, value: &str) -> bool {
   5157     serde_json::from_str::<serde_json::Value>(content)
   5158         .ok()
   5159         .and_then(|json| {
   5160             json.get(field)
   5161                 .and_then(|field| field.as_str())
   5162                 .map(|field| field == value)
   5163         })
   5164         .unwrap_or(false)
   5165 }
   5166 
   5167 #[cfg(test)]
   5168 #[cfg_attr(coverage_nightly, coverage(off))]
   5169 mod tests;