fix subject authority and bounded memory browsing

This commit is contained in:
2026-10-03 01:18:24 +09:00
parent f4966b78ea
commit 13bce155aa
40 changed files with 927 additions and 174 deletions
+4
View File
@@ -298,6 +298,10 @@ pub struct CreateWorkerRequest {
/// Backend-authored immutable Workspace Memory settings snapshot.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub memory_settings: Option<manifest::WorkspaceMemorySettingsSnapshot>,
/// Backend-attested subject or consolidation attachment. A reusable Profile
/// may enable subjektiv policy, but only this host-owned bit activates it.
#[serde(default, skip_serializing_if = "is_false")]
pub subjektiv_attached: bool,
}
/// Last persisted Worker lifecycle status.
+2
View File
@@ -3102,6 +3102,7 @@ mod tests {
settings_revision: 1,
language: "English".to_string(),
}),
subjektiv_attached: false,
}
}
@@ -4081,6 +4082,7 @@ mod ws_tests {
settings_revision: 1,
language: "English".to_string(),
}),
subjektiv_attached: false,
}
}
+1
View File
@@ -5992,6 +5992,7 @@ mod tests {
settings_revision: 1,
language: "English".to_string(),
}),
subjektiv_attached: false,
}
}
+110 -18
View File
@@ -944,7 +944,7 @@ fn bind_workspace_memory_settings(
request: &CreateWorkerRequest,
) -> Result<(), String> {
let Some(snapshot) = request.memory_settings.as_ref() else {
if request.workspace_api.is_some() {
if request.workspace_api.is_some() || request.subjektiv_attached {
return Err(
"Workspace Worker request is missing its bound Memory settings snapshot"
.to_string(),
@@ -967,7 +967,12 @@ fn bind_workspace_memory_settings(
.bind_workspace_settings(snapshot.clone())
.map_err(str::to_string)?;
}
if manifest.feature.subjektiv.profile.enabled {
if request.subjektiv_attached {
if !manifest.feature.subjektiv.profile.enabled {
return Err(
"subject-attached Worker profile does not enable subjektiv policy".to_string(),
);
}
manifest
.feature
.subjektiv
@@ -992,6 +997,12 @@ fn validate_worker_memory_settings(
request: &CreateWorkerRequest,
) -> Result<(), String> {
let Some(expected) = request.memory_settings.as_ref() else {
if request.subjektiv_attached {
return Err(
"subject-attached Worker restore is missing its trusted settings snapshot"
.to_string(),
);
}
return Ok(());
};
manifest
@@ -1004,30 +1015,50 @@ fn validate_worker_memory_settings(
.subjektiv
.validate_execution()
.map_err(str::to_string)?;
for (feature, enabled, actual) in [
(
"Memory",
manifest.feature.memory.profile.enabled,
manifest.feature.memory.workspace_settings(),
),
(
"subjektiv",
manifest.feature.subjektiv.profile.enabled,
manifest.feature.subjektiv.workspace_settings(),
),
] {
if enabled && actual.as_ref() != Some(expected) {
let actual = actual.ok_or_else(|| {
format!("Workspace Worker restored without its bound {feature} settings snapshot")
if manifest.feature.memory.profile.enabled
&& manifest.feature.memory.workspace_settings().as_ref() != Some(expected)
{
let actual = manifest
.feature
.memory
.workspace_settings()
.ok_or_else(|| {
"Workspace Worker restored without its bound Memory settings snapshot".to_string()
})?;
return Err(format!(
"Workspace Worker Memory settings snapshot mismatch: expected {} revision {}, restored {} revision {}",
expected.workspace_id,
expected.settings_revision,
actual.workspace_id,
actual.settings_revision
));
}
let subjektiv_settings = manifest.feature.subjektiv.workspace_settings();
if request.subjektiv_attached {
if !manifest.feature.subjektiv.profile.enabled {
return Err(
"subject-attached Worker restored with a Profile that disables subjektiv policy"
.to_string(),
);
}
if subjektiv_settings.as_ref() != Some(expected) {
let actual = subjektiv_settings.ok_or_else(|| {
"subject-attached Worker restored without its bound subjektiv settings snapshot"
.to_string()
})?;
return Err(format!(
"Workspace Worker {feature} settings snapshot mismatch: expected {} revision {}, restored {} revision {}",
"Workspace Worker subjektiv settings snapshot mismatch: expected {} revision {}, restored {} revision {}",
expected.workspace_id,
expected.settings_revision,
actual.workspace_id,
actual.settings_revision
));
}
} else if subjektiv_settings.is_some() {
return Err(
"ordinary Workspace Worker restored with an unauthorized subjektiv attachment"
.to_string(),
);
}
Ok(())
}
@@ -4412,9 +4443,70 @@ mod tests {
worker_observation_grants: Vec::new(),
workspace_api: None,
memory_settings: None,
subjektiv_attached: false,
}
}
#[test]
fn host_attachment_alone_activates_subjektiv_profile_policy() {
let manifest = || {
let mut manifest = WorkerManifest::from_toml(
r#"
[worker]
name = "subjektiv-attachment-test"
pwd = "./"
[model]
scheme = "anthropic"
model_id = "test-model"
auth = { kind = "none" }
[engine]
max_tokens = 100
[[scope.allow]]
target = "./"
permission = "read"
"#,
)
.unwrap();
manifest.feature.subjektiv.profile.enabled = true;
manifest.feature.subjektiv.profile.extraction.enabled = true;
manifest
};
let settings = manifest::WorkspaceMemorySettingsSnapshot {
workspace_id: "workspace-subject".to_string(),
settings_revision: 3,
language: "English".to_string(),
};
let mut ordinary_request = create_request("ordinary");
ordinary_request.workspace_api = Some(WorkspaceApiRef {
workspace_id: settings.workspace_id.clone(),
base_url: "http://workspace.invalid".to_string(),
});
ordinary_request.memory_settings = Some(settings.clone());
let mut ordinary = manifest();
bind_workspace_memory_settings(&mut ordinary, &ordinary_request).unwrap();
assert!(ordinary.feature.subjektiv.profile.enabled);
assert!(!ordinary.feature.subjektiv.execution_enabled());
validate_worker_memory_settings(&ordinary, &ordinary_request).unwrap();
let mut attached_request = ordinary_request.clone();
attached_request.subjektiv_attached = true;
let mut attached = manifest();
bind_workspace_memory_settings(&mut attached, &attached_request).unwrap();
assert!(attached.feature.subjektiv.execution_enabled());
assert_eq!(
attached.feature.subjektiv.workspace_settings(),
Some(settings.clone())
);
validate_worker_memory_settings(&attached, &attached_request).unwrap();
assert!(validate_worker_memory_settings(&attached, &ordinary_request).is_err());
attached_request.memory_settings = None;
assert!(bind_workspace_memory_settings(&mut manifest(), &attached_request).is_err());
}
fn git(path: &std::path::Path, args: &[&str]) {
let status = Command::new("git")
.arg("-C")