Skip to main content

systemprompt_models/bridge/
manifest.rs

1//! Signed manifest wire format.
2//!
3//! [`SignedManifest`] is the JSON document the gateway returns from
4//! `GET /v1/bridge/manifest` and the bridge consumes to drive its
5//! plugin / skill / agent / managed-MCP sync. Every public type in
6//! this module is part of that wire contract — the gateway server
7//! (in `crates/entry/api`) emits these structs and the bridge
8//! deserialises them, so any change here is a wire-format change.
9//!
10//! Signing, signature verification, and manifest construction live in
11//! the bridge crate (`bin/bridge/src/gateway/manifest.rs`) alongside
12//! the gateway client. Those layers pull in `ed25519-dalek` and
13//! `serde_jcs` which are not appropriate dependencies for this
14//! foundation crate.
15
16use std::collections::BTreeMap;
17
18use serde::{Deserialize, Serialize};
19
20pub use crate::bridge::ids::ManifestSignature;
21use crate::bridge::ids::{
22    ManagedMcpServerName, PluginId, Sha256Digest, SkillId, SkillName, ToolName, ToolPolicy,
23};
24use crate::bridge::manifest_version::ManifestVersion;
25use crate::services::hooks::{HookCategory, HookEvent};
26use crate::services::plugin::PluginComponentRef;
27use systemprompt_identifiers::{AgentId, AgentName, HookId, TenantId, UserId, ValidatedUrl};
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct SignedManifest {
31    pub manifest_version: ManifestVersion,
32    pub issued_at: String,
33    pub not_before: String,
34    pub user_id: UserId,
35    pub tenant_id: Option<TenantId>,
36    #[serde(default)]
37    pub user: Option<UserInfo>,
38    pub plugins: Vec<PluginEntry>,
39    #[serde(default)]
40    pub skills: Vec<SkillEntry>,
41    #[serde(default)]
42    pub agents: Vec<AgentEntry>,
43    #[serde(default)]
44    pub hooks: Vec<HookEntry>,
45    pub managed_mcp_servers: Vec<ManagedMcpServer>,
46    pub revocations: Vec<String>,
47    #[serde(default)]
48    pub enabled_hosts: Vec<String>,
49    /// Optional per-host wire-protocol filter, keyed by host id. A present
50    /// entry overrides the host's built-in default `accepted_protocols`; an
51    /// empty value means "all models" (no restriction). An absent entry leaves
52    /// the host on its default.
53    #[serde(default)]
54    pub host_model_protocols: BTreeMap<String, Vec<String>>,
55    /// Detached ed25519 signature of the canonicalised payload (every
56    /// field above this one). Always present on the wire even for
57    /// unsigned manifests, where it is the empty string.
58    pub signature: ManifestSignature,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct UserInfo {
63    pub id: UserId,
64    pub name: String,
65    pub email: String,
66    #[serde(default)]
67    pub display_name: Option<String>,
68    #[serde(default)]
69    pub roles: Vec<String>,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct PluginEntry {
74    pub id: PluginId,
75    pub version: String,
76    pub sha256: Sha256Digest,
77    pub files: Vec<PluginFile>,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct PluginFile {
82    pub path: String,
83    pub sha256: Sha256Digest,
84    pub size: u64,
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct SkillEntry {
89    pub id: SkillId,
90    pub name: SkillName,
91    pub description: String,
92    pub file_path: String,
93    #[serde(default)]
94    pub tags: Vec<String>,
95    pub sha256: Sha256Digest,
96    pub instructions: String,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct AgentEntry {
101    pub id: AgentId,
102    pub name: AgentName,
103    pub display_name: String,
104    pub description: String,
105    pub version: String,
106    pub endpoint: String,
107    pub enabled: bool,
108    pub is_default: bool,
109    pub is_primary: bool,
110    #[serde(default)]
111    pub provider: Option<String>,
112    #[serde(default)]
113    pub model: Option<String>,
114    #[serde(default)]
115    pub mcp_servers: PluginComponentRef,
116    #[serde(default)]
117    pub skills: PluginComponentRef,
118    #[serde(default)]
119    pub tags: Vec<String>,
120    #[serde(default)]
121    pub system_prompt: Option<String>,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct HookEntry {
126    pub id: HookId,
127    pub name: String,
128    pub description: String,
129    pub version: String,
130    pub event: HookEvent,
131    pub matcher: String,
132    pub command: String,
133    #[serde(default)]
134    pub is_async: bool,
135    pub category: HookCategory,
136    #[serde(default)]
137    pub tags: Vec<String>,
138    pub sha256: Sha256Digest,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct ManagedMcpServer {
143    pub name: ManagedMcpServerName,
144    pub url: ValidatedUrl,
145    #[serde(skip_serializing_if = "Option::is_none")]
146    pub transport: Option<String>,
147    #[serde(skip_serializing_if = "Option::is_none")]
148    pub headers: Option<BTreeMap<String, String>>,
149    #[serde(skip_serializing_if = "Option::is_none")]
150    pub oauth: Option<bool>,
151    #[serde(skip_serializing_if = "Option::is_none")]
152    pub tool_policy: Option<BTreeMap<ToolName, ToolPolicy>>,
153}