1pub(crate) mod marker;
9pub mod model;
10pub mod store;
11pub mod trust;
12
13use std::path::PathBuf;
14
15pub use model::{OrgPolicyV1, OrgVerifyResult};
16pub use trust::{TrustStore, TrustedKey};
17
18use crate::core::policy::ResolvedPolicy;
19
20#[must_use]
21pub fn org_key_id(org: &str) -> String {
22 let safe: String = org
23 .chars()
24 .map(|c| {
25 if c.is_ascii_alphanumeric() || c == '-' {
26 c.to_ascii_lowercase()
27 } else {
28 '-'
29 }
30 })
31 .collect();
32 format!("org-{safe}")
33}
34
35#[must_use]
42pub fn active_resolved() -> Option<ResolvedPolicy> {
43 let artifact = store::load_active()?;
44
45 let result = artifact.verify();
46 if !result.signature_valid {
47 tracing::warn!(
48 "org policy: signature invalid ({}), ignoring",
49 result.error.as_deref().unwrap_or("unknown")
50 );
51 return None;
52 }
53
54 let signer = artifact.signer_public_key.as_deref()?;
55 if !trust::is_trusted(signer) {
56 tracing::debug!("org policy: signer not trust-pinned, ignoring (fail-open)");
57 return None;
58 }
59
60 if !artifact.enforced {
61 tracing::debug!("org policy: admin did not set enforced=true, skipping");
62 return None;
63 }
64
65 match artifact.resolved() {
66 Ok(resolved) => Some(resolved),
67 Err(e) => {
68 tracing::warn!("org policy: failed to resolve pack: {e}");
69 None
70 }
71 }
72}
73
74pub fn active_resolved_checked() -> Result<Option<ResolvedPolicy>, String> {
83 AuthoritySnapshot::capture()?.resolve()
84}
85
86pub struct AuthoritySnapshot {
95 fingerprint: blake3::Hash,
96 before_marker: blake3::Hasher,
98 policy: Option<String>,
99 trust: Option<String>,
100 marker: Option<String>,
101}
102
103impl AuthoritySnapshot {
104 pub fn capture() -> Result<Self, String> {
107 let (policy_path, policy_required) = store::selected_source()?;
108 let mut hash = blake3::Hasher::new();
109 let mut read =
112 |path: PathBuf, required: bool, artifact: bool| -> Result<Option<String>, String> {
113 let path_bytes = path.as_os_str().as_encoded_bytes();
114 hash.update(&(path_bytes.len() as u64).to_le_bytes());
115 hash.update(path_bytes);
116 let text = if artifact {
117 crate::core::policy::files::read_org_artifact(&path, required, false)?
118 } else {
119 crate::core::policy::files::read(&path, required)?
120 };
121 hash_text(&mut hash, text.as_deref());
122 Ok(text)
123 };
124 let policy = read(policy_path, policy_required, true)?;
125 let trust = read(trust::trust_path_read_only()?, false, false)?;
126 let marker_path = marker::marker_path_read_only()?;
129 let path_bytes = marker_path.as_os_str().as_encoded_bytes();
130 hash.update(&(path_bytes.len() as u64).to_le_bytes());
131 hash.update(path_bytes);
132 let marker = crate::core::policy::files::read(&marker_path, false)?;
133 let before_marker = hash.clone();
134 hash_text(&mut hash, marker.as_deref());
135 Ok(Self {
136 fingerprint: hash.finalize(),
137 before_marker,
138 policy,
139 trust,
140 marker,
141 })
142 }
143
144 #[cfg(any(test, feature = "enterprise"))]
145 pub fn fingerprint(&self) -> blake3::Hash {
146 self.fingerprint
147 }
148
149 fn required(&self) -> Result<bool, String> {
152 Ok(self
153 .marker
154 .as_deref()
155 .map(marker::parse)
156 .transpose()?
157 .is_some_and(|marker| marker.is_required()))
158 }
159
160 pub fn resolve(&self) -> Result<Option<ResolvedPolicy>, String> {
166 self.resolve_observed().map(|(floor, _)| floor)
167 }
168
169 pub(crate) fn resolve_observed(
175 &self,
176 ) -> Result<(Option<ResolvedPolicy>, Option<Observation>), String> {
177 let required = self.required()?;
178 let Some(policy) = self.policy.as_deref() else {
179 if required {
180 return Err("required organization policy is missing".into());
181 }
182 return Ok((None, None));
183 };
184 if is_v2(policy) {
187 let verified = verify_v2(
188 policy,
189 self.trust.as_deref(),
190 self.marker.as_deref(),
191 std::time::Instant::now() + RESOLVE_DEADLINE,
192 )?;
193 let mut expected = self.before_marker.clone();
199 hash_text(&mut expected, Some(&verified.marker));
200 if Self::capture()?.fingerprint != expected.finalize() {
201 return Err("organization authority changed during verification".into());
202 }
203 verified.lifetime.check()?;
205 return Ok((verified.floor, Some(verified.observation)));
206 }
207 if policy.len() as u64 > crate::core::policy::files::MAX_BYTES {
209 return Err("policy input exceeds byte limit".into());
210 }
211 if required {
212 return Err("required organization policy is not verifiable".into());
213 }
214 let artifact = OrgPolicyV1::from_json(policy)?;
215 let trust: TrustStore = match self.trust.as_deref() {
216 Some(text) => {
217 toml::from_str(text).map_err(|_| "org trust input is invalid".to_string())?
218 }
219 None => TrustStore::default(),
220 };
221 resolve_checked(&artifact, &trust).map(|floor| (floor, None))
222 }
223}
224
225fn hash_text(hash: &mut blake3::Hasher, text: Option<&str>) {
226 match text {
227 Some(text) => {
228 hash.update(&[1]);
229 hash.update(&(text.len() as u64).to_le_bytes());
230 hash.update(text.as_bytes());
231 }
232 None => {
233 hash.update(&[0]);
234 }
235 }
236}
237
238pub(crate) fn is_v2(text: &str) -> bool {
243 let Ok(value) = serde_json::from_str::<serde_json::Value>(text) else {
244 return false;
245 };
246 let schema = value
247 .get("schema")
248 .or_else(|| value.get("statement").and_then(|s| s.get("schema")))
249 .and_then(serde_json::Value::as_str);
250 schema == Some(lean_ctx_protocol::org_policy_verifier::ORG_POLICY_V2_ARTIFACT_SCHEMA_ID)
251}
252
253pub(crate) fn verify_v2(
271 artifact: &str,
272 trust: Option<&str>,
273 marker_text: Option<&str>,
274 deadline: std::time::Instant,
275) -> Result<VerifiedV2, String> {
276 use crate::core::intelligence_runtime::org_policy_verifier::{Accepted, Snapshot, verify};
277 use lean_ctx_protocol::org_policy_verifier::{
278 OrgRequiredMarkerEntryV1, OrgRequiredMarkerStateV1,
279 };
280
281 let marker_text =
282 marker_text.ok_or("organization policy has no recorded requirement to verify against")?;
283 let stored = marker::parse(marker_text)?;
284 let accepted = verify(
285 &Snapshot {
286 organization: &stored.organization_id,
287 artifact: artifact.as_bytes(),
288 trust: trust.map(str::as_bytes),
289 marker: Some(marker_text.as_bytes()),
290 },
291 deadline,
292 )
293 .map_err(|_| "organization policy could not be verified")?;
294 let (state, sequence, pack, observed, statement) = match accepted {
297 Accepted::Enforce {
298 pack_toml,
299 policy_digest,
300 sequence,
301 valid_until,
302 } => {
303 let statement = policy_digest.as_str().to_owned();
304 (
305 OrgRequiredMarkerStateV1::Required { policy_digest },
306 sequence,
307 Some((pack_toml, valid_until)),
308 ObservedState::Required,
309 statement,
310 )
311 }
312 Accepted::NotRequired {
313 unpin_digest,
314 sequence,
315 } => {
316 let statement = unpin_digest.as_str().to_owned();
317 (
318 OrgRequiredMarkerStateV1::Unpinned { unpin_digest },
319 sequence,
320 None,
321 ObservedState::Unpinned,
322 statement,
323 )
324 }
325 };
326 let recorded_at = lean_ctx_protocol::TeamTimestampV1::new(
327 chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Nanos, true),
328 )
329 .map_err(|_| "organization policy decision could not be dated")?;
330 let (_, marker) = marker::advance_within(
331 &stored.organization_id,
332 OrgRequiredMarkerEntryV1 {
333 sequence,
334 recorded_at,
335 state,
336 },
337 marker::Predecessor::Stored(marker_text),
338 deadline,
339 )
340 .map_err(|_| "organization policy decision could not be recorded")?;
341 let (floor, horizon) = match pack {
342 Some((pack_toml, valid_until)) => {
343 let floor = crate::core::policy::parse(&pack_toml)
344 .and_then(|pack| crate::core::policy::resolve(&pack))
345 .map_err(|_| "verified organization policy is invalid")?;
346 let horizon = chrono::DateTime::parse_from_rfc3339(valid_until.as_str())
348 .map_err(|_| "verified organization policy has expired")?
349 .with_timezone(&chrono::Utc);
350 (Some(floor), Some(horizon))
351 }
352 None => (None, None),
353 };
354 let lifetime = Lifetime { horizon, deadline };
355 lifetime.check()?;
357 Ok(VerifiedV2 {
358 floor,
359 marker,
360 lifetime,
361 observation: Observation {
365 state: observed,
366 statement,
367 policy_version: sequence,
368 valid_until: horizon,
369 },
370 })
371}
372
373pub(crate) struct VerifiedV2 {
378 pub(crate) floor: Option<ResolvedPolicy>,
379 pub(crate) marker: String,
380 pub(crate) lifetime: Lifetime,
381 pub(crate) observation: Observation,
382}
383
384#[derive(Debug, Clone, Copy, PartialEq, Eq)]
386pub(crate) enum ObservedState {
387 Required,
389 Unpinned,
393}
394
395#[derive(Debug, Clone, PartialEq, Eq)]
405pub struct Observation {
406 state: ObservedState,
407 statement: String,
409 policy_version: u64,
410 valid_until: Option<chrono::DateTime<chrono::Utc>>,
413}
414
415impl Observation {
416 #[cfg(any(test, feature = "test-support"))]
419 pub fn unpinned_for_test(policy_version: u64, fill: char) -> Self {
420 Self {
421 state: ObservedState::Unpinned,
422 statement: format!("sha256:{}", fill.to_string().repeat(64)),
423 policy_version,
424 valid_until: None,
425 }
426 }
427
428 pub(crate) fn state(&self) -> ObservedState {
429 self.state
430 }
431
432 pub(crate) fn statement(&self) -> &str {
433 &self.statement
434 }
435
436 pub fn policy_version(&self) -> u64 {
437 self.policy_version
438 }
439
440 pub(crate) fn valid_until(&self) -> Option<chrono::DateTime<chrono::Utc>> {
441 self.valid_until
442 }
443}
444
445#[derive(Debug, Clone, Copy)]
451pub(crate) struct Lifetime {
452 horizon: Option<chrono::DateTime<chrono::Utc>>,
453 deadline: std::time::Instant,
454}
455
456impl Lifetime {
457 #[cfg(any(test, feature = "test-support"))]
460 pub(crate) fn for_test(
461 horizon: Option<chrono::DateTime<chrono::Utc>>,
462 deadline: std::time::Instant,
463 ) -> Self {
464 Self { horizon, deadline }
465 }
466
467 pub(crate) fn restored(horizon: Option<chrono::DateTime<chrono::Utc>>) -> Self {
472 Self {
473 horizon,
474 deadline: std::time::Instant::now(),
475 }
476 }
477
478 pub(crate) fn horizon(&self) -> Option<chrono::DateTime<chrono::Utc>> {
481 self.horizon
482 }
483
484 pub(crate) fn check(&self) -> Result<(), String> {
486 self.check_horizon()?;
487 if std::time::Instant::now() >= self.deadline {
488 return Err("organization policy verification deadline exceeded".into());
489 }
490 Ok(())
491 }
492
493 pub(crate) fn check_horizon(&self) -> Result<(), String> {
496 if self
497 .horizon
498 .is_some_and(|horizon| chrono::Utc::now() >= horizon)
499 {
500 return Err("verified organization policy has expired".into());
501 }
502 Ok(())
503 }
504}
505
506const RESOLVE_DEADLINE: std::time::Duration = std::time::Duration::from_secs(5);
509
510pub(crate) fn resolve_checked(
511 artifact: &OrgPolicyV1,
512 trusted: &TrustStore,
513) -> Result<Option<ResolvedPolicy>, String> {
514 if !artifact.verify().signature_valid {
515 return Err("org policy signature invalid".into());
516 }
517 let signer = artifact
518 .signer_public_key
519 .as_deref()
520 .ok_or_else(|| "org policy signer missing".to_string())?;
521 if !trusted
522 .keys
523 .iter()
524 .any(|key| key.public_key == signer && key.org == artifact.org)
525 {
526 return Err("org policy signer is not pinned for this organization".into());
527 }
528 let resolved = artifact.resolved().map_err(|error| error.to_string())?;
529 Ok(artifact.enforced.then_some(resolved))
530}
531
532#[derive(Debug, Default, PartialEq, Eq)]
534pub struct Uninstalled {
535 pub artifact: bool,
537 pub org: Option<String>,
539 pub unpinned: Vec<String>,
541 pub remaining: Vec<TrustedKey>,
545}
546
547pub fn uninstall() -> Result<Uninstalled, String> {
554 let path = store::installed_path()?;
555 let mut outcome = Uninstalled::default();
556 if path.exists() {
557 outcome.org = store::read(&path).ok().map(|artifact| artifact.org);
561 outcome.artifact = store::uninstall()?;
562 }
563 if let Some(org) = &outcome.org {
564 outcome.unpinned = trust::remove_org(org)?;
565 }
566 outcome.remaining = trust::load()?.keys;
567 Ok(outcome)
568}
569
570#[cfg(test)]
573mod authority_tests {
574 use super::*;
575 use lean_ctx_protocol::org_policy_verifier::{
576 OrgRequiredMarkerEntryV1, OrgRequiredMarkerStateV1,
577 };
578 use lean_ctx_protocol::{RecordDigest, TeamId, TeamTimestampV1};
579
580 const PACK: &str = "name = \"floor\"\nversion = \"1.0.0\"\ndescription = \"test\"\n[context]\ndeny_tools = [\"ctx_shell\"]\n";
581
582 fn install_policy() -> OrgPolicyV1 {
584 let key = ed25519_dalek::SigningKey::from_bytes(&[23; 32]);
585 let mut artifact = OrgPolicyV1::build("acme", "1", true, PACK).unwrap();
586 artifact.sign_with_key(&key);
587 store::install(&artifact).unwrap();
588 artifact
589 }
590
591 fn pin(org: &str, artifact: &OrgPolicyV1) {
592 trust::pin(org, artifact.signer_public_key.as_deref().unwrap()).unwrap();
593 }
594
595 fn require(sequence: u64) {
596 let entry = OrgRequiredMarkerEntryV1 {
597 sequence,
598 recorded_at: TeamTimestampV1::new("2026-10-10T09:00:00Z").unwrap(),
599 state: OrgRequiredMarkerStateV1::Required {
600 policy_digest: RecordDigest::new(format!("sha256:{}", "a".repeat(64))).unwrap(),
601 },
602 };
603 let path = marker::marker_path_read_only().unwrap();
606 let stored = crate::core::policy::files::read(&path, false).unwrap();
607 let predecessor = stored.as_deref().map_or(
608 marker::Predecessor::NeverStored,
609 marker::Predecessor::Stored,
610 );
611 marker::advance(&TeamId::new("acme").unwrap(), entry, predecessor).unwrap();
612 }
613
614 #[test]
615 fn status_reports_a_key_pinned_for_another_organization_as_not_applied() {
616 let _data = crate::core::data_dir::isolated_data_dir();
617 let artifact = install_policy();
618 pin("other", &artifact);
619
620 let status = status();
621 assert!(status.present && status.signature_valid);
622 assert!(!status.trusted, "the key is pinned, but not for this org");
623 assert!(!status.applied);
624 assert!(
625 active_resolved_checked().is_err(),
626 "enforcement refuses the same state"
627 );
628
629 pin("acme", &artifact);
630 assert!(!self::status().trusted);
632
633 let key = artifact.signer_public_key.as_deref().unwrap();
637 assert!(trust::is_trusted(key), "the key alone is pinned");
638 assert!(!trust::is_trusted_for("acme", key));
639 assert!(trust::is_trusted_for("other", key));
640 assert_eq!(trust::pinned_organization(key).as_deref(), Some("other"));
641 assert_eq!(
642 trust::pin_for("acme", key).unwrap(),
643 trust::PinOutcome::PinnedForAnother("other".into())
644 );
645 assert_eq!(
646 trust::pin_for("other", key).unwrap(),
647 trust::PinOutcome::AlreadyPinned
648 );
649 assert!(!trust::is_trusted_for("acme", key), "nothing was moved");
650 assert_eq!(trust::trusted_keys().len(), 1);
651 }
652
653 #[test]
657 fn status_creates_nothing_and_reports_a_missing_explicit_source() {
658 let data = crate::core::data_dir::isolated_data_dir();
659 let observed = || {
660 [
661 store::selected_source().unwrap().0,
662 trust::trust_path_read_only().unwrap(),
663 marker::marker_path_read_only().unwrap(),
664 ]
665 .map(|path| {
666 (
667 path.exists(),
668 path.parent().is_some_and(std::path::Path::exists),
669 )
670 })
671 };
672 let before = observed();
673 let status = status();
674 assert!(!status.present && status.resolve_error.is_none());
675 assert_eq!(status.recorded, RecordedRequirement::Absent);
676 assert_eq!(observed(), before, "the status left a trace on disk");
677
678 let missing = data.path().join("does-not-exist.json");
679 crate::test_env::set_var("LEANCTX_ORG_POLICY", &missing);
680 let status = self::status();
681 crate::test_env::remove_var("LEANCTX_ORG_POLICY");
682 assert!(
683 status.resolve_error.is_some(),
684 "an explicit source that is missing is not 'nothing installed'"
685 );
686 assert!(!missing.exists());
687 }
688
689 #[test]
690 fn status_and_enforcement_agree_for_a_correctly_pinned_policy() {
691 let _data = crate::core::data_dir::isolated_data_dir();
692 let artifact = install_policy();
693 pin("acme", &artifact);
694
695 let status = status();
696 assert!(status.trusted && status.applied && status.resolve_error.is_none());
697 assert!(active_resolved_checked().unwrap().is_some());
698 }
699
700 #[test]
701 fn a_recorded_requirement_or_a_damaged_marker_is_never_reported_as_applied() {
702 let _data = crate::core::data_dir::isolated_data_dir();
703 let artifact = install_policy();
704 pin("acme", &artifact);
705 require(1);
706
707 let status = status();
708 assert!(status.present && status.trusted);
709 assert!(!status.applied, "a version-one policy does not satisfy it");
710 assert!(status.resolve_error.is_some());
711 assert!(active_resolved_checked().is_err());
712
713 store::uninstall().unwrap();
715 let status = self::status();
716 assert!(!status.present && !status.applied);
717 assert!(status.resolve_error.is_some());
718 assert!(active_resolved_checked().is_err());
719
720 std::fs::write(marker::marker_path_read_only().unwrap(), b"{}").unwrap();
722 assert!(self::status().resolve_error.is_some());
723 assert!(active_resolved_checked().is_err());
724 store::install(&artifact).unwrap();
725 let status = self::status();
726 assert!(!status.applied && status.resolve_error.is_some());
727 assert!(AuthoritySnapshot::capture().unwrap().resolve().is_err());
728 }
729
730 #[test]
734 fn the_stored_record_is_read_as_recorded_absent_or_unavailable() {
735 let _data = crate::core::data_dir::isolated_data_dir();
736 assert_eq!(recorded_requirement(), RecordedRequirement::Absent);
737 assert_eq!(status().recorded, RecordedRequirement::Absent);
738
739 require(3);
740 match recorded_requirement() {
741 RecordedRequirement::Recorded {
742 organization,
743 required: true,
744 sequence: 3,
745 entries: 1,
746 ..
747 } => assert_eq!(organization, "acme"),
748 other => panic!("not the recorded requirement: {other:?}"),
749 }
750
751 let path = marker::marker_path_read_only().unwrap();
752 let stored = crate::core::policy::files::read(&path, false)
753 .unwrap()
754 .unwrap();
755 marker::advance(
756 &TeamId::new("acme").unwrap(),
757 OrgRequiredMarkerEntryV1 {
758 sequence: 4,
759 recorded_at: TeamTimestampV1::new("2026-10-11T09:00:00Z").unwrap(),
760 state: OrgRequiredMarkerStateV1::Unpinned {
761 unpin_digest: RecordDigest::new(format!("sha256:{}", "b".repeat(64))).unwrap(),
762 },
763 },
764 marker::Predecessor::Stored(&stored),
765 )
766 .unwrap();
767 let status = status();
768 assert!(!status.present && status.source.is_none());
769 assert!(
770 status.resolve_error.is_none(),
771 "a lifted requirement refuses nothing"
772 );
773 match status.recorded {
774 RecordedRequirement::Recorded {
775 required: false,
776 sequence: 4,
777 entries: 2,
778 statement,
779 ..
780 } => assert_eq!(statement, format!("sha256:{}", "b".repeat(64))),
781 other => panic!("the unpin is not on record: {other:?}"),
782 }
783
784 std::fs::write(&path, b"{}").unwrap();
785 assert!(matches!(
786 recorded_requirement(),
787 RecordedRequirement::Unavailable(_)
788 ));
789 }
790
791 #[test]
792 fn a_capture_resolves_its_own_bytes_whatever_changes_afterwards() {
793 let _data = crate::core::data_dir::isolated_data_dir();
794 let artifact = install_policy();
795 pin("acme", &artifact);
796
797 let enforced = AuthoritySnapshot::capture().unwrap();
798 store::uninstall().unwrap();
801 trust::remove(artifact.signer_public_key.as_deref().unwrap()).unwrap();
802 require(1);
803 assert!(
804 enforced.resolve().unwrap().is_some(),
805 "the capture answers for the bytes it holds"
806 );
807
808 let now = AuthoritySnapshot::capture().unwrap();
809 assert_ne!(now.fingerprint(), enforced.fingerprint());
810 assert!(now.resolve().is_err(), "required, and nothing installed");
811
812 let before = AuthoritySnapshot::capture().unwrap().fingerprint();
814 store::install(&artifact).unwrap();
815 let with_policy = AuthoritySnapshot::capture().unwrap().fingerprint();
816 pin("acme", &artifact);
817 let with_trust = AuthoritySnapshot::capture().unwrap().fingerprint();
818 require(2);
819 let with_marker = AuthoritySnapshot::capture().unwrap().fingerprint();
820 assert!(before != with_policy && with_policy != with_trust && with_trust != with_marker);
821 }
822
823 #[test]
824 fn an_unreadable_explicit_source_refuses_instead_of_resolving_to_no_policy() {
825 let data = crate::core::data_dir::isolated_data_dir();
826 let missing = data.path().join("does-not-exist.json");
827 crate::test_env::set_var("LEANCTX_ORG_POLICY", &missing);
828 let captured = AuthoritySnapshot::capture();
829 crate::test_env::remove_var("LEANCTX_ORG_POLICY");
830 assert!(captured.is_err(), "an explicit source that is absent");
831 }
832}
833
834#[cfg(test)]
835mod checked_tests {
836 use super::*;
837
838 fn fixture(enforced: bool) -> (OrgPolicyV1, TrustStore) {
839 let key = ed25519_dalek::SigningKey::from_bytes(&[19; 32]);
840 let mut artifact = OrgPolicyV1::build(
841 "acme", "1", enforced,
842 "name = \"floor\"\nversion = \"1.0.0\"\ndescription = \"test\"\n[context]\ndeny_tools = [\"ctx_shell\"]\n",
843 ).unwrap();
844 artifact.sign_with_key(&key);
845 let trusted = TrustStore {
846 keys: vec![TrustedKey {
847 org: "acme".into(),
848 public_key: artifact.signer_public_key.clone().unwrap(),
849 added_at: "2026-09-09T00:00:00Z".into(),
850 }],
851 };
852 (artifact, trusted)
853 }
854
855 #[test]
856 fn checked_resolution_requires_valid_signature_and_org_pin() {
857 let (mut artifact, mut trusted) = fixture(true);
858 assert!(resolve_checked(&artifact, &trusted).unwrap().is_some());
859 assert!(resolve_checked(&artifact, &TrustStore::default()).is_err());
860 trusted.keys[0].org = "other".into();
861 assert!(resolve_checked(&artifact, &trusted).is_err());
862 trusted.keys[0].org = "acme".into();
863 artifact.enforced = false;
864 assert!(resolve_checked(&artifact, &trusted).is_err());
865 }
866
867 #[test]
868 fn checked_resolution_allows_only_trusted_signed_disable() {
869 let (artifact, trusted) = fixture(false);
870 assert!(resolve_checked(&artifact, &trusted).unwrap().is_none());
871 assert!(resolve_checked(&artifact, &TrustStore::default()).is_err());
872 }
873}
874
875#[derive(Debug, Clone, Default, PartialEq, Eq)]
882pub enum RecordedRequirement {
883 #[default]
885 Absent,
886 Unavailable(String),
889 Recorded {
891 organization: String,
892 required: bool,
895 sequence: u64,
897 statement: String,
899 recorded_at: String,
900 entries: usize,
902 },
903}
904
905#[must_use]
907pub fn recorded_requirement() -> RecordedRequirement {
908 use lean_ctx_protocol::org_policy_verifier::OrgRequiredMarkerStateV1;
909
910 let text = match marker::marker_path_read_only()
911 .and_then(|path| crate::core::policy::files::read(&path, false))
912 {
913 Ok(Some(text)) => text,
914 Ok(None) => return RecordedRequirement::Absent,
915 Err(error) => return RecordedRequirement::Unavailable(error),
916 };
917 let stored = match marker::parse(&text) {
918 Ok(stored) => stored,
919 Err(error) => return RecordedRequirement::Unavailable(error),
920 };
921 let current = stored.current();
922 let (required, statement) = match ¤t.state {
923 OrgRequiredMarkerStateV1::Required { policy_digest } => (true, policy_digest.as_str()),
924 OrgRequiredMarkerStateV1::Unpinned { unpin_digest } => (false, unpin_digest.as_str()),
925 };
926 RecordedRequirement::Recorded {
927 organization: stored.organization_id.as_str().to_owned(),
928 required,
929 sequence: current.sequence,
930 statement: statement.to_owned(),
931 recorded_at: current.recorded_at.as_str().to_owned(),
932 entries: stored.entries.len(),
933 }
934}
935
936#[derive(Debug, Clone, Default)]
937pub struct OrgStatus {
938 pub recorded: RecordedRequirement,
940 pub present: bool,
941 pub source: Option<PathBuf>,
942 pub org: Option<String>,
943 pub policy_version: Option<String>,
944 pub enforced: bool,
945 pub issued_at: Option<String>,
946 pub signer_public_key: Option<String>,
947 pub signature_valid: bool,
948 pub trusted: bool,
949 pub applied: bool,
950 pub resolve_error: Option<String>,
951 pub pinned_anchors: usize,
952 pub floor_preview: Option<ResolvedPolicy>,
956}
957
958#[must_use]
959pub fn status() -> OrgStatus {
960 let recorded = recorded_requirement();
966 let required = match &recorded {
969 RecordedRequirement::Absent => Ok(false),
970 RecordedRequirement::Recorded { required, .. } => Ok(*required),
971 RecordedRequirement::Unavailable(error) => Err(error.clone()),
972 };
973 let (anchors, trust_error) = match trust::load_read_only() {
976 Ok(store) => (store.keys, None),
977 Err(error) => (Vec::new(), Some(error)),
978 };
979 let pinned_anchors = anchors.len();
980 let (source, text) = match store::selected_source() {
984 Ok((path, explicit)) => {
985 match crate::core::policy::files::read_org_artifact(&path, explicit, false) {
986 Ok(text) => (text.as_ref().map(|_| path), text),
987 Err(error) => {
988 return OrgStatus {
989 recorded,
990 present: true,
991 source: Some(path),
992 resolve_error: Some(error),
993 pinned_anchors,
994 ..Default::default()
995 };
996 }
997 }
998 }
999 Err(error) => {
1000 return OrgStatus {
1001 recorded,
1002 resolve_error: Some(error),
1003 pinned_anchors,
1004 ..Default::default()
1005 };
1006 }
1007 };
1008 let artifact = text
1009 .as_deref()
1010 .filter(|text| text.len() as u64 <= crate::core::policy::files::MAX_BYTES)
1011 .and_then(|text| OrgPolicyV1::from_json(text).ok());
1012 let Some(artifact) = artifact else {
1013 let resolve_error = match required {
1017 Ok(true) => Some(
1018 "an organization policy is required on this endpoint, but none that can be \
1019 verified is installed"
1020 .to_string(),
1021 ),
1022 Ok(false) => source
1023 .as_ref()
1024 .map(|_| {
1025 "the installed organization policy is not a version-one artifact; it needs \
1026 a verifier"
1027 .to_string()
1028 })
1029 .or(trust_error),
1030 Err(error) => Some(error),
1031 };
1032 return OrgStatus {
1033 recorded,
1034 present: source.is_some(),
1036 source,
1037 resolve_error,
1038 pinned_anchors,
1039 ..Default::default()
1040 };
1041 };
1042 let verify = artifact.verify();
1043 let signer_key = artifact.signer_public_key.clone();
1044 let trusted = signer_key.as_deref().is_some_and(|signer| {
1048 anchors
1049 .iter()
1050 .any(|key| key.public_key == signer && key.org == artifact.org)
1051 });
1052 let resolve_result = artifact.resolved();
1053 let authority_error = match required {
1057 Ok(true) => Some(
1058 "an organization policy is required on this endpoint; the installed version-one \
1059 policy does not satisfy it"
1060 .to_string(),
1061 ),
1062 Ok(false) => trust_error,
1063 Err(error) => Some(error),
1064 };
1065 let applied = authority_error.is_none()
1066 && trusted
1067 && verify.signature_valid
1068 && artifact.enforced
1069 && resolve_result.is_ok();
1070
1071 OrgStatus {
1072 recorded,
1073 present: true,
1074 source,
1075 org: Some(artifact.org.clone()),
1076 policy_version: Some(artifact.policy_version.clone()),
1077 enforced: artifact.enforced,
1078 issued_at: Some(artifact.issued_at.clone()),
1079 signer_public_key: signer_key,
1080 signature_valid: verify.signature_valid,
1081 trusted,
1082 applied,
1083 floor_preview: resolve_result.as_ref().ok().cloned(),
1086 resolve_error: authority_error.or_else(|| resolve_result.err().map(|e| e.to_string())),
1087 pinned_anchors,
1088 }
1089}