//! `GSH_*` env-var contract for child processes spawned inside gsh. //! //! org-ops-core (substrate-level operations library) reads these //! discrete env vars to construct a `GshContext` without re-parsing //! the `GSAP_SESSION_AC` JSON blob: //! //! | Variable | Source | //! |----------------------|---------------------------------------| //! | `GSH_DID` | `principal.did` (canonical string) | //! | `GSH_ACCORD_HASH` | `accord_hash` | //! | `GSH_SHELL_CLASS` | `shell_class` | //! | `GSH_SHELL_TIER` | `shell_tier` numeric (0–6) | //! | `GSH_POSTURE_LEVEL` | `posture_level` (decimal) | //! | `GSH_CAPABILITY_SET` | `capability_set` formatted `0x{:08x}` | //! //! The legacy `GSAP_SESSION_*` exports are kept by the gsh binary for //! existing consumers; this module is purely additive. use std::process::Command; use crate::ac::AuthorizationContext; use crate::shell_tier::ShellTier; /// Apply the `GSH_*` env-var contract to a child `Command`. /// /// Each parameter is `Option`-typed; absent values leave the /// corresponding env var unset (the child sees no `GSH_FOO` rather /// than an empty `GSH_FOO`). Caller passes whatever subset is known — /// ungoverned mode might supply only `did`. pub fn apply( cmd: &mut Command, did: Option<&str>, accord_hash: Option<&str>, shell_class: Option<&str>, shell_tier: Option, posture_level: Option, capability_set: Option, cap_bounding: Option, ) { if let Some(d) = did { cmd.env("GSH_DID", d); } if let Some(h) = accord_hash { cmd.env("GSH_ACCORD_HASH", h); } if let Some(c) = shell_class { cmd.env("GSH_SHELL_CLASS", c); } if let Some(tier) = shell_tier { cmd.env("GSH_SHELL_TIER", tier.numeric_level().to_string()); } if let Some(p) = posture_level { cmd.env("GSH_POSTURE_LEVEL", p.to_string()); } if let Some(c) = capability_set { cmd.env("GSH_CAPABILITY_SET", format!("0x{:08x}", c)); } if let Some(b) = cap_bounding { cmd.env("GSH_CAP_BOUNDING", format!("0x{b:016x}")); } } /// Apply the `GSH_*` env-var contract from a parsed AC. pub fn apply_from_ac(cmd: &mut Command, ac: &AuthorizationContext) { let did = ac .principal .as_ref() .and_then(|p| p.did.as_ref()) .map(|d| d.as_str().to_owned()); let tier = ac .shell_tier .and_then(ShellTier::from_numeric) .or_else(|| ac.shell_class.as_deref().map(ShellTier::from_shell_class)); apply( cmd, did.as_deref(), ac.accord_hash.as_deref(), ac.shell_class.as_deref(), tier, ac.posture_level, ac.capability_set, None, ); } #[cfg(test)] mod tests { use super::*; use std::process::Command; fn cmd_env(cmd: &Command, key: &str) -> Option { cmd.get_envs().find_map(|(k, v)| { if k == std::ffi::OsStr::new(key) { v.map(|s| s.to_string_lossy().into_owned()) } else { None } }) } #[test] fn apply_all_fields() { let mut cmd = Command::new("true"); apply( &mut cmd, Some("did:web:guildhouse.dev:user:tking"), Some("sha256:abcd"), Some("Application"), Some(ShellTier::T2Operator), Some(3), Some(0xCAFEBABE), Some(0x0000000000200404), ); assert_eq!( cmd_env(&cmd, "GSH_DID").as_deref(), Some("did:web:guildhouse.dev:user:tking") ); assert_eq!(cmd_env(&cmd, "GSH_ACCORD_HASH").as_deref(), Some("sha256:abcd")); assert_eq!(cmd_env(&cmd, "GSH_SHELL_CLASS").as_deref(), Some("Application")); assert_eq!(cmd_env(&cmd, "GSH_SHELL_TIER").as_deref(), Some("2")); assert_eq!(cmd_env(&cmd, "GSH_POSTURE_LEVEL").as_deref(), Some("3")); assert_eq!( cmd_env(&cmd, "GSH_CAPABILITY_SET").as_deref(), Some("0xcafebabe") ); assert_eq!( cmd_env(&cmd, "GSH_CAP_BOUNDING").as_deref(), Some("0x0000000000200404") ); } #[test] fn apply_partial_only_did() { let mut cmd = Command::new("true"); apply(&mut cmd, Some("did:web:foo:bar"), None, None, None, None, None, None); assert_eq!(cmd_env(&cmd, "GSH_DID").as_deref(), Some("did:web:foo:bar")); assert!(cmd_env(&cmd, "GSH_ACCORD_HASH").is_none()); assert!(cmd_env(&cmd, "GSH_SHELL_CLASS").is_none()); assert!(cmd_env(&cmd, "GSH_SHELL_TIER").is_none()); assert!(cmd_env(&cmd, "GSH_POSTURE_LEVEL").is_none()); assert!(cmd_env(&cmd, "GSH_CAPABILITY_SET").is_none()); } #[test] fn apply_shell_tier_without_class() { let mut cmd = Command::new("true"); apply(&mut cmd, None, None, None, Some(ShellTier::T3Agent), None, None, None); assert!(cmd_env(&cmd, "GSH_SHELL_CLASS").is_none()); assert_eq!(cmd_env(&cmd, "GSH_SHELL_TIER").as_deref(), Some("3")); } #[test] fn apply_from_ac_full() { let ac_json = r#"{ "context_id":"x", "principal":{"did":"did:web:guildhouse.dev:user:tking"}, "accord_hash":"sha256:zz", "shell_class":"System", "capability_set":1, "posture_level":5 }"#; let ac: AuthorizationContext = serde_json::from_str(ac_json).unwrap(); let mut cmd = Command::new("true"); apply_from_ac(&mut cmd, &ac); assert_eq!( cmd_env(&cmd, "GSH_DID").as_deref(), Some("did:web:guildhouse.dev:user:tking") ); assert_eq!(cmd_env(&cmd, "GSH_ACCORD_HASH").as_deref(), Some("sha256:zz")); assert_eq!(cmd_env(&cmd, "GSH_SHELL_CLASS").as_deref(), Some("System")); assert_eq!(cmd_env(&cmd, "GSH_SHELL_TIER").as_deref(), Some("1")); assert_eq!(cmd_env(&cmd, "GSH_POSTURE_LEVEL").as_deref(), Some("5")); assert_eq!( cmd_env(&cmd, "GSH_CAPABILITY_SET").as_deref(), Some("0x00000001") ); } #[test] fn apply_from_ac_with_explicit_tier() { let ac_json = r#"{ "context_id":"x", "principal":{"did":"did:web:example.com:user:bob"}, "shell_class":"Application", "shell_tier":3 }"#; let ac: AuthorizationContext = serde_json::from_str(ac_json).unwrap(); let mut cmd = Command::new("true"); apply_from_ac(&mut cmd, &ac); assert_eq!(cmd_env(&cmd, "GSH_SHELL_CLASS").as_deref(), Some("Application")); assert_eq!(cmd_env(&cmd, "GSH_SHELL_TIER").as_deref(), Some("3")); } #[test] fn apply_from_legacy_ac_no_governance_fields() { let ac_json = r#"{"context_id":"legacy","principal":{"did":"did:web:foo:bar"}}"#; let ac: AuthorizationContext = serde_json::from_str(ac_json).unwrap(); let mut cmd = Command::new("true"); apply_from_ac(&mut cmd, &ac); assert_eq!(cmd_env(&cmd, "GSH_DID").as_deref(), Some("did:web:foo:bar")); // No governance metadata — none of the other GSH_* vars set. assert!(cmd_env(&cmd, "GSH_ACCORD_HASH").is_none()); assert!(cmd_env(&cmd, "GSH_SHELL_CLASS").is_none()); assert!(cmd_env(&cmd, "GSH_POSTURE_LEVEL").is_none()); assert!(cmd_env(&cmd, "GSH_CAPABILITY_SET").is_none()); } }