router-protocol 0.1.1

Canonical protocol types for SteadyMux.
Documentation
// SPDX-License-Identifier: AGPL-3.0-or-later

//! Content-free contracts for device-local catalog discovery and qualification.

use crate::{CatalogSourceKind, PhaseId, ProviderService, TransportKind};
use serde::{Deserialize, Serialize};

pub const CATALOG_PROOF_SCHEMA_VERSION: &str = "1.0.0";
pub const LIVE_CATALOG_DISCOVERY_CONFIRMATION: &str = "RUN_STEADYMUX_LIVE_CATALOG_DISCOVERY";
pub const LIVE_MODEL_QUALIFICATION_CONFIRMATION: &str = "RUN_STEADYMUX_LIVE_MODEL_QUALIFICATION";
pub const MAX_CATALOG_PROOF_PHASES: usize = 64;
pub const MAX_CATALOG_PROOF_MODELS: usize = 512;
pub const MAX_CATALOG_QUALIFICATION_TARGETS: usize = 64;

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogDiscoveryPreviewRequest {
    /// Stable device-local authority identity selected for the later
    /// authenticated apply. This is not an authority digest and does not
    /// imply that authority has been provisioned.
    pub configuration_authority_id: String,
    /// Digest of the immutable pre-authority candidate configuration.
    pub pre_authority_configuration_sha256: String,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogDiscoveryApplyRequest {
    pub preview: CatalogDiscoveryPreview,
    pub expected_preview_sha256: String,
    pub confirmation: String,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogQualificationPreviewRequest {
    pub configuration_authority_id: String,
    pub pre_authority_configuration_sha256: String,
    pub discovery_receipt_sha256: String,
    pub targets: Vec<CatalogQualificationTarget>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogQualificationApplyRequest {
    pub preview: CatalogQualificationPreview,
    pub expected_preview_sha256: String,
    pub confirmation: String,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogDiscoveryPlanEntry {
    pub phase_id: PhaseId,
    pub provider: ProviderService,
    pub transport: TransportKind,
    pub source: CatalogSourceKind,
    pub configured_model_ids: Vec<String>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogDiscoveryPreview {
    pub schema_version: String,
    pub configuration_authority_id: String,
    pub pre_authority_configuration_sha256: String,
    pub phases: Vec<CatalogDiscoveryPlanEntry>,
    pub maximum_provider_requests: u32,
    pub network_effect: bool,
    pub inference_effect: bool,
    pub credential_read_effect: bool,
    pub preview_sha256: String,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CatalogDiscoveryOutcome {
    Accepted,
    RetainedLastKnownGood,
    RejectedNoUsableLastKnownGood,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogCapabilityClassification {
    pub context_tokens: u64,
    pub modalities: Vec<String>,
    pub tool_support: bool,
    pub reasoning_modes: Vec<String>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogDiscoveredModelReceipt {
    pub provider_model_id: String,
    pub model_sha256: String,
    pub capabilities: CatalogCapabilityClassification,
    pub qualification_status: CatalogQualificationStatus,
    pub exclusion_reason: Option<String>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogPhaseDiscoveryReceipt {
    pub phase_id: PhaseId,
    pub provider: ProviderService,
    pub source: CatalogSourceKind,
    pub observed_at_epoch_seconds: u64,
    pub outcome: CatalogDiscoveryOutcome,
    pub catalog_version: Option<String>,
    pub catalog_sha256: Option<String>,
    pub models: Vec<CatalogDiscoveredModelReceipt>,
    pub exclusion_reason: Option<String>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogDiscoveryReceipt {
    pub schema_version: String,
    pub configuration_authority_id: String,
    pub pre_authority_configuration_sha256: String,
    pub completed_at_epoch_seconds: u64,
    pub phases: Vec<CatalogPhaseDiscoveryReceipt>,
    pub receipt_sha256: String,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CatalogProofRole {
    Routine,
    Standard,
    Complex,
    Vision,
    LongContext,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogQualificationTarget {
    pub phase_id: PhaseId,
    pub provider: ProviderService,
    /// Exact configured profile selected for the one-shot canary. Model IDs
    /// are not route identities because one model may exist under multiple
    /// accounts or transports.
    pub profile_id: String,
    pub provider_model_id: String,
    pub roles: Vec<CatalogProofRole>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogQualificationPreview {
    pub schema_version: String,
    pub configuration_authority_id: String,
    pub pre_authority_configuration_sha256: String,
    pub discovery_receipt_sha256: String,
    pub targets: Vec<CatalogQualificationTarget>,
    pub maximum_inference_requests: u32,
    pub maximum_openrouter_micro_usd: u64,
    pub network_effect: bool,
    pub inference_effect: bool,
    pub credential_read_effect: bool,
    pub preview_sha256: String,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CatalogQualificationStatus {
    DiscoveredUnqualified,
    Qualifying,
    Qualified,
    Rejected,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogModelQualificationReceipt {
    pub target: CatalogQualificationTarget,
    pub status: CatalogQualificationStatus,
    pub catalog_version: String,
    pub catalog_sha256: String,
    pub model_sha256: String,
    pub policy_version: Option<String>,
    pub policy_sha256: Option<String>,
    pub conformance_sha256: Option<String>,
    pub evidence_ref: Option<String>,
    pub exclusion_reason: Option<String>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogQualificationReceipt {
    pub schema_version: String,
    pub configuration_authority_id: String,
    pub pre_authority_configuration_sha256: String,
    pub discovery_receipt_sha256: String,
    pub completed_at_epoch_seconds: u64,
    pub results: Vec<CatalogModelQualificationReceipt>,
    /// Exact deterministic artifacts produced from the accepted discovery
    /// snapshots and completed live qualification attempts. Consumers must
    /// serialize `content` with the repository's canonical JSON encoder and
    /// verify `content_sha256` before writing or provisioning.
    pub artifacts: Vec<CatalogQualificationArtifact>,
    pub receipt_sha256: String,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CatalogQualificationArtifactKind {
    CatalogBundle,
    PolicyPack,
    ConformanceSuite,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogQualificationArtifact {
    pub kind: CatalogQualificationArtifactKind,
    pub file_name: String,
    pub provider: Option<ProviderService>,
    pub phase_id: Option<PhaseId>,
    pub content_sha256: String,
    pub content: serde_json::Value,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogProofArtifact {
    pub kind: String,
    pub provider: Option<ProviderService>,
    pub content_sha256: String,
    pub byte_length: u64,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogProofArtifactManifest {
    pub schema_version: String,
    pub configuration_authority_id: String,
    pub pre_authority_configuration_sha256: String,
    pub discovery_receipt_sha256: String,
    pub qualification_receipt_sha256: Option<String>,
    pub artifacts: Vec<CatalogProofArtifact>,
    pub manifest_sha256: String,
}

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

    #[test]
    fn apply_contracts_deny_unknown_fields_and_keep_confirmation_out_of_receipts() {
        let value = serde_json::json!({
            "preview": {
                "schema_version": CATALOG_PROOF_SCHEMA_VERSION,
                "configuration_authority_id": "configuration.device.alpha",
                "pre_authority_configuration_sha256": "a".repeat(64),
                "phases": [],
                "maximum_provider_requests": 0,
                "network_effect": true,
                "inference_effect": false,
                "credential_read_effect": true,
                "preview_sha256": "b".repeat(64)
            },
            "expected_preview_sha256": "a".repeat(64),
            "confirmation": LIVE_CATALOG_DISCOVERY_CONFIRMATION,
            "secret": "forbidden"
        });
        assert!(serde_json::from_value::<CatalogDiscoveryApplyRequest>(value).is_err());
        let receipt = CatalogDiscoveryReceipt {
            schema_version: CATALOG_PROOF_SCHEMA_VERSION.to_owned(),
            configuration_authority_id: "configuration.device.alpha".to_owned(),
            pre_authority_configuration_sha256: "a".repeat(64),
            completed_at_epoch_seconds: 1,
            phases: Vec::new(),
            receipt_sha256: "b".repeat(64),
        };
        let encoded = serde_json::to_string(&receipt).expect("receipt JSON");
        assert!(!encoded.contains("confirmation"));
        assert!(!encoded.contains("secret"));
    }
}