Skip to main content

subc_protocol/
session.rs

1//! Session route control wire contract.
2//!
3//! subc has two distinct channel-0 handshakes. Module registration is the
4//! module-to-subc `HELLO`/`HELLO_ACK` handshake that registers the manifest and
5//! liveness. Route bind is the client-to-subc-to-module request/response
6//! handshake that binds one client route to a module route channel.
7//!
8//! Config is forwarded as an ordered, provenance-tagged tier list. subc treats
9//! every config document as opaque text and preserves `tier`, `source`, and
10//! `doc` exactly from the client `route.open` request to the module
11//! `route.bind`; it never parses, merges, partitions, or relabels config in
12//! transit.
13
14use serde::{Deserialize, Serialize};
15
16use crate::{BindIdentity, RouteTarget};
17
18/// One provenance-tagged config tier supplied during route open.
19#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
20pub struct ConfigTier {
21    /// Trust label for this tier.
22    ///
23    /// Known v1 values are `"user"` and `"project"`, but this is intentionally
24    /// an open string so a future tier is a value, not a struct change.
25    ///
26    /// SECURITY INVARIANT: the label is stamped from `source` (the file path
27    /// that was read), never derived from `doc` content. subc preserves labels
28    /// exactly and never relabels them in transit.
29    pub tier: String,
30    /// Absolute path the document was read from, used for provenance and by AFT
31    /// to infer format/JSONC behavior and produce diagnostics.
32    pub source: String,
33    /// Verbatim config-source text.
34    ///
35    /// subc treats this as opaque bytes-as-text and does not interpret, parse,
36    /// merge, or validate it. AFT owns parsing, including JSONC handling.
37    pub doc: String,
38}
39
40/// subc-to-module channel-0 control RPC body.
41#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
42#[serde(tag = "op")]
43pub enum ModuleControlRequest {
44    #[serde(rename = "route.bind")]
45    RouteBind {
46        route_channel: u16,
47        target: RouteTarget,
48        identity: BindIdentity,
49        #[serde(default)]
50        config: Vec<ConfigTier>,
51    },
52}
53
54/// Module-to-subc channel-0 response body.
55#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
56#[serde(tag = "op")]
57pub enum ModuleControlResponse {
58    /// ACK-only success. Rejections use the `FrameType::Error` lane.
59    #[serde(rename = "route.bind")]
60    RouteBindAck {},
61}
62
63/// Module-to-subc channel-0 push body.
64#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
65#[serde(tag = "op")]
66pub enum ModuleControlPush {
67    #[serde(rename = "route.status")]
68    RouteStatus { route_channel: u16, status: String },
69}