Skip to main content

cortex_core/
boundary.rs

1//! Typed Cortex / pai-axiom boundary envelopes.
2//!
3//! ADR 0040 requires cross-system exchanges to carry machine-readable
4//! constraints and receipt state. These structs are shape-only: they do not
5//! grant authority, persist records, or bypass the receiver's own gates.
6
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9
10use crate::{
11    AxiomConstraint, ClaimCeiling, ContextPackId, ProofClosureReport, ProvenanceClass,
12    SemanticTrustClass,
13};
14
15/// Current boundary envelope schema version.
16pub const BOUNDARY_SCHEMA_VERSION: u16 = 1;
17
18/// Stable type string for Cortex -> AXIOM constraint envelopes.
19pub const CORTEX_TO_AXIOM_CONSTRAINT_ENVELOPE_V1: &str = "cortex.boundary.constraint_envelope.v1";
20
21/// Stable type string for pai-axiom -> Cortex execution receipts.
22pub const PAI_AXIOM_TO_CORTEX_EXECUTION_RECEIPT_V1: &str =
23    "pai_axiom.boundary.execution_receipt.v1";
24
25/// Contradiction state crossing the Cortex / pai-axiom boundary.
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
27#[serde(rename_all = "snake_case")]
28pub enum BoundaryContradictionState {
29    /// Contradictions were scanned and resolved for this use.
30    Resolved,
31    /// Multiple hypotheses remain intentionally represented.
32    MultiHypothesis,
33    /// Contradiction state is missing or not scanned.
34    Unknown,
35    /// An unresolved contradiction blocks authority-bearing use.
36    Blocked,
37}
38
39/// Quarantine state crossing the Cortex / pai-axiom boundary.
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
41#[serde(rename_all = "snake_case")]
42pub enum BoundaryQuarantineState {
43    /// No quarantine or contamination is known.
44    Clean,
45    /// Material may be shown only for diagnostics.
46    DiagnosticOnly,
47    /// Material is quarantined and must not guide authority-bearing work.
48    Quarantined,
49    /// Material is contaminated by a failed or unsafe path.
50    Contaminated,
51}
52
53/// Redaction state crossing the Cortex / pai-axiom boundary.
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
55#[serde(rename_all = "snake_case")]
56pub enum BoundaryRedactionState {
57    /// Raw operator-mode material is present under explicit opt-in.
58    RawOperatorOptIn,
59    /// Content has been redacted.
60    Redacted,
61    /// Content has been abstracted.
62    Abstracted,
63    /// Content is safe for the declared export posture.
64    ExportSafe,
65}
66
67/// Claim language permitted to the receiving AXIOM runtime.
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
69#[serde(rename_all = "snake_case")]
70pub enum AllowedClaimLanguage {
71    /// Candidate claim only.
72    CandidateClaim,
73    /// Evidence reference only.
74    EvidenceReference,
75    /// Constraint or limit.
76    Constraint,
77    /// Residual risk.
78    ResidualRisk,
79    /// Request verification instead of asserting.
80    VerificationRequest,
81    /// Refusal or abstention.
82    Refusal,
83}
84
85/// Authority-bearing use forbidden by a boundary envelope.
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
87#[serde(rename_all = "snake_case")]
88pub enum ForbiddenBoundaryUse {
89    /// Principle or doctrine promotion.
90    Promotion,
91    /// Trusted run-history claim.
92    TrustedHistory,
93    /// Compliance evidence.
94    ComplianceEvidence,
95    /// Export as trusted material.
96    Export,
97    /// Release gate or release evidence.
98    Release,
99    /// External reporting.
100    ExternalReporting,
101    /// Execution authority or tool permission.
102    ExecutionAuthority,
103}
104
105/// Cortex -> pai-axiom constraint envelope.
106#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
107pub struct CortexAxiomConstraintEnvelopeV1 {
108    /// Schema version.
109    pub schema_version: u16,
110    /// Stable envelope type string.
111    pub envelope_type: String,
112    /// Context pack being constrained.
113    pub context_pack_id: ContextPackId,
114    /// Proof closure state and failing edges for the pack/use.
115    pub proof_state: ProofClosureReport,
116    /// Maximum claim strength permitted.
117    pub truth_ceiling: ClaimCeiling,
118    /// Semantic trust class for the selected context.
119    pub semantic_trust: SemanticTrustClass,
120    /// Weakest provenance class represented in selected context.
121    pub provenance_class: ProvenanceClass,
122    /// Contradiction posture.
123    pub contradiction_state: BoundaryContradictionState,
124    /// Quarantine posture.
125    pub quarantine_state: BoundaryQuarantineState,
126    /// Redaction posture.
127    pub redaction_state: BoundaryRedactionState,
128    /// Claim language the receiver may use.
129    pub allowed_claim_language: Vec<AllowedClaimLanguage>,
130    /// Authority-bearing uses forbidden by this envelope.
131    pub forbidden_uses: Vec<ForbiddenBoundaryUse>,
132    /// Detailed AXIOM constraints already understood by existing exports.
133    pub constraints: Vec<AxiomConstraint>,
134}
135
136impl CortexAxiomConstraintEnvelopeV1 {
137    /// Construct an envelope with the stable v1 type string.
138    #[must_use]
139    pub fn new(
140        context_pack_id: ContextPackId,
141        proof_state: ProofClosureReport,
142        truth_ceiling: ClaimCeiling,
143        semantic_trust: SemanticTrustClass,
144        provenance_class: ProvenanceClass,
145    ) -> Self {
146        Self {
147            schema_version: BOUNDARY_SCHEMA_VERSION,
148            envelope_type: CORTEX_TO_AXIOM_CONSTRAINT_ENVELOPE_V1.to_string(),
149            context_pack_id,
150            proof_state,
151            truth_ceiling,
152            semantic_trust,
153            provenance_class,
154            contradiction_state: BoundaryContradictionState::Unknown,
155            quarantine_state: BoundaryQuarantineState::DiagnosticOnly,
156            redaction_state: BoundaryRedactionState::Abstracted,
157            allowed_claim_language: default_allowed_claim_language(),
158            forbidden_uses: default_forbidden_boundary_uses(),
159            constraints: Vec::new(),
160        }
161    }
162}
163
164/// Capability-token decision from pai-axiom.
165#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
166#[serde(rename_all = "snake_case")]
167pub enum CapabilityTokenDecision {
168    /// Token and policy allowed the operation.
169    Allowed,
170    /// Token allowed with warning.
171    Warned,
172    /// Token or policy rejected the operation.
173    Rejected,
174    /// Token was expired.
175    Expired,
176    /// Token was revoked.
177    Revoked,
178}
179
180/// Capability-token state supplied by pai-axiom.
181#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
182pub struct CapabilityTokenState {
183    /// Final token decision.
184    pub decision: CapabilityTokenDecision,
185    /// Token structure was valid.
186    pub valid_structure: bool,
187    /// Token audience matched Cortex.
188    pub audience_bound: bool,
189    /// Token scope matched the operation.
190    pub scope_bound: bool,
191    /// Token operation binding matched the receipt.
192    pub operation_bound: bool,
193    /// Token was not expired at execution time.
194    pub not_expired: bool,
195    /// Token was not revoked at execution time.
196    pub not_revoked: bool,
197    /// Policy allowed the operation.
198    pub policy_allowed: bool,
199    /// Attestation link was present and verified.
200    pub attestation_linked: bool,
201}
202
203/// Runtime integrity state supplied by pai-axiom.
204#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
205#[serde(rename_all = "snake_case")]
206pub enum RuntimeIntegrityState {
207    /// Runtime integrity was not verified.
208    Unverified,
209    /// Release artifact was verified.
210    VerifiedRelease,
211    /// Release provenance was verified.
212    VerifiedProvenance,
213    /// Runtime is known or suspected compromised.
214    Compromised,
215}
216
217/// Execution trust state supplied by pai-axiom.
218#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
219pub struct ExecutionTrustState {
220    /// Runtime integrity state.
221    pub runtime_integrity: RuntimeIntegrityState,
222    /// Optional release or provenance evidence ref.
223    pub evidence_ref: Option<String>,
224}
225
226/// Operator approval state supplied by pai-axiom.
227#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
228#[serde(rename_all = "snake_case")]
229pub enum OperatorApprovalState {
230    /// Approval was not required.
231    NotRequired,
232    /// Approval was required but absent.
233    RequiredMissing,
234    /// Approval was supplied and operation-bound.
235    ApprovedBound,
236    /// Approval was supplied but not operation-bound.
237    ApprovedUnbound,
238}
239
240/// Tool invocation outcome in a boundary receipt.
241#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
242#[serde(rename_all = "snake_case")]
243pub enum BoundaryToolOutcome {
244    /// Tool invocation succeeded.
245    Succeeded,
246    /// Tool invocation failed.
247    Failed,
248    /// Tool invocation was blocked.
249    Blocked,
250}
251
252/// Tool provenance item in a boundary receipt.
253#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
254pub struct BoundaryToolInvocation {
255    /// Tool name.
256    pub tool_name: String,
257    /// Invocation identifier.
258    pub invocation_id: String,
259    /// Stable input anchor.
260    pub input_ref: Option<String>,
261    /// Stable output anchor.
262    pub output_ref: Option<String>,
263    /// Invocation outcome.
264    pub outcome: BoundaryToolOutcome,
265}
266
267/// Source anchor in a boundary receipt.
268#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
269pub struct BoundarySourceAnchor {
270    /// Source reference.
271    pub reference: String,
272    /// Source kind.
273    pub kind: String,
274}
275
276/// pai-axiom -> Cortex execution receipt.
277#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
278pub struct PaiAxiomExecutionReceiptV1 {
279    /// Schema version.
280    pub schema_version: u16,
281    /// Stable receipt type string.
282    pub receipt_type: String,
283    /// Runtime/workloop identity.
284    pub runtime_id: String,
285    /// Capability-token state.
286    pub capability_token_state: CapabilityTokenState,
287    /// Execution trust state.
288    pub execution_trust_state: ExecutionTrustState,
289    /// Tool invocations and anchors.
290    pub tool_provenance: Vec<BoundaryToolInvocation>,
291    /// Operator approval state.
292    pub operator_approval_state: OperatorApprovalState,
293    /// Runtime quarantine state.
294    pub quarantine_state: BoundaryQuarantineState,
295    /// Source anchors for claims proposed to Cortex.
296    pub source_anchors: Vec<BoundarySourceAnchor>,
297    /// Explicit residual risk strings.
298    pub residual_risk: Vec<String>,
299    /// Must remain true for Cortex admission: receipt is not promotion.
300    pub explicit_non_promotion: bool,
301}
302
303impl PaiAxiomExecutionReceiptV1 {
304    /// Construct a v1 execution receipt.
305    #[must_use]
306    pub fn new(
307        runtime_id: impl Into<String>,
308        capability_token_state: CapabilityTokenState,
309        execution_trust_state: ExecutionTrustState,
310        operator_approval_state: OperatorApprovalState,
311    ) -> Self {
312        Self {
313            schema_version: BOUNDARY_SCHEMA_VERSION,
314            receipt_type: PAI_AXIOM_TO_CORTEX_EXECUTION_RECEIPT_V1.to_string(),
315            runtime_id: runtime_id.into(),
316            capability_token_state,
317            execution_trust_state,
318            tool_provenance: Vec::new(),
319            operator_approval_state,
320            quarantine_state: BoundaryQuarantineState::DiagnosticOnly,
321            source_anchors: Vec::new(),
322            residual_risk: Vec::new(),
323            explicit_non_promotion: true,
324        }
325    }
326}
327
328/// Default claim language for constrained AXIOM work.
329#[must_use]
330pub fn default_allowed_claim_language() -> Vec<AllowedClaimLanguage> {
331    vec![
332        AllowedClaimLanguage::CandidateClaim,
333        AllowedClaimLanguage::EvidenceReference,
334        AllowedClaimLanguage::Constraint,
335        AllowedClaimLanguage::ResidualRisk,
336        AllowedClaimLanguage::VerificationRequest,
337        AllowedClaimLanguage::Refusal,
338    ]
339}
340
341/// Default forbidden authority-bearing uses for cross-boundary context.
342#[must_use]
343pub fn default_forbidden_boundary_uses() -> Vec<ForbiddenBoundaryUse> {
344    vec![
345        ForbiddenBoundaryUse::Promotion,
346        ForbiddenBoundaryUse::TrustedHistory,
347        ForbiddenBoundaryUse::ComplianceEvidence,
348        ForbiddenBoundaryUse::Export,
349        ForbiddenBoundaryUse::Release,
350        ForbiddenBoundaryUse::ExternalReporting,
351        ForbiddenBoundaryUse::ExecutionAuthority,
352    ]
353}
354
355#[cfg(test)]
356mod tests {
357    use serde_json::json;
358
359    use super::*;
360
361    fn token_state(decision: CapabilityTokenDecision) -> CapabilityTokenState {
362        CapabilityTokenState {
363            decision,
364            valid_structure: true,
365            audience_bound: true,
366            scope_bound: true,
367            operation_bound: true,
368            not_expired: true,
369            not_revoked: true,
370            policy_allowed: true,
371            attestation_linked: true,
372        }
373    }
374
375    #[test]
376    fn boundary_type_strings_are_stable() {
377        assert_eq!(
378            CORTEX_TO_AXIOM_CONSTRAINT_ENVELOPE_V1,
379            "cortex.boundary.constraint_envelope.v1"
380        );
381        assert_eq!(
382            PAI_AXIOM_TO_CORTEX_EXECUTION_RECEIPT_V1,
383            "pai_axiom.boundary.execution_receipt.v1"
384        );
385    }
386
387    #[test]
388    fn boundary_enum_wire_strings_are_stable() {
389        assert_eq!(
390            serde_json::to_value(BoundaryContradictionState::MultiHypothesis).unwrap(),
391            json!("multi_hypothesis")
392        );
393        assert_eq!(
394            serde_json::to_value(BoundaryQuarantineState::DiagnosticOnly).unwrap(),
395            json!("diagnostic_only")
396        );
397        assert_eq!(
398            serde_json::to_value(BoundaryRedactionState::RawOperatorOptIn).unwrap(),
399            json!("raw_operator_opt_in")
400        );
401        assert_eq!(
402            serde_json::to_value(AllowedClaimLanguage::VerificationRequest).unwrap(),
403            json!("verification_request")
404        );
405        assert_eq!(
406            serde_json::to_value(ForbiddenBoundaryUse::TrustedHistory).unwrap(),
407            json!("trusted_history")
408        );
409        assert_eq!(
410            serde_json::to_value(CapabilityTokenDecision::Revoked).unwrap(),
411            json!("revoked")
412        );
413        assert_eq!(
414            serde_json::to_value(RuntimeIntegrityState::VerifiedProvenance).unwrap(),
415            json!("verified_provenance")
416        );
417        assert_eq!(
418            serde_json::to_value(OperatorApprovalState::ApprovedBound).unwrap(),
419            json!("approved_bound")
420        );
421        assert_eq!(
422            serde_json::to_value(BoundaryToolOutcome::Blocked).unwrap(),
423            json!("blocked")
424        );
425    }
426
427    #[test]
428    fn constraint_envelope_defaults_forbid_authority_bearing_uses() {
429        let envelope = CortexAxiomConstraintEnvelopeV1::new(
430            ContextPackId::new(),
431            ProofClosureReport::full_chain_verified(Vec::new()),
432            ClaimCeiling::LocalUnsigned,
433            SemanticTrustClass::CandidateOnly,
434            ProvenanceClass::RuntimeDerived,
435        );
436
437        assert_eq!(envelope.schema_version, BOUNDARY_SCHEMA_VERSION);
438        assert_eq!(
439            envelope.envelope_type,
440            CORTEX_TO_AXIOM_CONSTRAINT_ENVELOPE_V1
441        );
442        assert!(envelope
443            .forbidden_uses
444            .contains(&ForbiddenBoundaryUse::ExecutionAuthority));
445        assert!(envelope
446            .allowed_claim_language
447            .contains(&AllowedClaimLanguage::Refusal));
448    }
449
450    #[test]
451    fn execution_receipt_defaults_to_candidate_only_non_promotion() {
452        let receipt = PaiAxiomExecutionReceiptV1::new(
453            "axiom-runtime:v3.1",
454            token_state(CapabilityTokenDecision::Allowed),
455            ExecutionTrustState {
456                runtime_integrity: RuntimeIntegrityState::Unverified,
457                evidence_ref: None,
458            },
459            OperatorApprovalState::NotRequired,
460        );
461
462        assert_eq!(receipt.schema_version, BOUNDARY_SCHEMA_VERSION);
463        assert_eq!(
464            receipt.receipt_type,
465            PAI_AXIOM_TO_CORTEX_EXECUTION_RECEIPT_V1
466        );
467        assert!(receipt.explicit_non_promotion);
468        assert_eq!(
469            receipt.quarantine_state,
470            BoundaryQuarantineState::DiagnosticOnly
471        );
472    }
473}