Bind public Eyes evidence to exact source authority

This commit is contained in:
Meta Vens 2026-08-11 22:07:02 +02:00
commit 8fb61ac236
18 changed files with 1037 additions and 35 deletions

1
Cargo.lock generated
View file

@ -2293,6 +2293,7 @@ dependencies = [
"rmcp 2.2.0",
"serde",
"serde_json",
"sha2 0.10.9",
"tempfile",
"tokio",
"toml",

View file

@ -201,14 +201,48 @@ pub fn accept_coordinator_role_finding(
let mut available = vec![MIND_GATEWAY_REVIEW_TYPE.to_string()];
let mut prerequisites = Vec::new();
if role_id == EpiphanyRoleResultRoleId::Research {
let public_source_lookups = runtime_authenticated_public_source_lookups_for_worker(
store,
finding.runtime_job_id.as_deref().unwrap_or_default(),
)?;
for lookup in &public_source_lookups {
if !finding
.evidence_ids
.iter()
.any(|evidence_id| evidence_id == &lookup.receipt_id)
|| !finding
.files_inspected
.iter()
.any(|source_ref| source_ref == &lookup.source_ref)
{
return Err(anyhow::anyhow!(
"Research finding did not cite its authenticated public source receipt and source ref"
));
}
}
if finding
.evidence_ids
.iter()
.any(|evidence_id| evidence_id.starts_with("eyes-source-"))
&& public_source_lookups.is_empty()
{
return Err(anyhow::anyhow!(
"Research finding cited public source evidence without an authenticated lookup"
));
}
let packet = eyes_evidence_packet_from_research_finding(
format!("eyes-packet-{}", update.accepted_receipt_id),
&finding,
&update.applied_patch,
&public_source_lookups,
accepted_at.clone(),
);
available.push(EYES_EVIDENCE_PACKET_TYPE.to_string());
prerequisites.push(EpiphanyAcceptancePrerequisite::Eyes(packet));
for lookup in public_source_lookups {
available.push(EYES_SOURCE_LOOKUP_RECEIPT_TYPE.to_string());
prerequisites.push(EpiphanyAcceptancePrerequisite::EyesSourceLookup(lookup));
}
let grant_id = format!(
"substrate-grant-{}",
finding.runtime_job_id.as_deref().unwrap_or_default()
@ -609,6 +643,7 @@ pub fn read_accepted_coordinator_state(
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EpiphanyAcceptancePrerequisite {
Eyes(EyesEvidencePacket),
EyesSourceLookup(EyesSourceLookupReceipt),
SubstrateGate(SubstrateGateRepoAccessGrantReceipt),
Soul(SoulVerdictReceipt),
Continuity(ContinuityRecoveryReceipt),
@ -641,6 +676,19 @@ fn commit_state_with_mind_witness(
store_path,
expected_state,
)?;
for prerequisite in prerequisites {
if let EpiphanyAcceptancePrerequisite::EyesSourceLookup(document) = prerequisite {
let authenticated = crate::runtime_spine::authenticated_public_source_lookups_for_worker(
&cache,
&document.source_job_id,
)?;
if !authenticated.iter().any(|candidate| candidate == document) {
return Err(anyhow::anyhow!(
"Eyes source lookup lost its authenticated authority before Mind commit"
));
}
}
}
let (review_envelope, _) = cache.prepare_entry(&mind_review.gateway_id, mind_review)?;
let (commit_envelope, _) = cache.prepare_entry(&commit_receipt.receipt_id, commit_receipt)?;
let mut batch = vec![review_envelope, commit_envelope];
@ -649,6 +697,9 @@ fn commit_state_with_mind_witness(
EpiphanyAcceptancePrerequisite::Eyes(document) => {
cache.prepare_entry(&document.packet_id, document)?.0
}
EpiphanyAcceptancePrerequisite::EyesSourceLookup(document) => {
cache.prepare_entry(&document.receipt_id, document)?.0
}
EpiphanyAcceptancePrerequisite::SubstrateGate(document) => {
cache.prepare_entry(&document.receipt_id, document)?.0
}
@ -1067,6 +1118,7 @@ mod tests {
evidence_ids: Vec::new(),
observation_ids: Vec::new(),
source_refs: Vec::new(),
source_lookup_receipt_ids: Vec::new(),
summary: "looked".to_string(),
uncertainty: "none".to_string(),
emitted_at: "2026-07-11T00:00:00Z".to_string(),

View file

@ -532,7 +532,12 @@ fn commit_coordinator_job_launch_in_cache(
}
batch.push(cache.prepare_entry(&binding.binding_record_id, &binding)?.0);
}
if request.binding_id == EPIPHANY_RESEARCH_ROLE_BINDING_ID {
if matches!(
request.binding_id.as_str(),
EPIPHANY_RESEARCH_ROLE_BINDING_ID
| crate::EPIPHANY_MODELING_ROLE_BINDING_ID
| crate::EPIPHANY_VERIFICATION_ROLE_BINDING_ID
) {
let grant = substrate_gate_repo_access_grant_for_launch(
format!("substrate-grant-{}", plan.backend_job_id),
plan.backend_job_id.clone(),
@ -3887,11 +3892,14 @@ pub(crate) mod tests {
.get_all::<crate::RepoFrontierHandsAuthority>()?
.is_empty()
);
assert!(
cache
.get_all::<crate::SubstrateGateRepoAccessGrantReceipt>()?
.is_empty()
);
let grants = cache.get_all::<crate::SubstrateGateRepoAccessGrantReceipt>()?;
assert_eq!(grants.len(), 1);
assert_eq!(grants[0].binding_id, crate::EPIPHANY_MODELING_ROLE_BINDING_ID);
assert_eq!(grants[0].granted_operations, vec!["read", "snapshot"]);
assert!(!grants[0]
.granted_operations
.iter()
.any(|operation| operation == "publicSourceRead"));
Ok(())
}

View file

@ -17,6 +17,44 @@ pub const EYES_EVIDENCE_PACKET_SCHEMA_VERSION: &str = "epiphany.eyes.evidence_pa
pub const EYES_EVIDENCE_REFUSAL_RECEIPT_SCHEMA_VERSION: &str =
"epiphany.eyes.evidence_refusal_receipt.v0";
#[derive(Debug, Clone, PartialEq, Eq, DatabaseEntry)]
#[cultcache(
type = "epiphany.eyes.source_lookup_receipt",
schema = "EyesSourceLookupReceipt"
)]
pub struct EyesSourceLookupReceipt {
#[cultcache(key = 0)]
pub schema_version: String,
#[cultcache(key = 1)]
pub receipt_id: String,
#[cultcache(key = 2)]
pub source_job_id: String,
#[cultcache(key = 3)]
pub substrate_grant_receipt_id: String,
#[cultcache(key = 4)]
pub tool_intent_id: String,
#[cultcache(key = 5)]
pub tool_receipt_id: String,
#[cultcache(key = 6)]
pub provider: String,
#[cultcache(key = 7)]
pub repository: String,
#[cultcache(key = 8)]
pub revision: String,
#[cultcache(key = 9)]
pub path: String,
#[cultcache(key = 10)]
pub source_ref: String,
#[cultcache(key = 11)]
pub content_sha256: String,
#[cultcache(key = 12)]
pub byte_count: u64,
#[cultcache(key = 13)]
pub observed_at: String,
#[cultcache(key = 14)]
pub contract: String,
}
#[derive(Debug, Clone, PartialEq, Eq, DatabaseEntry)]
#[cultcache(type = "epiphany.eyes.evidence_packet", schema = "EyesEvidencePacket")]
pub struct EyesEvidencePacket {
@ -44,6 +82,8 @@ pub struct EyesEvidencePacket {
pub emitted_at: String,
#[cultcache(key = 11)]
pub contract: String,
#[cultcache(key = 12, default)]
pub source_lookup_receipt_ids: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -131,6 +171,7 @@ pub fn eyes_evidence_packet_from_research_finding(
packet_id: String,
finding: &EpiphanyRoleFindingInterpretation,
patch: &EpiphanyRoleStatePatchDocument,
source_lookups: &[EyesSourceLookupReceipt],
emitted_at: String,
) -> EyesEvidencePacket {
let evidence_ids = patch
@ -164,6 +205,9 @@ pub fn eyes_evidence_packet_from_research_finding(
push_unique(&mut source_refs, rendered);
}
}
for lookup in source_lookups {
push_unique(&mut source_refs, lookup.source_ref.clone());
}
EyesEvidencePacket {
schema_version: EYES_EVIDENCE_PACKET_SCHEMA_VERSION.to_string(),
packet_id,
@ -187,6 +231,10 @@ pub fn eyes_evidence_packet_from_research_finding(
},
emitted_at,
contract: "Eyes packet emitted from a reviewed Research lane finding; it makes the source-gathering evidence claim citable before Mind admission.".to_string(),
source_lookup_receipt_ids: source_lookups
.iter()
.map(|lookup| lookup.receipt_id.clone())
.collect(),
}
}
@ -286,6 +334,7 @@ mod tests {
"eyes-packet-1".to_string(),
&finding,
&patch,
&[],
"2026-05-30T00:00:00Z".to_string(),
);
assert_eq!(packet.source_role_id, "research");

View file

@ -618,6 +618,7 @@ pub use eyes_gateway::EYES_SOURCE_LOOKUP_RECEIPT_SCHEMA_VERSION;
pub use eyes_gateway::EYES_SOURCE_LOOKUP_RECEIPT_TYPE;
pub use eyes_gateway::EyesCultNetContract;
pub use eyes_gateway::EyesEvidencePacket;
pub use eyes_gateway::EyesSourceLookupReceipt;
pub use eyes_gateway::default_eyes_cultnet_contracts;
pub use eyes_gateway::eyes_evidence_packet_from_research_finding;
pub use hands_gateway::*;
@ -1111,6 +1112,7 @@ pub use runtime_spine::put_runtime_reorient_worker_result;
pub use runtime_spine::put_runtime_role_worker_result;
pub use runtime_spine::put_runtime_tool_execution_intent;
pub use runtime_spine::put_runtime_tool_execution_receipt;
pub use runtime_spine::runtime_authenticated_public_source_lookups_for_worker;
#[cfg(test)]
pub(crate) use runtime_spine::put_soul_verdict_receipt;
pub use runtime_spine::put_substrate_gate_repo_access_grant_receipt;
@ -1213,6 +1215,7 @@ pub use substrate_gate::SubstrateGateRepoAccessGrantReceipt;
pub use substrate_gate::default_substrate_gate_cultnet_contracts;
pub use substrate_gate::substrate_gate_coordinator_implementation_grant;
pub use substrate_gate::substrate_gate_repo_access_grant_for_launch;
pub use substrate_gate::substrate_gate_operation_for_governed_tool;
pub use substrate_gate::substrate_gate_repo_work_planning_grant;
pub use surfaces::AdmittedModelDirectionConsiderationContextProjection;
pub use surfaces::EpiphanyAcceptanceBundle;

View file

@ -1195,7 +1195,8 @@ mod tests {
"heartbeat.scheduler",
"coordinator.run",
"persona.public_speech",
"daemon.tool_invocation"
"daemon.tool_invocation",
"daemon.lifecycle_poke"
]
);
assert!(

View file

@ -28,6 +28,7 @@ use crate::eyes_gateway::EYES_EVIDENCE_REVIEW_TYPE;
use crate::eyes_gateway::EYES_SOURCE_LOOKUP_RECEIPT_SCHEMA_VERSION;
use crate::eyes_gateway::EYES_SOURCE_LOOKUP_RECEIPT_TYPE;
use crate::eyes_gateway::EyesEvidencePacket;
use crate::eyes_gateway::EyesSourceLookupReceipt;
use crate::hands_gateway::*;
use crate::heartbeat_state::HEARTBEAT_STATE_SCHEMA_VERSION;
use crate::heartbeat_state::HEARTBEAT_STATE_TYPE;
@ -358,6 +359,8 @@ pub struct EpiphanyRuntimeModelExecutionBinding {
pub provider: String,
#[cultcache(key = 6)]
pub bound_at: String,
#[cultcache(key = 7, default)]
pub source_worker_job_id: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)]
@ -1154,6 +1157,7 @@ pub fn runtime_spine_cache(store_path: impl AsRef<Path>) -> Result<CultCache> {
cache.register_entry_type::<MindGatewayReview>()?;
cache.register_entry_type::<MindStateCommitReceipt>()?;
cache.register_entry_type::<EyesEvidencePacket>()?;
cache.register_entry_type::<EyesSourceLookupReceipt>()?;
cache.register_entry_type::<SubstrateGateRepoAccessGrantReceipt>()?;
cache.register_entry_type::<HandsActionIntent>()?;
cache.register_entry_type::<HandsActionReview>()?;
@ -1675,6 +1679,47 @@ pub fn open_runtime_model_execution(
require_runtime_identity_not_archived(&cache, "session", &session_options.session_id)?;
require_runtime_identity_not_archived(&cache, "job", &job_options.job_id)?;
require_runtime_identity_not_archived(&cache, "model-request", &model_request.request_id)?;
let source_worker_envelopes = if let Some(worker_job_id) =
model_request.source_worker_job_id.as_deref()
{
let launch = cache
.get::<EpiphanyRuntimeWorkerLaunchRequest>(worker_job_id)?
.ok_or_else(|| {
anyhow!(
"model execution source worker {worker_job_id:?} has no launch request"
)
})?;
if launch.job_id != worker_job_id {
return Err(anyhow!(
"model execution source worker launch identity disagrees"
));
}
let worker_job = cache
.get::<EpiphanyRuntimeJob>(worker_job_id)?
.ok_or_else(|| anyhow!("model execution source worker has no runtime job"))?;
if worker_job.role != launch.role
|| matches!(
worker_job.status,
EpiphanyRuntimeJobStatus::Completed
| EpiphanyRuntimeJobStatus::Failed
| EpiphanyRuntimeJobStatus::Cancelled
)
{
return Err(anyhow!(
"model execution source worker job is foreign or terminal"
));
}
vec![
cache
.get_envelope::<EpiphanyRuntimeJob>(worker_job_id)?
.ok_or_else(|| anyhow!("model execution lost source worker job envelope"))?,
cache
.get_envelope::<EpiphanyRuntimeWorkerLaunchRequest>(worker_job_id)?
.ok_or_else(|| anyhow!("model execution lost source worker launch envelope"))?,
]
} else {
Vec::new()
};
let existing_session = cache.get::<EpiphanyRuntimeSession>(&session_options.session_id)?;
let session = existing_session
.clone()
@ -1763,11 +1808,13 @@ pub fn open_runtime_model_execution(
job_id: job.job_id.clone(),
provider: model_request.provider.clone(),
bound_at: bound_at.to_string(),
source_worker_job_id: model_request.source_worker_job_id.clone(),
};
let identity_envelope = cache
.get_envelope::<EpiphanyRuntimeIdentity>(RUNTIME_IDENTITY_KEY)?
.ok_or_else(|| anyhow!("model execution lost its exact runtime identity envelope"))?;
let mut expected = vec![identity_envelope.clone()];
expected.extend(source_worker_envelopes.iter().cloned());
if existing_session.is_some() {
expected.push(
cache
@ -1775,7 +1822,7 @@ pub fn open_runtime_model_execution(
.ok_or_else(|| anyhow!("model execution lost its exact session envelope"))?,
);
}
let replacements = vec![
let mut replacements = vec![
identity_envelope,
cache.prepare_entry(&session.session_id, &session)?.0,
cache.prepare_entry(&job.job_id, &job)?.0,
@ -1790,6 +1837,7 @@ pub fn open_runtime_model_execution(
.prepare_entry(&provider_request.request_id, provider_request)?
.0,
];
replacements.extend(source_worker_envelopes);
if !runtime_spine_backing_store(store_path)?.compare_and_swap_batch(&expected, replacements)? {
return Err(anyhow!(
"model execution request publication lost its snapshot fence"
@ -1843,6 +1891,8 @@ pub fn put_runtime_tool_execution_intent(
"tool execution job {job_id:?} is foreign or terminal"
));
}
let governed_source_authority =
validate_governed_source_tool_intent(&cache, job_id, intent)?;
if cache
.get::<EpiphanyRuntimeToolExecutionBinding>(&intent.intent_id)?
.is_some()
@ -1874,6 +1924,8 @@ pub fn put_runtime_tool_execution_intent(
job_envelope.clone(),
];
let mut replacements = expected.clone();
expected.extend(governed_source_authority.iter().cloned());
replacements.extend(governed_source_authority);
if let Some(model_request_id) = intent.model_request_id.as_deref() {
let model_binding = cache
.get::<EpiphanyRuntimeModelExecutionBinding>(model_request_id)?
@ -1892,6 +1944,15 @@ pub fn put_runtime_tool_execution_intent(
intent.intent_id
));
}
let persisted_model_request = cache
.get::<EpiphanyModelRequest>(model_request_id)?
.ok_or_else(|| anyhow!("model-derived tool intent lost its model request"))?;
if persisted_model_request.source_worker_job_id != model_binding.source_worker_job_id {
return Err(anyhow!(
"model-derived tool intent {:?} has substituted worker provenance",
intent.intent_id
));
}
for envelope in [
cache.get_envelope::<EpiphanyRuntimeModelExecutionBinding>(model_request_id)?,
cache.get_envelope::<EpiphanyModelRequest>(model_request_id)?,
@ -1971,6 +2032,7 @@ pub fn require_runtime_tool_execution_binding(
"tool execution binding {intent_id:?} has foreign or archived ownership"
));
}
let _ = validate_governed_source_tool_intent(&cache, &binding.job_id, &intent)?;
if let Some(model_request_id) = binding.model_request_id.as_deref() {
let model_binding = cache
.get::<EpiphanyRuntimeModelExecutionBinding>(model_request_id)?
@ -3527,6 +3589,260 @@ pub fn runtime_worker_process_claim(
cache.get::<EpiphanyRuntimeWorkerProcessClaim>(&worker_process_claim_id(job_id))
}
pub fn runtime_authenticated_public_source_lookups_for_worker(
store_path: impl AsRef<Path>,
worker_job_id: &str,
) -> Result<Vec<EyesSourceLookupReceipt>> {
validate_non_empty(worker_job_id, "public source worker job id")?;
let mut cache = runtime_spine_cache(store_path)?;
cache.pull_all_backing_stores()?;
require_identity(&cache)?;
authenticated_public_source_lookups_for_worker(&cache, worker_job_id)
}
pub(crate) fn authenticated_public_source_lookups_for_worker(
cache: &CultCache,
worker_job_id: &str,
) -> Result<Vec<EyesSourceLookupReceipt>> {
validate_non_empty(worker_job_id, "public source worker job id")?;
require_identity(cache)?;
let launch = cache
.get::<EpiphanyRuntimeWorkerLaunchRequest>(worker_job_id)?
.ok_or_else(|| anyhow!("public source worker has no launch request"))?;
if launch.binding_id != crate::EPIPHANY_RESEARCH_ROLE_BINDING_ID
|| launch.role != crate::EPIPHANY_RESEARCH_OWNER_ROLE
{
return Err(anyhow!("public source evidence is Eyes-owned"));
}
let grant_id = format!("substrate-grant-{worker_job_id}");
let grant = cache
.get::<SubstrateGateRepoAccessGrantReceipt>(&grant_id)?
.ok_or_else(|| anyhow!("public source worker lost its launch grant"))?;
if grant.runtime_job_id != worker_job_id
|| grant.binding_id != launch.binding_id
|| grant.role != launch.role
|| grant.authority_scope != launch.authority_scope
|| !grant
.granted_operations
.iter()
.any(|operation| operation == crate::substrate_gate::SUBSTRATE_GATE_PUBLIC_SOURCE_READ_OPERATION)
{
return Err(anyhow!("public source worker grant is not exact"));
}
let model_request_ids = cache
.get_all::<EpiphanyRuntimeModelExecutionBinding>()?
.into_iter()
.filter(|binding| binding.source_worker_job_id.as_deref() == Some(worker_job_id))
.map(|binding| binding.request_id)
.collect::<BTreeSet<_>>();
let mut lookups = Vec::new();
for binding in cache.get_all::<EpiphanyRuntimeToolExecutionBinding>()? {
let Some(model_request_id) = binding.model_request_id.as_deref() else {
continue;
};
if !model_request_ids.contains(model_request_id) {
continue;
}
let intent = cache
.get::<EpiphanyToolInvocationIntent>(&tool_invocation_intent_key(
&binding.intent_id,
))?
.ok_or_else(|| anyhow!("public source tool binding lost its intent"))?;
if intent.server != "epiphany_public" || intent.tool_name != "github_file" {
continue;
}
let receipt = cache
.get::<EpiphanyToolInvocationReceipt>(&tool_invocation_receipt_key(
&intent.intent_id,
))?
.ok_or_else(|| anyhow!("public source tool intent has no terminal receipt"))?;
let _ = validate_governed_source_tool_intent(&cache, &binding.job_id, &intent)?;
if binding.intent_id != intent.intent_id
|| intent.model_request_id.as_deref() != Some(model_request_id)
|| receipt.intent_id != intent.intent_id
|| receipt.adapter != intent.adapter
|| receipt.server != intent.server
|| receipt.tool_name != intent.tool_name
|| receipt.status != "completed"
|| chrono::DateTime::parse_from_rfc3339(&receipt.completed_at).is_err()
{
return Err(anyhow!("public source tool family is not exact and terminal"));
}
let arguments: serde_json::Value = serde_json::from_str(&intent.arguments_json)
.context("public source intent arguments are invalid")?;
let result: serde_json::Value = serde_json::from_str(
receipt
.result_json
.as_deref()
.ok_or_else(|| anyhow!("public source receipt has no result"))?,
)
.context("public source receipt result is invalid")?;
let owner = public_source_json_string(&arguments, "owner")?;
let repository_name = public_source_json_string(&arguments, "repository")?;
let revision = public_source_json_string(&arguments, "revision")?.to_ascii_lowercase();
let path = public_source_json_string(&arguments, "path")?;
let content = public_source_json_string(&result, "content")?;
let repository = format!("github://{owner}/{repository_name}");
let source_ref = format!("{repository}@{revision}/{path}");
let content_sha256 = format!("{:x}", Sha256::digest(content.as_bytes()));
let byte_count = content.len() as u64;
let lookup_receipt_id = format!("eyes-source-{}", intent.intent_id);
if public_source_json_string(&result, "provider")? != "github"
|| public_source_json_string(&result, "repository")? != repository
|| public_source_json_string(&result, "revision")? != revision
|| public_source_json_string(&result, "path")? != path
|| public_source_json_string(&result, "sourceRef")? != source_ref
|| public_source_json_string(&result, "contentSha256")? != content_sha256
|| result.get("byteCount").and_then(serde_json::Value::as_u64)
!= Some(byte_count)
|| public_source_json_string(&result, "evidenceReceiptId")? != lookup_receipt_id
{
return Err(anyhow!("public source result provenance is substituted"));
}
lookups.push(EyesSourceLookupReceipt {
schema_version: crate::EYES_SOURCE_LOOKUP_RECEIPT_SCHEMA_VERSION.to_string(),
receipt_id: lookup_receipt_id,
source_job_id: worker_job_id.to_string(),
substrate_grant_receipt_id: grant_id.clone(),
tool_intent_id: intent.intent_id,
tool_receipt_id: receipt.receipt_id,
provider: "github".to_string(),
repository,
revision,
path: path.to_string(),
source_ref,
content_sha256,
byte_count,
observed_at: receipt.completed_at,
contract: "Eyes admitted one bounded immutable public source only after authenticating its worker, model, tool, grant, provider identity, and content digest.".to_string(),
});
}
lookups.sort_by(|left, right| left.receipt_id.cmp(&right.receipt_id));
Ok(lookups)
}
fn public_source_json_string<'a>(value: &'a serde_json::Value, field: &str) -> Result<&'a str> {
value
.get(field)
.and_then(serde_json::Value::as_str)
.filter(|value| !value.is_empty())
.ok_or_else(|| anyhow!("public source cargo omitted {field:?}"))
}
fn validate_governed_source_tool_intent(
cache: &CultCache,
job_id: &str,
intent: &EpiphanyToolInvocationIntent,
) -> Result<Vec<CultCacheEnvelope>> {
let required_operation =
crate::substrate_gate_operation_for_governed_tool(&intent.server, &intent.tool_name);
let source_worker_job_id = match intent.model_request_id.as_deref() {
Some(model_request_id) => {
let model_binding = cache
.get::<EpiphanyRuntimeModelExecutionBinding>(model_request_id)?
.ok_or_else(|| {
anyhow!(
"model-derived tool intent {:?} has no execution binding",
intent.intent_id
)
})?;
match model_binding.source_worker_job_id {
Some(worker_job_id) => Some(worker_job_id),
None if required_operation.is_some() => {
return Err(anyhow!(
"governed source tool intent {:?} has no source worker authority",
intent.intent_id
));
}
None => None,
}
}
None => {
if cache
.get::<EpiphanyRuntimeWorkerLaunchRequest>(job_id)?
.is_some_and(|launch| {
matches!(
launch.binding_id.as_str(),
crate::EPIPHANY_RESEARCH_ROLE_BINDING_ID
| crate::EPIPHANY_MODELING_ROLE_BINDING_ID
| crate::EPIPHANY_VERIFICATION_ROLE_BINDING_ID
)
})
{
return Err(anyhow!(
"governed source worker refuses a tool intent without model-call provenance"
));
}
None
}
};
let Some(source_worker_job_id) = source_worker_job_id else {
return Ok(Vec::new());
};
let launch = cache
.get::<EpiphanyRuntimeWorkerLaunchRequest>(&source_worker_job_id)?
.ok_or_else(|| anyhow!("governed source tool intent lost its source worker launch"))?;
let source_worker_job = cache
.get::<EpiphanyRuntimeJob>(&source_worker_job_id)?
.ok_or_else(|| anyhow!("governed source tool intent lost its source worker job"))?;
if !matches!(
launch.binding_id.as_str(),
crate::EPIPHANY_RESEARCH_ROLE_BINDING_ID
| crate::EPIPHANY_MODELING_ROLE_BINDING_ID
| crate::EPIPHANY_VERIFICATION_ROLE_BINDING_ID
) {
return Ok(Vec::new());
}
let required_operation = required_operation.ok_or_else(|| {
anyhow!(
"governed source worker rejected unknown tool {}::{}",
intent.server,
intent.tool_name
)
})?;
let grant_id = format!("substrate-grant-{source_worker_job_id}");
let grant = cache
.get::<SubstrateGateRepoAccessGrantReceipt>(&grant_id)?
.ok_or_else(|| {
anyhow!(
"governed source tool intent {:?} has no exact launch grant",
intent.intent_id
)
})?;
if source_worker_job.role != launch.role
|| matches!(
source_worker_job.status,
EpiphanyRuntimeJobStatus::Completed
| EpiphanyRuntimeJobStatus::Failed
| EpiphanyRuntimeJobStatus::Cancelled
)
|| grant.runtime_job_id != source_worker_job_id
|| grant.binding_id != launch.binding_id
|| grant.role != launch.role
|| grant.authority_scope != launch.authority_scope
|| !grant
.granted_operations
.iter()
.any(|operation| operation == required_operation)
{
return Err(anyhow!(
"governed source tool intent {:?} is outside its launch grant",
intent.intent_id
));
}
Ok(vec![
cache
.get_envelope::<EpiphanyRuntimeJob>(&source_worker_job_id)?
.ok_or_else(|| anyhow!("governed source tool lost worker job envelope"))?,
cache
.get_envelope::<EpiphanyRuntimeWorkerLaunchRequest>(&source_worker_job_id)?
.ok_or_else(|| anyhow!("governed source tool lost worker launch envelope"))?,
cache
.get_envelope::<SubstrateGateRepoAccessGrantReceipt>(&grant_id)?
.ok_or_else(|| anyhow!("governed source tool lost launch grant envelope"))?,
])
}
pub fn runtime_worker_process_claims(
store_path: impl AsRef<Path>,
) -> Result<Vec<EpiphanyRuntimeWorkerProcessClaim>> {
@ -16047,6 +16363,7 @@ pub(crate) mod tests {
evidence_ids: vec![format!("evidence-{suffix}")],
observation_ids: vec![format!("observation-{suffix}")],
source_refs: vec!["epiphany-core/src/runtime_spine.rs:1".into()],
source_lookup_receipt_ids: Vec::new(),
summary: "The exact claim is challenged by inspected source.".into(),
uncertainty: "Bounded to the cited source.".into(),
emitted_at: "2026-07-13T04:01:00Z".into(),
@ -19160,6 +19477,202 @@ pub(crate) mod tests {
Ok(())
}
#[test]
fn governed_public_tool_intent_requires_exact_worker_launch_grant() -> Result<()> {
let temp = tempdir()?;
let store = temp.path().join("runtime.msgpack");
initialize_runtime_spine(
&store,
RuntimeSpineInitOptions {
runtime_id: "epiphany-public-tool-test".to_string(),
display_name: "Epiphany Public Tool Test".to_string(),
created_at: "2026-08-11T00:00:00Z".to_string(),
},
)?;
let state = crate::EpiphanyThreadState::default();
let launch = crate::build_epiphany_role_launch_request(
"thread-public-tool",
crate::EpiphanyRoleResultRoleId::Research,
Some(state.revision),
Some(60),
&state,
)
.map_err(anyhow::Error::msg)?;
let plan = crate::plan_coordinator_job_launch(
&state,
&launch,
&store,
"launcher-public-tool".to_string(),
"worker-public-tool".to_string(),
)?;
crate::commit_coordinator_job_launch(
&store,
"thread-public-tool",
&state,
&launch,
&plan,
"2026-08-11T00:00:01Z".to_string(),
)?;
let mut model_request = EpiphanyModelRequest::new(
"request-public-tool",
"conversation-public-tool",
"openai-codex",
"gpt-test",
"Read one immutable public source.",
);
model_request.source_worker_job_id = Some("worker-public-tool".to_string());
let provider_request = EpiphanyOpenAiModelRequest::new(
"request-public-tool",
"conversation-public-tool",
"gpt-test",
"Read one immutable public source.",
);
open_runtime_model_execution(
&store,
RuntimeSpineSessionOptions {
session_id: "model-session-public-tool".to_string(),
objective: "Read one immutable public source.".to_string(),
created_at: "2026-08-11T00:00:02Z".to_string(),
coordinator_note: "test".to_string(),
},
RuntimeSpineJobOptions {
job_id: "model-job-public-tool".to_string(),
session_id: "model-session-public-tool".to_string(),
role: "openai-model-adapter".to_string(),
created_at: "2026-08-11T00:00:02Z".to_string(),
summary: "Bound public source model turn.".to_string(),
artifact_refs: Vec::new(),
},
&model_request,
&provider_request,
"2026-08-11T00:00:02Z",
)?;
let exact = EpiphanyToolInvocationIntent::new(
"intent-public-tool-exact",
"epiphany-tools",
"epiphany_public",
"github_file",
r#"{"owner":"openai","repository":"openai-openapi","revision":"0123456789abcdef0123456789abcdef01234567","path":"openapi.yaml"}"#,
"model-runtime",
"Read immutable source.",
"2026-08-11T00:00:03Z",
)
.with_model_call("call-public-tool-exact", "request-public-tool");
put_runtime_tool_execution_intent(
&store,
"model-session-public-tool",
"model-job-public-tool",
&exact,
"2026-08-11T00:00:03Z",
)?;
let content = "openapi: 3.1.0\n";
let content_sha256 = format!("{:x}", Sha256::digest(content.as_bytes()));
let mut exact_receipt = EpiphanyToolInvocationReceipt::new(
"receipt-public-tool-exact",
&exact.intent_id,
&exact.adapter,
&exact.server,
&exact.tool_name,
"completed",
"2026-08-11T00:00:04Z",
);
exact_receipt.result_json = Some(
serde_json::json!({
"provider": "github",
"repository": "github://openai/openai-openapi",
"revision": "0123456789abcdef0123456789abcdef01234567",
"path": "openapi.yaml",
"sourceRef": "github://openai/openai-openapi@0123456789abcdef0123456789abcdef01234567/openapi.yaml",
"contentSha256": content_sha256,
"byteCount": content.len(),
"content": content,
"evidenceReceiptId": "eyes-source-intent-public-tool-exact"
})
.to_string(),
);
put_runtime_tool_execution_receipt(&store, &exact_receipt)?;
let lookups = runtime_authenticated_public_source_lookups_for_worker(
&store,
"worker-public-tool",
)?;
assert_eq!(lookups.len(), 1);
assert_eq!(
lookups[0].receipt_id,
"eyes-source-intent-public-tool-exact"
);
assert_eq!(lookups[0].content_sha256, content_sha256);
let mut cache = runtime_spine_cache(&store)?;
cache.pull_all_backing_stores()?;
let receipt_key = tool_invocation_receipt_key(&exact.intent_id);
let mut substituted_receipt = cache
.get::<EpiphanyToolInvocationReceipt>(&receipt_key)?
.expect("exact public source receipt");
let mut substituted_result: serde_json::Value = serde_json::from_str(
substituted_receipt.result_json.as_deref().unwrap(),
)?;
substituted_result["byteCount"] = serde_json::json!(1);
substituted_receipt.result_json = Some(substituted_result.to_string());
cache.put(&receipt_key, &substituted_receipt)?;
let before_refusal = runtime_spine_backing_store(&store)?.pull_all()?;
assert!(
runtime_authenticated_public_source_lookups_for_worker(
&store,
"worker-public-tool"
)
.is_err()
);
assert_eq!(runtime_spine_backing_store(&store)?.pull_all()?, before_refusal);
cache.put(&receipt_key, &exact_receipt)?;
let grant_id = "substrate-grant-worker-public-tool";
let mut grant = cache
.get::<SubstrateGateRepoAccessGrantReceipt>(grant_id)?
.expect("exact worker launch grant");
grant
.granted_operations
.retain(|operation| operation != "publicSourceRead");
cache.put(grant_id, &grant)?;
let hostile = EpiphanyToolInvocationIntent::new(
"intent-public-tool-hostile",
"epiphany-tools",
"epiphany_public",
"github_file",
"{}",
"model-runtime",
"Attempt public read without operation authority.",
"2026-08-11T00:00:04Z",
)
.with_model_call("call-public-tool-hostile", "request-public-tool");
let before = runtime_spine_backing_store(&store)?.pull_all()?;
assert!(
put_runtime_tool_execution_intent(
&store,
"model-session-public-tool",
"model-job-public-tool",
&hostile,
"2026-08-11T00:00:04Z",
)
.is_err()
);
assert_eq!(runtime_spine_backing_store(&store)?.pull_all()?, before);
let mut reloaded = runtime_spine_cache(&store)?;
reloaded.pull_all_backing_stores()?;
assert!(
reloaded
.get::<EpiphanyRuntimeToolExecutionBinding>(&hostile.intent_id)?
.is_none()
);
assert!(
reloaded
.get::<EpiphanyToolInvocationIntent>(&tool_invocation_intent_key(
&hostile.intent_id,
))?
.is_none()
);
Ok(())
}
#[test]
fn archived_model_session_is_exact_terminal_tombstone() -> Result<()> {
let temp = tempdir()?;
@ -19599,6 +20112,7 @@ pub(crate) mod tests {
evidence_ids: vec!["ev-1".to_string()],
observation_ids: vec!["obs-1".to_string()],
source_refs: vec!["src/lib.rs:1".to_string()],
source_lookup_receipt_ids: Vec::new(),
summary: "Evidence packet.".to_string(),
uncertainty: "none declared".to_string(),
emitted_at: "2026-05-06T00:05:00Z".to_string(),

View file

@ -27,6 +27,8 @@ pub const SUBSTRATE_GATE_REPO_SNAPSHOT_RECEIPT_SCHEMA_VERSION: &str =
"epiphany.substrate_gate.repo_snapshot_receipt.v0";
pub const SUBSTRATE_GATE_REPO_MUTATION_RECEIPT_SCHEMA_VERSION: &str =
"epiphany.substrate_gate.repo_mutation_receipt.v0";
pub const SUBSTRATE_GATE_SOURCE_READ_OPERATION: &str = "read";
pub const SUBSTRATE_GATE_PUBLIC_SOURCE_READ_OPERATION: &str = "publicSourceRead";
#[derive(Debug, Clone, PartialEq, Eq, DatabaseEntry)]
#[cultcache(
@ -143,6 +145,13 @@ pub fn substrate_gate_repo_access_grant_for_launch(
request: &EpiphanyJobLaunchRequest,
granted_at: String,
) -> SubstrateGateRepoAccessGrantReceipt {
let mut granted_operations = vec![
SUBSTRATE_GATE_SOURCE_READ_OPERATION.to_string(),
"snapshot".to_string(),
];
if request.binding_id == crate::EPIPHANY_RESEARCH_ROLE_BINDING_ID {
granted_operations.push(SUBSTRATE_GATE_PUBLIC_SOURCE_READ_OPERATION.to_string());
}
SubstrateGateRepoAccessGrantReceipt {
schema_version: SUBSTRATE_GATE_REPO_ACCESS_GRANT_RECEIPT_SCHEMA_VERSION.to_string(),
receipt_id,
@ -150,10 +159,29 @@ pub fn substrate_gate_repo_access_grant_for_launch(
binding_id: request.binding_id.clone(),
role: request.owner_role.clone(),
authority_scope: request.authority_scope.clone(),
granted_operations: vec!["read".to_string(), "snapshot".to_string()],
granted_operations,
granted_paths: vec![".".to_string()],
granted_at,
contract: "Substrate Gate granted scoped repository read/snapshot access for this worker launch; mutation remains forbidden without a separate mutation receipt.".to_string(),
contract: "Substrate Gate granted the exact worker launch its named governed source operations; public-source reads are Eyes-only and mutation remains forbidden without a separate mutation receipt.".to_string(),
}
}
pub fn substrate_gate_operation_for_governed_tool(
server: &str,
tool_name: &str,
) -> Option<&'static str> {
match (server, tool_name) {
(
"epiphany_source",
"read_file" | "directory_inventory" | "git_show" | "read_hands_receipt",
)
| ("epiphany_state", "resident_grant_lifecycle") => {
Some(SUBSTRATE_GATE_SOURCE_READ_OPERATION)
}
("epiphany_public", "github_file") => {
Some(SUBSTRATE_GATE_PUBLIC_SOURCE_READ_OPERATION)
}
_ => None,
}
}
@ -294,10 +322,31 @@ mod tests {
"2026-05-30T00:00:00Z".to_string(),
);
assert!(grant.granted_operations.contains(&"read".to_string()));
assert!(
grant
.granted_operations
.contains(&"publicSourceRead".to_string())
);
assert!(!grant.granted_operations.contains(&"write".to_string()));
assert!(grant.contract.contains("mutation remains forbidden"));
}
#[test]
fn governed_tools_map_to_fixed_grant_operations() {
assert_eq!(
substrate_gate_operation_for_governed_tool("epiphany_source", "read_file"),
Some("read")
);
assert_eq!(
substrate_gate_operation_for_governed_tool("epiphany_public", "github_file"),
Some("publicSourceRead")
);
assert_eq!(
substrate_gate_operation_for_governed_tool("epiphany_public", "arbitrary_url"),
None
);
}
#[test]
fn repo_work_and_implementation_grants_have_fixed_policies() {
let planning = substrate_gate_repo_work_planning_grant(

View file

@ -58,6 +58,8 @@ pub struct EpiphanyModelRequest {
pub tools: Vec<EpiphanyModelToolDefinition>,
#[cultcache(key = 13, default)]
pub output_schema_json: Option<String>,
#[cultcache(key = 14, default)]
pub source_worker_job_id: Option<String>,
}
impl EpiphanyModelRequest {
@ -83,6 +85,7 @@ impl EpiphanyModelRequest {
previous_response_id: None,
tools: Vec::new(),
output_schema_json: None,
source_worker_job_id: None,
}
}
}

View file

@ -595,7 +595,7 @@ pub fn build_worker_model_request(
if launch_request.binding_id == epiphany_core::EPIPHANY_VERIFICATION_ROLE_BINDING_ID {
instructions.push_str("\n\nTool mandate: before returning `needs-evidence` because source files, artifact directories, command artifacts, commit diffs, Hands receipt bodies, or resident grant lifecycle are not inspectable, call the governed read-only tools available on this request. Use `mcp__epiphany_source__read_file` for cited source/artifact files, `mcp__epiphany_source__directory_inventory` for bounded workspace directory counts and bytes, `mcp__epiphany_source__git_show` for commit diffs, `mcp__epiphany_source__read_hands_receipt` for Hands patch/command/commit receipts, and `mcp__epiphany_state__resident_grant_lifecycle` for exact or bounded recent grant-owned lifecycle state. Directory totals are authoritative only when the tool reports `complete=true`. Grant launchability is authoritative only from the typed state projection, never artifact names or acknowledgement presence. If a tool fails, cite that failed tool result and the exact remaining blocker.");
} else if launch_request.binding_id == epiphany_core::EPIPHANY_RESEARCH_ROLE_BINDING_ID {
instructions.push_str("\n\nTool mandate: Eyes must inspect current repository sources or typed resident state before emitting evidence. Call the bounded read-only file, directory-inventory, Git, or `mcp__epiphany_state__resident_grant_lifecycle` tool appropriate to the claim; cite exact inspected paths/revisions or grant identities in filesInspected and evidence, and report a source gap if the required body cannot be observed. Directory totals are authoritative only when the inventory reports `complete=true`; grant launchability is authoritative only from the typed lifecycle projection.");
instructions.push_str("\n\nTool mandate: Eyes must inspect current repository sources, immutable public GitHub source, or typed resident state before emitting evidence. Call the bounded governed tool appropriate to the claim. Public evidence uses `mcp__epiphany_public__github_file` with an exact 40-hex commit, repository, and path; put its exact sourceRef in filesInspected and its evidenceReceiptId in evidence, and preserve its contentSha256 in the finding. Never substitute a branch, tag, arbitrary URL, or model memory for immutable public evidence. Cite exact inspected paths/revisions or grant identities in filesInspected and evidence, and report a source gap if the required body cannot be observed. Directory totals are authoritative only when the inventory reports `complete=true`; grant launchability is authoritative only from the typed lifecycle projection.");
} else if launch_request.binding_id == epiphany_core::EPIPHANY_MODELING_ROLE_BINDING_ID {
instructions.push_str("\n\nTool mandate: Modeling must inspect current repository sources or typed resident state before proposing repository anatomy. Call the bounded read-only file, directory-inventory, Git, or `mcp__epiphany_state__resident_grant_lifecycle` tool appropriate to the claim; cite exact inspected paths/revisions or grant identities in filesInspected and evidence, and emit regather-needed instead of inventing unobserved structure. Directory totals are authoritative only when the inventory reports `complete=true`; grant launchability is authoritative only from the typed lifecycle projection.");
}
@ -615,13 +615,16 @@ pub fn build_worker_model_request(
request.reasoning_summary = Some("concise".to_string());
request.output_contract_id = Some(launch_request.output_contract_id.clone());
request.output_schema_json = Some(output_schema_json);
request.source_worker_job_id = Some(launch_request.job_id.clone());
if matches!(
launch_request.binding_id.as_str(),
epiphany_core::EPIPHANY_RESEARCH_ROLE_BINDING_ID
| epiphany_core::EPIPHANY_MODELING_ROLE_BINDING_ID
| epiphany_core::EPIPHANY_VERIFICATION_ROLE_BINDING_ID
) {
request.tools = repository_source_tools();
request.tools = repository_source_tools(
launch_request.binding_id == epiphany_core::EPIPHANY_RESEARCH_ROLE_BINDING_ID,
);
}
Ok(request)
}
@ -1047,6 +1050,7 @@ pub fn model_request_from_openai_request(
output_contract_id: request.output_contract_id.clone(),
previous_response_id: request.previous_response_id.clone(),
output_schema_json: request.output_schema_json.clone(),
source_worker_job_id: None,
tools: request
.tools
.iter()
@ -1091,8 +1095,8 @@ pub fn openai_request_from_model_request(
}
}
fn repository_source_tools() -> Vec<EpiphanyModelToolDefinition> {
vec![
fn repository_source_tools(include_public_source: bool) -> Vec<EpiphanyModelToolDefinition> {
let mut tools = vec![
EpiphanyModelToolDefinition {
name: "mcp__epiphany_source__read_file".to_string(),
description: "Read a bounded UTF-8 text slice from the current workspace for source-grounded Eyes, Modeling, or Soul work. Use only for repository sources and operator-safe artifacts in scope.".to_string(),
@ -1166,7 +1170,27 @@ fn repository_source_tools() -> Vec<EpiphanyModelToolDefinition> {
})
.to_string(),
},
]
];
if include_public_source {
tools.push(EpiphanyModelToolDefinition {
name: "mcp__epiphany_public__github_file".to_string(),
description: "Read one bounded UTF-8 file from a public GitHub repository at an exact immutable 40-hex commit. Returns provider/repository/revision/path/sourceRef/contentSha256 provenance. Branches, tags, redirects, arbitrary hosts, and mutable URLs are refused.".to_string(),
parameters_json: serde_json::json!({
"type": "object",
"additionalProperties": false,
"properties": {
"owner": {"type": "string"},
"repository": {"type": "string"},
"revision": {"type": "string", "pattern": "^[0-9a-fA-F]{40}$"},
"path": {"type": "string"},
"maxBytes": {"type": "integer", "minimum": 512, "maximum": 65536}
},
"required": ["owner", "repository", "revision", "path"]
})
.to_string(),
});
}
tools
}
fn model_input_from_openai_input(input: &EpiphanyOpenAiInputItem) -> EpiphanyModelInputItem {
@ -3186,6 +3210,10 @@ mod tests {
.tools
.iter()
.any(|tool| tool.name == "mcp__epiphany_state__resident_grant_lifecycle"));
assert!(!model_request
.tools
.iter()
.any(|tool| tool.name == "mcp__epiphany_public__github_file"));
assert!(model_request.instructions.contains("Modeling must inspect"));
let openai_summary = EpiphanyOpenAiRuntimeRunSummary {
store: store.display().to_string(),
@ -3338,6 +3366,7 @@ mod tests {
assert!(tool_names.contains(&"mcp__epiphany_source__git_show"));
assert!(tool_names.contains(&"mcp__epiphany_source__read_hands_receipt"));
assert!(tool_names.contains(&"mcp__epiphany_state__resident_grant_lifecycle"));
assert!(!tool_names.contains(&"mcp__epiphany_public__github_file"));
assert!(model_request
.instructions
.contains("mcp__epiphany_source__read_file"));
@ -3347,6 +3376,21 @@ mod tests {
assert!(model_request
.instructions
.contains("mcp__epiphany_state__resident_grant_lifecycle"));
let mut research_launch = launch_request;
research_launch.binding_id =
epiphany_core::EPIPHANY_RESEARCH_ROLE_BINDING_ID.to_string();
research_launch.role = epiphany_core::EPIPHANY_RESEARCH_OWNER_ROLE.to_string();
research_launch.authority_scope = "epiphany.role.research".to_string();
let research_request =
build_worker_model_request(&research_launch, DEFAULT_MODEL_PROVIDER, "gpt-5.4")?;
assert!(research_request
.tools
.iter()
.any(|tool| tool.name == "mcp__epiphany_public__github_file"));
assert!(research_request
.instructions
.contains("exact 40-hex commit"));
Ok(())
}

View file

@ -12,6 +12,7 @@ epiphany-core = { path = "../epiphany-core" }
cultcache-rs = { path = "../vendor/cultcache-rs" }
http = "1"
reqwest = { version = "0.13.2", default-features = false, features = ["json", "rustls"] }
sha2 = "0.10"
rmcp = { version = "2.2.0", default-features = false, features = ["client", "transport-child-process", "transport-streamable-http-client-reqwest"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

View file

@ -4,9 +4,11 @@
//! endpoint policy and translates between MCP cargo and typed tool receipts.
mod config;
mod native_public;
mod native_source;
pub use config::{McpRuntimeConfig, McpServerConfig, McpTransportConfig};
pub use native_public::execute_epiphany_public;
pub use native_source::execute_epiphany_source;
use anyhow::{Context, Result, anyhow};

View file

@ -5,7 +5,8 @@ use epiphany_tool_adapter::{
TOOL_ADAPTER_INVOCATION_RECEIPT_SCHEMA_ID, tool_invocation_intent_key,
};
use epiphany_tool_mcp_runtime::{
McpRuntimeConfig, execute_epiphany_source, invoke, validate_intent,
McpRuntimeConfig, execute_epiphany_public, execute_epiphany_source, invoke,
validate_intent,
};
use serde::Serialize;
use std::collections::BTreeMap;
@ -95,6 +96,8 @@ async fn execute_to_receipt(
None => std::env::current_dir()?,
};
execute_epiphany_source(intent, &options.store, &cwd)
} else if intent.server == "epiphany_public" {
execute_epiphany_public(intent).await
} else if intent.server == "epiphany_state" {
let resident_store = options.resident_store.as_ref().ok_or_else(|| {
anyhow!("epiphany_state requires an explicitly bound --resident-store")

View file

@ -0,0 +1,182 @@
use anyhow::{Context, Result, anyhow};
use epiphany_tool_adapter::EpiphanyToolInvocationIntent;
use reqwest::redirect::Policy;
use serde_json::{Value, json};
use sha2::{Digest, Sha256};
use std::time::Duration;
const MAX_PUBLIC_SOURCE_BYTES: usize = 64 * 1024;
pub async fn execute_epiphany_public(intent: &EpiphanyToolInvocationIntent) -> Result<Value> {
let arguments: Value =
serde_json::from_str(&intent.arguments_json).context("arguments_json is not valid JSON")?;
if !arguments.is_object() {
return Err(anyhow!("epiphany_public arguments must be an object"));
}
match intent.tool_name.as_str() {
"github_file" => github_file(intent, &arguments).await,
other => Err(anyhow!("unknown epiphany_public tool {other:?}")),
}
}
async fn github_file(intent: &EpiphanyToolInvocationIntent, arguments: &Value) -> Result<Value> {
let owner = repository_component(arguments, "owner")?;
let repository = repository_component(arguments, "repository")?;
let revision = required_string(arguments, "revision")?;
if revision.len() != 40 || !revision.bytes().all(|byte| byte.is_ascii_hexdigit()) {
return Err(anyhow!(
"github_file revision must be an immutable 40-hex commit id"
));
}
let path = required_string(arguments, "path")?;
validate_repository_path(path)?;
let maximum = arguments
.get("maxBytes")
.map(|value| {
value
.as_u64()
.ok_or_else(|| anyhow!("github_file maxBytes must be unsigned"))
})
.transpose()?
.unwrap_or(32_768)
.clamp(512, MAX_PUBLIC_SOURCE_BYTES as u64) as usize;
let revision = revision.to_ascii_lowercase();
let url = format!("https://raw.githubusercontent.com/{owner}/{repository}/{revision}/{path}");
let client = reqwest::Client::builder()
.redirect(Policy::none())
.no_proxy()
.timeout(Duration::from_secs(15))
.user_agent("epiphany-public-source/0")
.build()
.context("building bounded public-source client")?;
let mut response = client
.get(&url)
.send()
.await
.context("reading immutable GitHub source")?;
if !response.status().is_success() {
return Err(anyhow!(
"immutable GitHub source returned HTTP {}",
response.status()
));
}
if response
.content_length()
.is_some_and(|length| length > maximum as u64)
{
return Err(anyhow!("immutable GitHub source exceeds maxBytes"));
}
let mut bytes = Vec::with_capacity(maximum.min(8_192));
while let Some(chunk) = response
.chunk()
.await
.context("reading immutable GitHub source body")?
{
if bytes.len() + chunk.len() > maximum {
return Err(anyhow!("immutable GitHub source exceeds maxBytes"));
}
bytes.extend_from_slice(&chunk);
}
let content = String::from_utf8(bytes.clone())
.map_err(|_| anyhow!("immutable GitHub source is not UTF-8 text"))?;
let content_sha256 = format!("{:x}", Sha256::digest(&bytes));
let evidence_receipt_id = format!("eyes-source-{}", intent.intent_id);
Ok(json!({
"provider": "github",
"repository": format!("github://{owner}/{repository}"),
"revision": revision,
"path": path,
"sourceRef": format!("github://{owner}/{repository}@{revision}/{path}"),
"contentSha256": content_sha256,
"evidenceReceiptId": evidence_receipt_id,
"byteCount": bytes.len(),
"content": content,
}))
}
fn repository_component<'a>(arguments: &'a Value, name: &str) -> Result<&'a str> {
let value = required_string(arguments, name)?;
if value.len() > 100
|| !value
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_' | b'.'))
{
return Err(anyhow!("github_file {name} is invalid"));
}
Ok(value)
}
fn validate_repository_path(path: &str) -> Result<()> {
if path.len() > 512
|| path.starts_with('/')
|| path.split('/').any(|segment| {
segment.is_empty()
|| matches!(segment, "." | "..")
|| !segment
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_' | b'.'))
})
{
return Err(anyhow!("github_file path is invalid"));
}
Ok(())
}
fn required_string<'a>(value: &'a Value, name: &str) -> Result<&'a str> {
value
.get(name)
.and_then(Value::as_str)
.filter(|value| !value.trim().is_empty())
.ok_or_else(|| anyhow!("missing required string argument {name:?}"))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn public_source_identity_requires_immutable_github_shape() {
let valid = json!({
"owner": "openai",
"repository": "openai-openapi",
"revision": "0123456789abcdef0123456789abcdef01234567",
"path": "openapi.yaml"
});
assert_eq!(repository_component(&valid, "owner").unwrap(), "openai");
assert!(validate_repository_path("docs/source_file.rs").is_ok());
assert!(validate_repository_path("../secret").is_err());
assert!(validate_repository_path("path with spaces").is_err());
}
#[tokio::test]
#[ignore = "live immutable GitHub source proof"]
async fn reads_exact_public_epiphany_source_with_digest_provenance() -> Result<()> {
let intent = EpiphanyToolInvocationIntent::new(
"live-public-source",
epiphany_tool_adapter::EPIPHANY_TOOL_RUNTIME_ADAPTER_ID,
"epiphany_public",
"github_file",
r#"{"owner":"GameCult","repository":"Epiphany","revision":"43dc865e0f332b82de4b95292d5c600f8b901706","path":"README.md","maxBytes":65536}"#,
"live-test",
"Prove exact public source access.",
"2026-08-11T00:00:00Z",
);
let result = execute_epiphany_public(&intent).await?;
assert_eq!(
result["sourceRef"],
"github://GameCult/Epiphany@43dc865e0f332b82de4b95292d5c600f8b901706/README.md"
);
assert_eq!(result["contentSha256"].as_str().map(str::len), Some(64));
assert!(result["byteCount"].as_u64().is_some_and(|count| count > 0));
assert!(
result["content"]
.as_str()
.is_some_and(|content| content.contains("Epiphany"))
);
println!(
"sourceRef={} contentSha256={} byteCount={}",
result["sourceRef"], result["contentSha256"], result["byteCount"]
);
Ok(())
}
}

View file

@ -4327,6 +4327,49 @@ Soul binding, and split-admission replay.
routing excludes manual regather. c010 remains alive, braked, and without an
active lease after native Imagination produced no admitted work.
## Governed source-tool authority gap (2026-08-11)
The transport body is broader than the admitted source faculty. The generic
MCP runtime can call configured subprocess or HTTP providers, while live Eyes,
Modeling, and Soul requests advertise only workspace-confined `read_file`,
`directory_inventory`, `git_show`, typed Hands-receipt reads, and the exact
resident-grant projection. No public-source tool is currently model-visible.
- Owner: the immutable Substrate Gate grant created with an exact worker launch
must own which governed source operations that job may invoke.
- Inputs: exact runtime job, launch binding and role; exact tool server/name;
and a fixed tool-to-operation classification owned by the source boundary.
- Outputs: runtime admission accepts a model-derived tool intent only when the
exact launch grant names its required operation. Public evidence additionally
carries provider, repository, immutable revision, path, and content digest.
- Derived state: MCP configuration, model-visible tool definitions, tool-call
JSON, and transport reach are capability projections or protocol cargo. None
grants source access by existing.
- Forbidden writers: a model-selected tool name, generic MCP endpoint, prompt
instruction, runtime binding, or Eyes packet may not synthesize Substrate
authority. Arbitrary URLs and redirects are not public-source identity.
- Shared paths: Eyes, Modeling, and Soul must receive source tools only through
the same launch-grant and runtime-intent admission primitive. Local and public
source operations differ in provider policy but share the grant check.
- Cut line: publish an immutable grant for every launch that advertises source
tools; reject an intent before adapter/network execution when its exact grant
lacks the mapped operation. Then add one bounded provider-shaped public source
operation rather than exposing the generic MCP transport as evidence authority.
- Verification layer: focused grant/intent tests must refuse absent, foreign,
role-swapped, job-swapped, and operation-missing grants byte-identically. The
packaged Linux circuit must admit one immutable public source through Eyes and
Mind while an ungranted public request produces no network consequence.
Current source enforces this map. Research, Modeling, and Verification launches
publish exact read grants; Research alone receives `publicSourceRead` and the
model-visible `epiphany_public.github_file` tool. Runtime admission follows the
inner model binding back to the outer typed worker launch and exact grant before
persisting an intent or executing an adapter. Public GitHub results become
`EyesSourceLookupReceipt` only after the full worker/model/tool/grant family and
the provider/repository/commit/path/content digest are reauthenticated inside
the same full-snapshot CAS that admits the Eyes packet into Mind. Generic MCP
transport and result JSON remain transport cargo, not evidence authority.
## Resident PID 1 owns adopted worker reaping (2026-08-11)
- Owner: resident Self owns OS cleanup for model-worker grandchildren that its

View file

@ -12,17 +12,18 @@ the actual braked resident body are the live gates.
## Current body
- Source branch: `codex/epiphany-shakedown-live`.
- Current pushed source: exact `a07f6541`.
- Accepted live runtime implementation: exact `a07f6541` through resident
directive settlement; autonomous promotion remains failed closed.
- Current pushed source: exact `43dc865e`.
- Accepted live runtime implementation: exact `465af24d` through resident PID 1
reaping across genuine Modeling and Imagination worker families.
- Audited/live release:
`sha256-ba51cbac5fe2400c27497c5df92fe128ce083a29c45e9ccf5eb95d21ec807b26`;
`sha256-089e000511f0420bfaab1136952b8cf94677a72d5c30a3fb3e6a26996bd3726f`;
witness
`sha256-01a6d5d74129ffb3096a3aa3f4ea7cd98d13af38b27d5322a8ab1d8f458d7a03`.
- Actual c008b: both owners stopped. The operator retry settled once at
revision 320; later autonomous turnover killed resident Self on a promotion
companion collision and left an active lease. Heartbeat stopped exit zero;
the exact cognitive brake is engaged.
`sha256-20ca293589b33d3b2e6ea5a7411db544741fe18360ecbd4423cdfa000d1ad7c6`.
- Actual c011: both owners are alive under the engaged cognitive brake at
resident revision 384, with no active lease, no admitted work, and zero
defunct children. Exact claimed worker PIDs 152 and 275 terminalized through
Modeling and Imagination respectively, settled once, and were reaped by the
resident PID 1 owner.
- Build body: Starfire. Yggdrasil is the small public crossing and never builds
this graph.
@ -31,8 +32,8 @@ the actual braked resident body are the live gates.
| Faculty | Owner | Accepted proof | Remaining live gate |
|---|---|---|---|
| Self | resident Self state machine | single-consumption Heartbeat grants, typed fulfillment, exact recovery/requeue, cooldown and brake behavior; actual c007h superseded one overtaken unlaunched continuation and accepted the recovered proposal-Modeling result exactly once | autonomous turnover from a genuinely new typed frontier; current `awaitFrontierProposal` is correctly idle |
| Substrate Gate | `substrate_gate` | scoped read/mutation grants on Eyes and Hands paths; canonical brake covers direct daemon lifecycle actuation; non-repository process utilities retain explicit owners | one broader foreign/public Verse evidence adoption path with Eyes |
| Eyes | Eyes gateway and authenticated source tools | challenged-claim Windows circuit and governed Linux tool circuit accepted into Mind | one broader foreign/public Verse evidence adoption path |
| Substrate Gate | `substrate_gate` | immutable scoped grants now bind every advertised Eyes, Modeling, and Soul source operation through the outer worker launch; canonical brake covers direct daemon lifecycle actuation; non-repository process utilities retain explicit owners | packaged negative proof that an ungranted public request creates no network consequence |
| Eyes | Eyes gateway and authenticated source tools | challenged-claim Windows circuit, governed Linux tool circuit, and exact immutable GitHub reader with typed receipt derivation | package and adopt one broader public source through Eyes into Mind |
| Modeling | runtime-owned typed Modeling contracts | causal Eyes-to-Modeling acceptance, direct proposal Modeling, archived fulfillment | broader Body-map coverage across a changing live repository |
| Imagination | typed proposal/consideration producers | genuine packaged multi-option generation, proposal-only candidates, exact fulfillment, no Hands adoption leak | autonomous turnover from the next genuine eligible frontier |
| Hands | Hands gateway | bounded patch, command, commit, push, and changed-path receipts; Idunn, crossing, forge, IDE, repo-birth, and model-runtime processes retain separate owners; Unity method override is structurally refused | audit the next Soul verifier boundary rather than adding Hands gates around non-Hands owners |
@ -41,7 +42,7 @@ the actual braked resident body are the live gates.
| Persona | Persona turn, speech audit, public mouth | cross-host request/permit/receipt, signed failure and unknown no-repost behavior, conversation retention | one completed public consequence; currently blocked by absent Yggdrasil Discord credential |
| Continuity | runtime/resident recovery and retention | graceful shutdown, exact process-death recovery, session closure/archive, lifecycle retention, non-resurrection, bounded endurance, and receipt-derived recovery on the current body | later deployed restart supervision |
| Nervous system | Heartbeat, runtime spine, Idunn, CultMesh | detached scheduling, typed jobs, provider readiness, active/warming transitions across PID namespaces, bounded endurance, narrow local-Verse decision reads, bounded Docker logs, and causal grant turnover | audit non-repository actuator publication and command ownership |
| Body | Rust binaries, named-volume CultCache stores, model transport | deterministic 24-binary packaging, fast warm replay, named-volume interruption proof | deployment-sized endurance and remaining inspection surfaces |
| Body | Rust binaries, named-volume CultCache stores, model transport | deterministic 24-binary packaging, fast warm replay, named-volume interruption proof, and exact PID 1 reaping across two adopted worker kinds | deployment-sized endurance and remaining inspection surfaces |
## Accepted coordinated circuits
@ -118,11 +119,23 @@ these legitimate aggregates into ad hoc query piles.
## Selected next bounded cut
Keep c008b stopped. Inspect the partial autonomous proposal companion family,
map the promotion transaction owner, and prove a copied-state repair that is
idempotent without accepting substituted companions. Then recover the exact
missing active process through Continuity before restarting the resident pair.
Do not build on Yggdrasil.
Keep c011 alive and braked. Source now binds every governed model-derived tool
intent to its outer worker launch and exact Substrate Gate grant. Eyes alone can
see the bounded `github_file` provider, which accepts only one GitHub repository,
exact 40-hex commit, repository path, 64 KiB UTF-8 body, no redirects, and no
proxy. Runtime recomputes its identity and bytes before a typed
`EyesSourceLookupReceipt` can share the Mind acceptance transaction. Focused
hostile substitution is byte-identical and the exact upstream README reader is
already proven from Starfire.
Package this source once on Starfire. On copied named-volume state, launch an
actual Research lane that cites the returned `sourceRef` and
`evidenceReceiptId`, accept it into Mind, and inspect the typed lookup plus Eyes
packet in the committed snapshot. Then exercise an otherwise exact worker whose
grant lacks `publicSourceRead`; require refusal before adapter/network execution
and byte-identical runtime state. Only after both gates pass may c011 be replaced.
Generic MCP transport remains transport and arbitrary URL fetching remains
absent. Do not build on Yggdrasil.
## Completion standard

View file

@ -18,12 +18,45 @@
"reasoning_effort": { "type": ["string", "null"] },
"reasoning_summary": { "type": ["string", "null"] },
"service_tier": { "type": ["string", "null"] },
"output_contract_id": { "type": ["string", "null"] }
"output_contract_id": { "type": ["string", "null"] },
"previous_response_id": { "type": ["string", "null"] },
"tools": {
"type": "array",
"items": {
"type": "object",
"required": ["name", "description", "parameters_json"],
"properties": {
"name": { "type": "string" },
"description": { "type": "string" },
"parameters_json": { "type": "string" }
},
"additionalProperties": false
}
},
"output_schema_json": { "type": ["string", "null"] },
"source_worker_job_id": { "type": ["string", "null"] }
},
"additionalProperties": false,
"$defs": {
"input_item": {
"oneOf": [
{
"type": "object",
"required": ["ToolCall"],
"properties": {
"ToolCall": {
"type": "object",
"required": ["call_id", "name", "arguments"],
"properties": {
"call_id": { "type": "string" },
"name": { "type": "string" },
"arguments": { "type": "string" }
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": ["UserText"],

View file

@ -12,7 +12,8 @@
"sessionId": { "type": "string", "minLength": 1 },
"jobId": { "type": "string", "minLength": 1 },
"provider": { "type": "string", "minLength": 1 },
"boundAt": { "type": "string", "format": "date-time" }
"boundAt": { "type": "string", "format": "date-time" },
"sourceWorkerJobId": { "type": ["string", "null"] }
},
"required": [
"schemaVersion",