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}