feat: deliver exact Plugin artifacts through current Host authority

Bind remote and in-process delivery to authenticated Worker lineage, live installation permission and persisted exact pins. Verify canonical bytes and complete descriptors in a bounded Runtime-only atomic cache; cache hits reauthorize and process-crash staging never becomes visible.

Shares the exact PluginInstallation permission shape published for T-793; no HTTPS or launch/restore integration is included. Depends on the T-465 source ancestry and the shared Host execution identity helper.
This commit is contained in:
2026-10-30 04:09:12 +09:00
parent 732fe5c4c1
commit 3a13301f75
23 changed files with 3971 additions and 11 deletions
+20
View File
@@ -224,6 +224,8 @@ impl_openapi_schema!(
PluginInstallation,
PluginListResponse,
PluginOperationResponse,
PluginArtifactDeliveryRequest,
PluginArtifactDeliveryMetadata,
SubjectPermissionsResponse,
SubjectPermissionsMutationResponse,
SubjectPermissionInterruption,
@@ -3014,6 +3016,22 @@ pub trait ServerApi {
#[path] operation_id: String,
) -> Result<PluginOperationResponse, RepositoryApiError>;
#[post("/api/w/{workspace_id}/workers/self/plugin-artifacts/authorize", status = 200, error_status = 400, additional_error_statuses = [401,403,404,409,413,503], openapi = false, normalize_body_errors = true)]
async fn plugin_artifact_authorize(
&self,
#[extension] context: ServerRequestContext,
#[path] workspace_id: String,
#[body] request: PluginArtifactDeliveryRequest,
) -> Result<PluginArtifactDeliveryMetadata, RepositoryApiError>;
#[post("/api/w/{workspace_id}/workers/self/plugin-artifacts/pull", status = 200, error_status = 400, additional_error_statuses = [401,403,404,409,413,503], openapi = false, normalize_body_errors = true)]
async fn plugin_artifact_pull(
&self,
#[extension] context: ServerRequestContext,
#[path] workspace_id: String,
#[body] request: PluginArtifactDeliveryRequest,
) -> Result<BinaryBody, RepositoryApiError>;
// Ordinary operations accept member browser sessions or trusted Worker request
// context. The service resolves Worker grants; no operation accepts an actor or
// grant ID from the caller. Grant management is Workspace-owner-only.
@@ -14943,6 +14961,8 @@ mod openapi_artifact_tests {
// identity, or a one-use Reviewer capability. They remain generated Rust client/Axum
// operations, but must not appear as unauthenticated operations in the public OpenAPI.
const SIGNED_INTERNAL: &[&str] = &[
"plugin_artifact_authorize",
"plugin_artifact_pull",
"operation_authorize",
"bound_operation_authorize",
"current_worker_workspace_config_get",
+34
View File
@@ -182,3 +182,37 @@ pub struct PluginOperationResponse {
pub kind: PluginOperationKind,
pub installation: PluginInstallation,
}
/// Namespace a byte cache by the authenticated Server origin and signing key.
/// This is not a package hash or an authorization decision. Hosts derive the
/// bundle from their current registered authority, never from a delivery reply.
pub fn plugin_server_authority_identity(bundle: &crate::WorkspacePublicIdentityBundle) -> String {
serde_json::to_string(&(
bundle.backend_url.trim_end_matches('/'),
&bundle.public_key_fingerprint,
))
.expect("string tuple is serializable")
}
/// A selection within a persisted plan, never digest-only retrieval authority.
/// Workspace and Worker identities come from authenticated Host context.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
#[serde(deny_unknown_fields)]
pub struct PluginArtifactDeliveryRequest {
pub plan_id: String,
pub plan_fingerprint: String,
pub installation_id: String,
pub artifact_digest: String,
}
/// Fresh owner observation for one exact selection. Cache only bytes, not this
/// observation or the Workspace's configuration, grants or installation state.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
#[serde(deny_unknown_fields)]
pub struct PluginArtifactDeliveryMetadata {
pub workspace_id: String,
pub request: PluginArtifactDeliveryRequest,
pub artifact: PluginArtifact,
}
+53
View File
@@ -46,6 +46,8 @@ pub fn plugin_api_typescript() -> String {
PluginListResponse::decl(&config),
PluginOperationKind::decl(&config),
PluginOperationResponse::decl(&config),
PluginArtifactDeliveryRequest::decl(&config),
PluginArtifactDeliveryMetadata::decl(&config),
];
format!(
"// Generated from server-api. Do not edit by hand.\n// Regenerate: cargo run -q -p server-api --features typescript --example generate_plugin_api_types > web/workspace/src/lib/generated/plugin-api.ts\n\n{}\n",
@@ -74,6 +76,7 @@ mod tests {
assert!(text.contains("\"signed\", \"value\": string"));
assert!(text.contains("\"unsigned\", \"value\": string"));
}
use api_macros::ApiContract;
#[test]
fn plugin_routes_use_binary_upload_and_closed_management_contracts() {
let document = crate::canonical_openapi_document().unwrap();
@@ -98,6 +101,56 @@ mod tests {
assert!(value["paths"]["/api/w/{workspace_id}/plugins/{installation_id}/data"].is_null());
}
#[test]
fn artifact_delivery_is_runtime_signed_and_has_no_caller_authority_or_transport_inputs() {
for operation in ["plugin_artifact_authorize", "plugin_artifact_pull"] {
assert!(
crate::ServerApiMetadata::OPERATIONS
.iter()
.any(|entry| entry.operation_id == operation)
);
}
let document = crate::canonical_openapi_document().unwrap();
let value: serde_json::Value = serde_json::from_str(&document.to_json().unwrap()).unwrap();
for suffix in ["authorize", "pull"] {
assert!(
value["paths"]
[format!("/api/w/{{workspace_id}}/workers/self/plugin-artifacts/{suffix}")]
.is_null()
);
}
let selection = serde_json::json!({
"plan_id":"plan", "plan_fingerprint":"fingerprint",
"installation_id":"installation", "artifact_digest":"digest"
});
assert!(
serde_json::from_value::<crate::PluginArtifactDeliveryRequest>(selection.clone())
.is_ok()
);
for field in [
"workspace_id",
"worker_id",
"principal_id",
"delegation_id",
"url",
"token",
"cache_path",
"bytes",
] {
let mut invalid = selection.clone();
invalid[field] = "caller-selected".into();
assert!(
serde_json::from_value::<crate::PluginArtifactDeliveryRequest>(invalid).is_err()
);
}
assert!(
serde_json::from_value::<crate::PluginArtifactDeliveryRequest>(
serde_json::json!({"artifact_digest":"digest"})
)
.is_err()
);
}
#[cfg(feature = "typescript")]
#[test]
fn generated_plugin_types_are_current() {