Skip to main content

arcp_runtime/runtime/
session.rs

1//! Per-session bookkeeping owned by the runtime.
2
3use arcp_core::ids::SessionId;
4use arcp_core::messages::Capabilities;
5
6/// Phase of the four-step handshake (RFC §8.1).
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub enum HandshakePhase {
9    /// `session.open` received; waiting for credential validation outcome.
10    Opened,
11    /// `session.challenge` sent; awaiting `session.authenticate`.
12    Challenged,
13    /// `session.accepted` sent; protocol traffic permitted.
14    Accepted,
15    /// Terminal: rejected or evicted.
16    Closed,
17}
18
19/// Server-side bookkeeping for one session.
20#[derive(Debug, Clone)]
21pub struct SessionState {
22    /// Session identifier.
23    pub session_id: SessionId,
24    /// Authenticated principal (set after `session.accepted`).
25    pub principal: Option<String>,
26    /// Negotiated capability set.
27    pub capabilities: Capabilities,
28    /// Current handshake phase.
29    pub phase: HandshakePhase,
30    /// Active challenge nonce (set during `Challenged`).
31    pub active_challenge: Option<String>,
32}
33
34impl SessionState {
35    /// Construct a new session in [`HandshakePhase::Opened`].
36    #[must_use]
37    pub const fn new(session_id: SessionId, capabilities: Capabilities) -> Self {
38        Self {
39            session_id,
40            principal: None,
41            capabilities,
42            phase: HandshakePhase::Opened,
43            active_challenge: None,
44        }
45    }
46
47    /// True when the session has completed the handshake and may carry
48    /// non-handshake protocol traffic.
49    #[must_use]
50    pub const fn is_accepted(&self) -> bool {
51        matches!(self.phase, HandshakePhase::Accepted)
52    }
53}