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    PromoteTemplate,
13    Migrate,
14    Install,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
18#[serde(deny_unknown_fields)]
19pub struct AuthenticatedOperator {
20    pub uid: u32,
21    pub gid: u32,
22    pub action: OperatorAction,
23    pub resource_kind: String,
24    pub resource_id: String,
25    pub expected_state_digest: String,
26    pub nonce: String,
27    pub issued_at: DateTime<Utc>,
28    pub expires_at: DateTime<Utc>,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
32#[serde(deny_unknown_fields)]
33pub struct AuthorizationReceipt {
34    pub receipt_id: String,
35    pub request_digest: String,
36    pub action: OperatorAction,
37    pub resource: String,
38    pub state_digest: String,
39    pub operator_uid: u32,
40    pub daemon_instance_id: String,
41    pub nonce: String,
42    pub issued_at: DateTime<Utc>,
43    pub expires_at: DateTime<Utc>,
44    pub consumed_at: Option<DateTime<Utc>>,
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct PeerCredentials {
49    pub uid: u32,
50    pub gid: u32,
51}
52
53/// Obtain credentials from the kernel-owned Unix socket peer context.
54pub async fn peer_credentials(stream: &UnixStream) -> std::io::Result<PeerCredentials> {
55    let c = stream.peer_cred()?;
56    Ok(PeerCredentials {
57        uid: c.uid(),
58        gid: c.gid(),
59    })
60}
61
62pub fn validate_window(op: &AuthenticatedOperator, now: DateTime<Utc>) -> Result<(), &'static str> {
63    if op.nonce.is_empty() {
64        return Err("AUTHORIZATION_NONCE_REQUIRED");
65    }
66    if op.expires_at <= op.issued_at || op.expires_at <= now {
67        return Err("AUTHORIZATION_EXPIRED");
68    }
69    if op.issued_at > now {
70        return Err("AUTHORIZATION_NOT_YET_VALID");
71    }
72    Ok(())
73}