diff --git a/crates/workspace-server/src/server/subject_authorization.rs b/crates/workspace-server/src/server/subject_authorization.rs index ba6ba4786..1cac9095e 100644 --- a/crates/workspace-server/src/server/subject_authorization.rs +++ b/crates/workspace-server/src/server/subject_authorization.rs @@ -314,6 +314,37 @@ pub(super) fn worker_permissions( )) } +/// Host-only identity shared by plan creation and live Plugin operations. The +/// identity is an immutable ceiling, not a permission: recheck the live lineage +/// on every use, including byte delivery and cache hits. +pub(super) fn plugin_execution_identity( + api: &WorkspaceApi, + worker: &RuntimeWorkerRef, +) -> ApiResult { + worker_permissions(api, worker)?; + let (principal_id, delegation_id) = if let Some(grant) = live_child_grant(api, worker)? { + ( + format!("subject:{}", grant.subject_id), + Some(grant.source_change_id), + ) + } else if let Some(subject) = current_body_subject(api, worker)? { + (format!("subject:{subject}"), None) + } else if let Some(subject) = job_subject(api, worker)? { + (format!("subject:{subject}"), None) + } else { + ( + format!("worker:{}/{}", worker.runtime_id, worker.worker_id), + None, + ) + }; + Ok(crate::plugin_plan::HostExecutionIdentity { + runtime_id: worker.runtime_id.clone(), + worker_id: worker.worker_id.clone(), + principal_id, + delegation_id, + }) +} + pub(super) fn authorize( api: &WorkspaceApi, worker: &RuntimeWorkerRef,