lib

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

commit 4367ce9d28abf8aeeb46b9bb0e2da0db41a90238
parent 037dbf8d6d06410e7a17f1fa6ea30f4e2f486d83
Author: triesap <tyson@radroots.org>
Date:   Thu, 11 Jun 2026 16:52:21 -0700

events: add farm workspace manifest model

- add the Field workspace manifest and workspace reference structs
- model relay and media server endpoints for workspace bootstrap data
- cover manifest kind aliasing and serde content shape with unit tests

Diffstat:
Acrates/events/src/farm_workspace.rs | 141+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/events/src/lib.rs | 1+
2 files changed, 142 insertions(+), 0 deletions(-)

diff --git a/crates/events/src/farm_workspace.rs b/crates/events/src/farm_workspace.rs @@ -0,0 +1,141 @@ +#![forbid(unsafe_code)] + +use crate::farm::RadrootsFarmRef; +use crate::kinds::KIND_FARM_WORKSPACE_MANIFEST as KIND_FARM_WORKSPACE_MANIFEST_EVENT; + +#[cfg(not(feature = "std"))] +use alloc::{string::String, vec::Vec}; + +pub const KIND_FARM_WORKSPACE_MANIFEST: u32 = KIND_FARM_WORKSPACE_MANIFEST_EVENT; +pub const RADROOTS_FARM_WORKSPACE_SCHEMA: &str = "radroots.farm.workspace.v1"; +pub const RADROOTS_FARM_WORKSPACE_PROTOCOL_VERSION: &str = "field-nostr-v1"; +pub const RADROOTS_FARM_WORKSPACE_TAG: &str = "radroots:farm:workspace"; + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug)] +pub struct RadrootsFarmWorkspaceManifest { + pub d_tag: String, + pub schema: String, + pub farm_group_id: String, + pub name: String, + pub owner_pubkey: String, + pub farm: Option<RadrootsFarmRef>, + pub relays: Vec<RadrootsFarmWorkspaceRelay>, + pub media_servers: Vec<RadrootsFarmWorkspaceMediaServer>, + pub supported_kinds: Vec<u32>, + pub protocol_version: String, + pub created_at_ms: u64, + pub updated_at_ms: Option<u64>, +} + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct RadrootsFarmWorkspaceRef { + pub pubkey: String, + pub d_tag: String, +} + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct RadrootsFarmWorkspaceRelay { + pub url: String, + pub mode: RadrootsFarmWorkspaceRelayMode, +} + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum RadrootsFarmWorkspaceRelayMode { + Read, + Write, + ReadWrite, +} + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct RadrootsFarmWorkspaceMediaServer { + pub url: String, + pub service: String, +} + +#[cfg(all(test, feature = "serde"))] +mod tests { + use super::*; + use crate::kinds::{ + KIND_APP_DATA, KIND_FARM_CRDT_CHANGE, KIND_FARM_FILE_METADATA, + KIND_FARM_WORKSPACE_MANIFEST as KIND_FARM_WORKSPACE_MANIFEST_KIND, KIND_HTTP_AUTH, + KIND_RELAY_AUTH, + }; + + #[test] + fn manifest_kind_aliases_existing_app_data_kind() { + assert_eq!(KIND_FARM_WORKSPACE_MANIFEST, KIND_APP_DATA); + assert_eq!(KIND_FARM_WORKSPACE_MANIFEST_KIND, KIND_APP_DATA); + } + + #[test] + fn manifest_represents_required_workspace_contract_fields() { + let manifest = sample_manifest(); + + assert_eq!(manifest.d_tag, "ABCDEFGHIJKLMNOPQRSTUV"); + assert_eq!(manifest.schema, RADROOTS_FARM_WORKSPACE_SCHEMA); + assert_eq!(manifest.farm_group_id, "BCDEFGHIJKLMNOPQRSTUVW"); + assert_eq!(manifest.relays.len(), 1); + assert_eq!(manifest.media_servers.len(), 1); + assert!(manifest.supported_kinds.contains(&KIND_FARM_CRDT_CHANGE)); + assert!( + manifest + .supported_kinds + .contains(&KIND_FARM_WORKSPACE_MANIFEST) + ); + assert_eq!(manifest.created_at_ms, 1_780_000_000_000); + assert_eq!(manifest.updated_at_ms, None); + } + + #[test] + fn manifest_serializes_stable_content_shape() { + let value = serde_json::to_value(sample_manifest()).unwrap(); + + assert_eq!(value["schema"], RADROOTS_FARM_WORKSPACE_SCHEMA); + assert_eq!( + value["protocol_version"], + RADROOTS_FARM_WORKSPACE_PROTOCOL_VERSION + ); + assert_eq!(value["farm"]["d_tag"], "CDEFGHIJKLMNOPQRSTUVWX"); + assert_eq!(value["relays"][0]["mode"], "ReadWrite"); + assert_eq!(value["media_servers"][0]["service"], "RadrootsPrivateMedia"); + assert_eq!(value["supported_kinds"][0], KIND_FARM_CRDT_CHANGE); + assert_eq!(value["supported_kinds"][1], KIND_FARM_WORKSPACE_MANIFEST); + } + + fn sample_manifest() -> RadrootsFarmWorkspaceManifest { + RadrootsFarmWorkspaceManifest { + d_tag: "ABCDEFGHIJKLMNOPQRSTUV".to_string(), + schema: RADROOTS_FARM_WORKSPACE_SCHEMA.to_string(), + farm_group_id: "BCDEFGHIJKLMNOPQRSTUVW".to_string(), + name: "Small Regen Farm".to_string(), + owner_pubkey: "owner_pubkey".to_string(), + farm: Some(RadrootsFarmRef { + pubkey: "farm_pubkey".to_string(), + d_tag: "CDEFGHIJKLMNOPQRSTUVWX".to_string(), + }), + relays: vec![RadrootsFarmWorkspaceRelay { + url: "wss://relay.example.invalid/farm/ABCDEFGHIJKLMNOPQRSTUV".to_string(), + mode: RadrootsFarmWorkspaceRelayMode::ReadWrite, + }], + media_servers: vec![RadrootsFarmWorkspaceMediaServer { + url: "https://media.example.invalid/farm/ABCDEFGHIJKLMNOPQRSTUV".to_string(), + service: "RadrootsPrivateMedia".to_string(), + }], + supported_kinds: vec![ + KIND_FARM_CRDT_CHANGE, + KIND_FARM_WORKSPACE_MANIFEST, + KIND_FARM_FILE_METADATA, + KIND_RELAY_AUTH, + KIND_HTTP_AUTH, + ], + protocol_version: RADROOTS_FARM_WORKSPACE_PROTOCOL_VERSION.to_string(), + created_at_ms: 1_780_000_000_000, + updated_at_ms: None, + } + } +} diff --git a/crates/events/src/lib.rs b/crates/events/src/lib.rs @@ -12,6 +12,7 @@ pub mod comment; pub mod coop; pub mod document; pub mod farm; +pub mod farm_workspace; pub mod follow; pub mod geochat; pub mod gift_wrap;