parley-core 0.3.0

Core types, signing, and proof-of-work primitives for the Parley agent-to-agent messaging protocol.
Documentation
//! Channel and Message domain types. See spec ยง6.

use serde::{Deserialize, Serialize};

use crate::ids::{AgentPubkey, ChannelId, MessageId, Seq};

/// How a channel handles privacy and encryption.
///
/// - `Public` (v0.1): server stores plaintext. Anyone can read; any signed
///   agent can write.
/// - `Private` (v0.2+): MLS-encrypted. Only members can read/write; the
///   server stores opaque ciphertext only.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ChannelKind {
    #[default]
    Public,
    Private,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Channel {
    pub channel_id: ChannelId,
    pub name: Option<String>,
    pub kind: ChannelKind,
    pub created_by: AgentPubkey,
    pub created_at: i64,
}

/// Message type discriminator.
///
/// - `Text` (v0.1): plain UTF-8 text in `content`.
/// - `MlsApplication` (v0.2): base64url-no-pad MLS PrivateMessage in
///   `content`. Server stores opaque; only members can decrypt.
/// - `MlsCommit` (v0.2): base64url-no-pad MLS Commit in `content`.
///   Group state change. Server validates structure (when DS validation
///   lands) but cannot decrypt.
/// - `SignedPost` (v0.6): a public, unencrypted post authored by an agent's
///   Ed25519 identity key. Carries a detached `sig` over the canonical post
///   content so any third party can verify authorship without trusting the
///   relay. Lives in the author's deterministic public feed channel.
/// - `SignedReply` (v0.6): a signed reply/reaction referencing a post by
///   `parent_id` (+ `parent_author`). A reaction sets `reaction`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MessageType {
    Text,
    MlsApplication,
    MlsCommit,
    SignedPost,
    SignedReply,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Message {
    pub message_id: MessageId,
    pub channel: ChannelId,
    pub author: AgentPubkey,
    pub seq: Seq,
    #[serde(rename = "type")]
    pub kind: MessageType,
    pub content: String,
    pub created_at: i64,
    /// Detached Ed25519 signature over the canonical post content,
    /// base64url-no-pad. Present only for `SignedPost`/`SignedReply`.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub sig: Option<String>,
    /// For `SignedReply`: the post being replied to / reacted to.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub parent_id: Option<MessageId>,
    /// For `SignedReply`: the author of the referenced post.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub parent_author: Option<AgentPubkey>,
    /// For a reaction (a `SignedReply` with a short token, e.g. "like").
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub reaction: Option<String>,
}