Skip to main content

agent_graph_mcp/
operator_auth.rs

1//! OS-authenticated operator authority primitives. JSON caller labels are never authority.
2use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4use tokio::net::UnixStream;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(rename_all = "snake_case")]
8pub enum OperatorAction {
9    Approve,
10    Reject,
11    DeleteGraph,
12    SetGraphRetention,
13    ApproveGraphDeletion,
14    ClearExecutionLineage,
15    PurgeGraph,
16    PromoteTemplate,
17    Migrate,
18    Install,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
22#[serde(deny_unknown_fields)]
23pub struct AuthenticatedOperator {
24    pub uid: u32,
25    pub gid: u32,
26    pub action: OperatorAction,
27    pub resource_kind: String,
28    pub resource_id: String,
29    pub expected_state_digest: String,
30    pub nonce: String,
31    pub issued_at: DateTime<Utc>,
32    pub expires_at: DateTime<Utc>,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36#[serde(deny_unknown_fields)]
37pub struct AuthorizationReceipt {
38    pub receipt_id: String,
39    pub request_digest: String,
40    pub action: OperatorAction,
41    pub resource: String,
42    pub state_digest: String,
43    pub operator_uid: u32,
44    pub daemon_instance_id: String,
45    pub nonce: String,
46    pub issued_at: DateTime<Utc>,
47    pub expires_at: DateTime<Utc>,
48    pub consumed_at: Option<DateTime<Utc>>,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct PeerCredentials {
53    pub uid: u32,
54    pub gid: u32,
55}
56
57/// Obtain credentials from the kernel-owned Unix socket peer context.
58pub async fn peer_credentials(stream: &UnixStream) -> std::io::Result<PeerCredentials> {
59    let c = stream.peer_cred()?;
60    Ok(PeerCredentials {
61        uid: c.uid(),
62        gid: c.gid(),
63    })
64}
65
66pub fn validate_window(op: &AuthenticatedOperator, now: DateTime<Utc>) -> Result<(), &'static str> {
67    if op.nonce.is_empty() {
68        return Err("AUTHORIZATION_NONCE_REQUIRED");
69    }
70    if op.expires_at <= op.issued_at || op.expires_at <= now {
71        return Err("AUTHORIZATION_EXPIRED");
72    }
73    if op.issued_at > now {
74        return Err("AUTHORIZATION_NOT_YET_VALID");
75    }
76    Ok(())
77}