use std::sync::Arc;
use exo_core::{Did, Hash256, PublicKey, Signature};
use exo_gatekeeper::{
ActionRequest, AdjudicationContext, InvariantSet, Kernel, Verdict,
authority_link_signature_message, provenance_signature_message,
types::{
AuthorityChain, AuthorityLink, BailmentState, ConsentRecord, GovernmentBranch, Permission,
PermissionSet, Provenance, Role, TrustedAuthorityKeys, TrustedProvenanceKeys,
},
};
use serde::{Deserialize, Serialize};
const DEFAULT_CONSTITUTION: &[u8] = b"EXOCHAIN Constitution v1.0: \
We the people of the EXOCHAIN fabric establish this constitution \
to secure the blessings of ordered, consented, and auditable agency.";
const INVARIANT_COUNT: usize = 8;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum KernelVerdict {
Permitted,
Denied {
violations: Vec<String>,
},
Escalated {
reason: String,
},
}
impl KernelVerdict {
#[must_use]
pub fn is_permitted(&self) -> bool {
matches!(self, Self::Permitted)
}
#[must_use]
pub fn is_denied(&self) -> bool {
matches!(self, Self::Denied { .. })
}
#[must_use]
pub fn is_escalated(&self) -> bool {
matches!(self, Self::Escalated { .. })
}
}
pub struct ConstitutionalKernel {
inner: Kernel,
constitution: Vec<u8>,
authority: Option<KernelAuthority>,
}
type AuthoritySigner = Arc<dyn Fn(&[u8]) -> Signature + Send + Sync>;
struct KernelAuthority {
did: Did,
public_key: PublicKey,
signer: AuthoritySigner,
}
impl ConstitutionalKernel {
#[must_use]
pub fn new() -> Self {
Self {
inner: Kernel::new(DEFAULT_CONSTITUTION, InvariantSet::all()),
constitution: DEFAULT_CONSTITUTION.to_vec(),
authority: None,
}
}
#[must_use]
pub fn with_authority_identity(authority: crate::identity::Identity) -> Self {
let authority_did = authority.did().clone();
let authority_public_key = *authority.public_key();
Self::with_authority(
authority_did,
authority_public_key,
Arc::new(move |message: &[u8]| authority.sign(message)),
)
}
#[must_use]
pub fn with_authority(
authority_did: Did,
authority_public_key: PublicKey,
authority_signer: AuthoritySigner,
) -> Self {
Self {
inner: Kernel::new(DEFAULT_CONSTITUTION, InvariantSet::all()),
constitution: DEFAULT_CONSTITUTION.to_vec(),
authority: Some(KernelAuthority {
did: authority_did,
public_key: authority_public_key,
signer: authority_signer,
}),
}
}
#[must_use]
pub fn adjudicate(&self, actor: &Did, action: &str) -> KernelVerdict {
self.adjudicate_internal(actor, action, false, false, true, true)
}
#[must_use]
pub fn adjudicate_self_grant(&self, actor: &Did, action: &str) -> KernelVerdict {
self.adjudicate_internal(actor, action, true, false, true, true)
}
#[must_use]
pub fn adjudicate_kernel_modification(&self, actor: &Did, action: &str) -> KernelVerdict {
self.adjudicate_internal(actor, action, false, true, true, true)
}
#[must_use]
pub fn adjudicate_without_bailment(&self, actor: &Did, action: &str) -> KernelVerdict {
self.adjudicate_internal(actor, action, false, false, false, true)
}
#[must_use]
pub fn verify_integrity(&self) -> bool {
self.inner.verify_kernel_integrity(&self.constitution)
}
#[must_use]
pub fn invariant_count(&self) -> usize {
INVARIANT_COUNT
}
fn signed_authority_link(
authority: &KernelAuthority,
grantee: &Did,
permissions: &PermissionSet,
) -> exo_core::Result<AuthorityLink> {
let mut link = AuthorityLink {
grantor: authority.did.clone(),
grantee: grantee.clone(),
permissions: permissions.clone(),
signature: Vec::new(),
grantor_public_key: Some(authority.public_key.as_bytes().to_vec()),
};
let message = authority_link_signature_message(&link)?;
let signature = (authority.signer)(message.as_bytes());
link.signature = signature.to_bytes().to_vec();
Ok(link)
}
fn signed_provenance(
authority: &KernelAuthority,
actor: &Did,
action: &str,
) -> exo_core::Result<Provenance> {
let timestamp = "sdk".to_owned();
let action_hash = Hash256::digest(action.as_bytes()).as_bytes().to_vec();
let mut provenance = Provenance {
actor: actor.clone(),
timestamp,
action_hash,
signature: Vec::new(),
public_key: Some(authority.public_key.as_bytes().to_vec()),
voice_kind: None,
independence: None,
review_order: None,
};
let message = provenance_signature_message(&provenance)?;
let signature = (authority.signer)(message.as_bytes());
provenance.signature = signature.to_bytes().to_vec();
Ok(provenance)
}
fn permission_scope(permissions: &PermissionSet) -> String {
let mut labels: Vec<&str> = permissions
.permissions
.iter()
.map(|permission| permission.0.as_str())
.collect();
labels.sort_unstable();
labels.dedup();
labels.join(";")
}
fn adjudicate_internal(
&self,
actor: &Did,
action: &str,
is_self_grant: bool,
modifies_kernel: bool,
include_bailment: bool,
human_override_preserved: bool,
) -> KernelVerdict {
let Some(authority) = self.authority.as_ref() else {
return KernelVerdict::Denied {
violations: vec![
"AuthorityChainValid: SDK authority signer is required for adjudication".into(),
],
};
};
let permissions = PermissionSet::new(vec![Permission::new("read")]);
let request = ActionRequest {
actor: actor.clone(),
action: action.to_owned(),
required_permissions: permissions.clone(),
is_self_grant,
modifies_kernel,
};
let scope = Self::permission_scope(&permissions);
let (bailment_state, consent_records) = if include_bailment {
(
BailmentState::Active {
bailor: authority.did.clone(),
bailee: actor.clone(),
scope: scope.clone(),
},
vec![ConsentRecord {
subject: authority.did.clone(),
granted_to: actor.clone(),
scope,
active: true,
}],
)
} else {
(BailmentState::None, Vec::new())
};
let authority_link = match Self::signed_authority_link(authority, actor, &permissions) {
Ok(link) => link,
Err(err) => {
return KernelVerdict::Denied {
violations: vec![format!(
"AuthorityChainValid: canonical authority signature payload failed: {err}"
)],
};
}
};
let provenance = match Self::signed_provenance(authority, actor, action) {
Ok(provenance) => provenance,
Err(err) => {
return KernelVerdict::Denied {
violations: vec![format!(
"ProvenanceVerifiable: canonical provenance signature payload failed: {err}"
)],
};
}
};
let authority_chain = AuthorityChain {
links: vec![authority_link],
};
let mut trusted_authority_keys = TrustedAuthorityKeys::default();
for link in &authority_chain.links {
if let Some(public_key) = &link.grantor_public_key {
trusted_authority_keys.insert(link.grantor.clone(), vec![public_key.clone()]);
}
}
let mut trusted_provenance_keys = TrustedProvenanceKeys::default();
trusted_provenance_keys.insert(
actor.clone(),
vec![authority.public_key.as_bytes().to_vec()],
);
let context = AdjudicationContext {
actor_roles: vec![Role {
name: "judge".into(),
branch: GovernmentBranch::Judicial,
}],
authority_chain,
consent_records,
bailment_state,
human_override_preserved,
actor_permissions: permissions,
trusted_authority_keys,
trusted_provenance_keys,
provenance: Some(provenance),
quorum_evidence: None,
active_challenge_reason: None,
};
match self.inner.adjudicate(&request, &context) {
Verdict::Permitted => KernelVerdict::Permitted,
Verdict::Denied { violations } => KernelVerdict::Denied {
violations: violations
.into_iter()
.map(|v| format!("{}: {}", v.invariant.id(), v.description))
.collect(),
},
Verdict::Escalated { reason } => KernelVerdict::Escalated { reason },
}
}
}
impl Default for ConstitutionalKernel {
fn default() -> Self {
Self::new()
}
}
impl core::fmt::Debug for ConstitutionalKernel {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ConstitutionalKernel")
.field("invariant_count", &INVARIANT_COUNT)
.finish()
}
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used)]
mod tests {
use super::*;
fn did(s: &str) -> Did {
Did::new(s).expect("valid DID")
}
fn signed_kernel() -> ConstitutionalKernel {
ConstitutionalKernel::with_authority_identity(crate::identity::Identity::generate(
"sdk-authority",
))
}
#[test]
fn new_initialises_with_eight_invariants() {
let k = ConstitutionalKernel::new();
assert_eq!(k.invariant_count(), 8);
}
#[test]
fn verify_integrity_holds_after_new() {
let k = ConstitutionalKernel::new();
assert!(k.verify_integrity());
}
#[test]
fn default_matches_new() {
let a = ConstitutionalKernel::default();
let b = ConstitutionalKernel::new();
assert_eq!(a.invariant_count(), b.invariant_count());
assert_eq!(a.verify_integrity(), b.verify_integrity());
}
#[test]
fn valid_action_permitted() {
let k = signed_kernel();
let actor = did("did:exo:valid-actor");
let verdict = k.adjudicate(&actor, "read-medical-record");
assert!(
verdict.is_permitted(),
"expected Permitted, got {verdict:?}"
);
}
#[test]
fn adjudicate_without_authority_signer_fails_closed() {
let k = ConstitutionalKernel::new();
let actor = did("did:exo:valid-actor");
let verdict = k.adjudicate(&actor, "read-medical-record");
assert!(verdict.is_denied(), "expected Denied, got {verdict:?}");
match verdict {
KernelVerdict::Denied { violations } => {
assert!(violations.iter().any(|v| v.contains("authority signer")));
}
other => panic!("expected Denied, got {other:?}"),
}
}
#[test]
fn self_grant_denied() {
let k = signed_kernel();
let actor = did("did:exo:self-granter");
let verdict = k.adjudicate_self_grant(&actor, "escalate-self");
assert!(verdict.is_denied(), "expected Denied, got {verdict:?}");
match verdict {
KernelVerdict::Denied { violations } => {
assert!(
violations
.iter()
.any(|violation| violation.starts_with("no-self-grant: ")),
"violations must use stable invariant IDs: {violations:?}"
);
}
other => panic!("expected Denied, got {other:?}"),
}
}
#[test]
fn kernel_modification_denied() {
let k = signed_kernel();
let actor = did("did:exo:patcher");
let verdict = k.adjudicate_kernel_modification(&actor, "patch-kernel");
assert!(verdict.is_denied(), "expected Denied, got {verdict:?}");
}
#[test]
fn no_bailment_denied() {
let k = signed_kernel();
let actor = did("did:exo:unauth");
let verdict = k.adjudicate_without_bailment(&actor, "read-data");
assert!(verdict.is_denied(), "expected Denied, got {verdict:?}");
}
#[test]
fn sdk_violation_labels_do_not_depend_on_debug_formatting() {
let source = include_str!("kernel.rs")
.split("// ===========================================================================\n// Tests")
.next()
.expect("production section");
assert!(
!source.contains("format!(\"{:?}: {}\", v.invariant, v.description)"),
"SDK violation labels must use stable invariant IDs"
);
}
#[test]
fn verdict_helpers() {
assert!(KernelVerdict::Permitted.is_permitted());
assert!(!KernelVerdict::Permitted.is_denied());
let denied = KernelVerdict::Denied { violations: vec![] };
assert!(denied.is_denied());
assert!(!denied.is_permitted());
let esc = KernelVerdict::Escalated { reason: "r".into() };
assert!(esc.is_escalated());
assert!(!esc.is_permitted());
}
#[test]
fn verdict_serde_roundtrip() {
let v = KernelVerdict::Denied {
violations: vec!["NoSelfGrant: reason".into()],
};
let json = serde_json::to_string(&v).expect("ser");
let decoded: KernelVerdict = serde_json::from_str(&json).expect("de");
assert_eq!(v, decoded);
}
#[test]
fn debug_impl_smoke() {
let k = ConstitutionalKernel::new();
let dbg = format!("{k:?}");
assert!(dbg.contains("ConstitutionalKernel"));
assert!(dbg.contains("8"));
}
}