dig-rpc-types 0.2.1

Canonical DIG-node JSON-RPC interface: request/response types, the method enum + tier classification, the error-code taxonomy, and an OpenRPC 1.2.6 document generator. The single source of truth both DIG node implementations depend on. Pure types — no I/O, no async, no server logic.
Documentation
//! RPC access tiers and the peer-surface allowlist.
//!
//! Every DIG-node RPC method belongs to exactly one [`Tier`], its PRIMARY tier.
//! Three tiers, matching the dual-transport model:
//!
//! - [`Tier::PublicRead`] — anonymous, self-verified content + discovery reads
//!   over plain HTTPS (browser) or mTLS. A browser resolves these without a
//!   client cert.
//! - [`Tier::Peer`] — the mTLS peer surface between DIG nodes (discovery,
//!   availability, range serving). `peer_id = SHA-256(TLS SPKI DER)`.
//! - [`Tier::Control`] — loopback / in-process only (cache + node lifecycle +
//!   staging). NEVER reachable over the peer surface.
//!
//! # The peer allowlist is separate from the tier
//!
//! Being `PublicRead` does NOT make a method peer-reachable — the peer surface
//! is an **allowlist**, not a denylist. Three chain-anchored public reads
//! (`getAnchoredRoot`, `getCollection`, `listCollectionItems`) are PRIMARY
//! `PublicRead` yet ALSO answered on the peer surface. The allowlist mirrors the
//! canonical node's `is_peer_reachable_method` exactly; see
//! [`Method::is_peer_reachable`](crate::method::Method::is_peer_reachable).

use serde::{Deserialize, Serialize};

/// A method's primary access tier.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub enum Tier {
    /// Anonymous, self-verified content + discovery reads (plain HTTPS / mTLS).
    PublicRead,
    /// The mTLS peer surface between DIG nodes.
    Peer,
    /// Loopback / in-process only (cache, lifecycle, staging).
    Control,
}

impl Tier {
    /// A stable, lower-case identifier for the tier (for OpenRPC tags / logs).
    pub const fn as_str(self) -> &'static str {
        match self {
            Tier::PublicRead => "public-read",
            Tier::Peer => "peer",
            Tier::Control => "control",
        }
    }
}

impl std::fmt::Display for Tier {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(self.as_str())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    /// **Proves:** each tier renders its stable lower-case identifier and
    /// serializes camelCase, matching the OpenRPC `x-dig-tier` extension.
    #[test]
    fn tier_strings_and_serde() {
        assert_eq!(Tier::PublicRead.as_str(), "public-read");
        assert_eq!(Tier::Peer.to_string(), "peer");
        assert_eq!(Tier::Control.as_str(), "control");
        assert_eq!(
            serde_json::to_string(&Tier::PublicRead).unwrap(),
            "\"publicRead\""
        );
        assert_eq!(
            serde_json::from_str::<Tier>("\"peer\"").unwrap(),
            Tier::Peer
        );
    }
}