use camino::Utf8PathBuf;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct CortexReceiptV1Candidate {
pub schema: String, pub version: u32,
pub lifecycle: String, pub target_cortex_surface: String,
pub authority_boundary: AuthorityBoundary,
pub cordance_execution_receipt_v1: ReceiptBody,
pub residual_gaps: Vec<String>,
}
impl CortexReceiptV1Candidate {
#[must_use]
pub const fn new(
schema: String,
version: u32,
lifecycle: String,
target_cortex_surface: String,
authority_boundary: AuthorityBoundary,
cordance_execution_receipt_v1: ReceiptBody,
residual_gaps: Vec<String>,
) -> Self {
Self {
schema,
version,
lifecycle,
target_cortex_surface,
authority_boundary,
cordance_execution_receipt_v1,
residual_gaps,
}
}
pub const fn validate_invariants(&self) -> Result<(), ReceiptInvariantError> {
let ab = &self.authority_boundary;
if !ab.candidate_only {
return Err(ReceiptInvariantError::CandidateOnlyMustBeTrue);
}
if ab.cortex_truth_allowed {
return Err(ReceiptInvariantError::AuthorityFlagSet(
"cortex_truth_allowed",
));
}
if ab.cortex_admission_allowed {
return Err(ReceiptInvariantError::AuthorityFlagSet(
"cortex_admission_allowed",
));
}
if ab.durable_promotion_allowed {
return Err(ReceiptInvariantError::AuthorityFlagSet(
"durable_promotion_allowed",
));
}
if ab.memory_promotion_allowed {
return Err(ReceiptInvariantError::AuthorityFlagSet(
"memory_promotion_allowed",
));
}
if ab.doctrine_promotion_allowed {
return Err(ReceiptInvariantError::AuthorityFlagSet(
"doctrine_promotion_allowed",
));
}
if ab.trusted_history_allowed {
return Err(ReceiptInvariantError::AuthorityFlagSet(
"trusted_history_allowed",
));
}
if ab.release_acceptance_allowed {
return Err(ReceiptInvariantError::AuthorityFlagSet(
"release_acceptance_allowed",
));
}
if ab.runtime_authority_allowed {
return Err(ReceiptInvariantError::AuthorityFlagSet(
"runtime_authority_allowed",
));
}
if self.cordance_execution_receipt_v1.forbidden_uses.is_empty() {
return Err(ReceiptInvariantError::ForbiddenUsesEmpty);
}
if self
.cordance_execution_receipt_v1
.allowed_claim_language
.is_empty()
{
return Err(ReceiptInvariantError::AllowedClaimLanguageEmpty);
}
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum ReceiptInvariantError {
#[error("authority_boundary.candidate_only must be true")]
CandidateOnlyMustBeTrue,
#[error("authority_boundary.{0} must be false")]
AuthorityFlagSet(&'static str),
#[error("cordance_execution_receipt_v1.forbidden_uses must be non-empty")]
ForbiddenUsesEmpty,
#[error("cordance_execution_receipt_v1.allowed_claim_language must be non-empty")]
AllowedClaimLanguageEmpty,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[allow(clippy::struct_excessive_bools)]
#[non_exhaustive]
pub struct AuthorityBoundary {
pub candidate_only: bool,
pub cortex_truth_allowed: bool,
pub cortex_admission_allowed: bool,
pub durable_promotion_allowed: bool,
pub memory_promotion_allowed: bool,
pub doctrine_promotion_allowed: bool,
pub trusted_history_allowed: bool,
pub release_acceptance_allowed: bool,
pub runtime_authority_allowed: bool,
}
impl AuthorityBoundary {
#[must_use]
pub const fn candidate_only() -> Self {
Self {
candidate_only: true,
cortex_truth_allowed: false,
cortex_admission_allowed: false,
durable_promotion_allowed: false,
memory_promotion_allowed: false,
doctrine_promotion_allowed: false,
trusted_history_allowed: false,
release_acceptance_allowed: false,
runtime_authority_allowed: false,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct ReceiptBody {
pub receipt_id: String,
pub generated_at: DateTime<Utc>,
pub execution_state: String, pub execution_trust_state: String, pub runtime_integrity_state: String, pub operator_approval_state: String, pub action_id: String,
pub source_context: SourceContext,
pub execution_trust: ExecutionTrust,
pub runtime_integrity: RuntimeIntegrity,
pub operator_approval: OperatorApproval,
pub allowed_claim_language: Vec<String>,
pub forbidden_uses: Vec<String>,
pub source_anchors: Vec<SourceAnchor>,
pub residual_risk: Vec<String>,
}
impl ReceiptBody {
#[must_use]
#[allow(clippy::too_many_arguments)]
pub const fn new(
receipt_id: String,
generated_at: DateTime<Utc>,
execution_state: String,
execution_trust_state: String,
runtime_integrity_state: String,
operator_approval_state: String,
action_id: String,
source_context: SourceContext,
execution_trust: ExecutionTrust,
runtime_integrity: RuntimeIntegrity,
operator_approval: OperatorApproval,
allowed_claim_language: Vec<String>,
forbidden_uses: Vec<String>,
source_anchors: Vec<SourceAnchor>,
residual_risk: Vec<String>,
) -> Self {
Self {
receipt_id,
generated_at,
execution_state,
execution_trust_state,
runtime_integrity_state,
operator_approval_state,
action_id,
source_context,
execution_trust,
runtime_integrity,
operator_approval,
allowed_claim_language,
forbidden_uses,
source_anchors,
residual_risk,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct SourceContext {
pub context_id: String,
pub truth_ceiling: TruthCeiling,
pub quarantine_state: String, }
impl SourceContext {
#[must_use]
pub const fn new(
context_id: String,
truth_ceiling: TruthCeiling,
quarantine_state: String,
) -> Self {
Self {
context_id,
truth_ceiling,
quarantine_state,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TruthCeiling {
Candidate,
Partial,
Advisory,
CandidateEvidenceOnly,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct ExecutionTrust {
pub repo_trust_result: String,
pub policy_decision: String, pub tool_provenance_state: String,
pub token_scope_state: String,
}
impl ExecutionTrust {
#[must_use]
pub const fn new(
repo_trust_result: String,
policy_decision: String,
tool_provenance_state: String,
token_scope_state: String,
) -> Self {
Self {
repo_trust_result,
policy_decision,
tool_provenance_state,
token_scope_state,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct RuntimeIntegrity {
pub runtime_write_attempted: bool,
pub runtime_write_authorized: bool,
pub runtime_root_projection: String,
}
impl RuntimeIntegrity {
#[must_use]
pub const fn new(
runtime_write_attempted: bool,
runtime_write_authorized: bool,
runtime_root_projection: String,
) -> Self {
Self {
runtime_write_attempted,
runtime_write_authorized,
runtime_root_projection,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct OperatorApproval {
pub operator_approval_required_for_promotion: bool,
pub operator_approval_ref: String,
pub operator_approval_hash: String,
}
impl OperatorApproval {
#[must_use]
pub const fn new(
operator_approval_required_for_promotion: bool,
operator_approval_ref: String,
operator_approval_hash: String,
) -> Self {
Self {
operator_approval_required_for_promotion,
operator_approval_ref,
operator_approval_hash,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct SourceAnchor {
pub source_id: String,
pub path: Utf8PathBuf,
pub sha256: String,
}
impl SourceAnchor {
#[must_use]
pub const fn new(source_id: String, path: Utf8PathBuf, sha256: String) -> Self {
Self {
source_id,
path,
sha256,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::schema;
fn valid_receipt() -> CortexReceiptV1Candidate {
CortexReceiptV1Candidate::new(
schema::CORDANCE_CORTEX_RECEIPT_V1_CANDIDATE.into(),
1,
"candidate_only".into(),
"cortex context-pack admit-cordance".into(),
AuthorityBoundary::candidate_only(),
ReceiptBody::new(
"cordance-candidate-2026-05-17".into(),
Utc::now(),
"candidate_only".into(),
"partial_structural_evidence".into(),
"repo_only_no_runtime_write".into(),
"not_bound_for_cortex_promotion".into(),
"cordance-pack-fixture".into(),
SourceContext::new(
"fixture".into(),
TruthCeiling::CandidateEvidenceOnly,
"not_cleared_by_boundary_crossing".into(),
),
ExecutionTrust::new(
"local_candidate_only".into(),
"deny_authority_grant".into(),
"not_field_level_bound_for_cortex".into(),
"not_field_level_bound_for_cortex".into(),
),
RuntimeIntegrity::new(false, false, "not_requested".into()),
OperatorApproval::new(
true,
"not_supplied_for_cortex_promotion".into(),
"not_supplied_for_cortex_promotion".into(),
),
vec![
"cordance candidate receipt for cortex context-pack admit-cordance".into(),
"candidate-only evidence".into(),
],
vec![
"claim Cortex truth".into(),
"claim Cortex admission".into(),
"promote Cortex memory".into(),
"promote Cortex doctrine".into(),
"create trusted history".into(),
"claim release acceptance".into(),
"authorize runtime writes".into(),
],
vec![],
vec!["Cortex may reject or quarantine this sample under its native parser.".into()],
),
vec![],
)
}
#[test]
fn candidate_only_constructor_never_grants_authority() {
let b = AuthorityBoundary::candidate_only();
assert!(b.candidate_only);
assert!(!b.cortex_truth_allowed);
assert!(!b.cortex_admission_allowed);
assert!(!b.durable_promotion_allowed);
assert!(!b.memory_promotion_allowed);
assert!(!b.doctrine_promotion_allowed);
assert!(!b.trusted_history_allowed);
assert!(!b.release_acceptance_allowed);
assert!(!b.runtime_authority_allowed);
}
#[test]
fn authority_boundary_candidate_only_is_canonical() {
let b = AuthorityBoundary::candidate_only();
assert!(b.candidate_only);
assert!(!b.cortex_truth_allowed);
}
#[test]
fn receipt_roundtrips_through_json() {
let r = valid_receipt();
let s = serde_json::to_string(&r).expect("ser");
let back: CortexReceiptV1Candidate = serde_json::from_str(&s).expect("de");
back.validate_invariants()
.expect("invariants hold after round-trip");
}
#[test]
fn validate_invariants_accepts_canonical_receipt() {
let r = valid_receipt();
r.validate_invariants()
.expect("canonical receipt must validate");
}
#[test]
fn tampered_receipt_fails_invariants() {
let r = valid_receipt();
let mut value = serde_json::to_value(&r).expect("to_value");
value["authority_boundary"]["cortex_truth_allowed"] = serde_json::Value::Bool(true);
let tampered: CortexReceiptV1Candidate =
serde_json::from_value(value).expect("tampered JSON still parses");
let err = tampered
.validate_invariants()
.expect_err("tampered receipt must fail validation");
match err {
ReceiptInvariantError::AuthorityFlagSet("cortex_truth_allowed") => {}
other => panic!("unexpected error: {other:?}"),
}
}
#[test]
fn every_authority_grant_flag_is_rejected() {
let flags = [
"cortex_truth_allowed",
"cortex_admission_allowed",
"durable_promotion_allowed",
"memory_promotion_allowed",
"doctrine_promotion_allowed",
"trusted_history_allowed",
"release_acceptance_allowed",
"runtime_authority_allowed",
];
for flag in flags {
let r = valid_receipt();
let mut value = serde_json::to_value(&r).expect("to_value");
value["authority_boundary"][flag] = serde_json::Value::Bool(true);
let tampered: CortexReceiptV1Candidate =
serde_json::from_value(value).expect("tampered JSON parses");
let err = tampered.validate_invariants().unwrap_err();
match err {
ReceiptInvariantError::AuthorityFlagSet(got) if got == flag => {}
other => panic!("flag {flag}: unexpected error {other:?}"),
}
}
}
#[test]
fn candidate_only_false_is_rejected() {
let r = valid_receipt();
let mut value = serde_json::to_value(&r).expect("to_value");
value["authority_boundary"]["candidate_only"] = serde_json::Value::Bool(false);
let tampered: CortexReceiptV1Candidate = serde_json::from_value(value).expect("parses");
let err = tampered.validate_invariants().unwrap_err();
assert!(matches!(
err,
ReceiptInvariantError::CandidateOnlyMustBeTrue
));
}
#[test]
fn extra_top_level_fields_rejected_by_serde() {
let r = valid_receipt();
let mut value = serde_json::to_value(&r).expect("to_value");
value.as_object_mut().expect("top-level object").insert(
"extra_authority_grant".into(),
serde_json::Value::Bool(true),
);
let result = serde_json::from_value::<CortexReceiptV1Candidate>(value);
assert!(
result.is_err(),
"extra top-level field must be rejected by deny_unknown_fields"
);
}
#[test]
fn extra_authority_boundary_field_rejected_by_serde() {
let r = valid_receipt();
let mut value = serde_json::to_value(&r).expect("to_value");
value["authority_boundary"]
.as_object_mut()
.expect("authority_boundary object")
.insert("cordance_god_mode".into(), serde_json::Value::Bool(true));
let result = serde_json::from_value::<CortexReceiptV1Candidate>(value);
assert!(
result.is_err(),
"extra authority_boundary field must be rejected by deny_unknown_fields"
);
}
#[test]
fn empty_forbidden_uses_rejected() {
let r = valid_receipt();
let mut value = serde_json::to_value(&r).expect("to_value");
value["cordance_execution_receipt_v1"]["forbidden_uses"] = serde_json::Value::Array(vec![]);
let parsed: CortexReceiptV1Candidate = serde_json::from_value(value).expect("parses");
let err = parsed.validate_invariants().unwrap_err();
assert!(matches!(err, ReceiptInvariantError::ForbiddenUsesEmpty));
}
#[test]
fn empty_allowed_claim_language_rejected() {
let r = valid_receipt();
let mut value = serde_json::to_value(&r).expect("to_value");
value["cordance_execution_receipt_v1"]["allowed_claim_language"] =
serde_json::Value::Array(vec![]);
let parsed: CortexReceiptV1Candidate = serde_json::from_value(value).expect("parses");
let err = parsed.validate_invariants().unwrap_err();
assert!(matches!(
err,
ReceiptInvariantError::AllowedClaimLanguageEmpty
));
}
}