use serde::Serialize;
use crate::sandbox::EnforcementTier;
pub const SESSION_ID_PLACEHOLDER: &str = "<session-id>";
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum RuntimeWriteKind {
File,
Directory,
}
impl RuntimeWriteKind {
#[must_use]
pub fn wire_name(self) -> &'static str {
match self {
Self::File => "file",
Self::Directory => "directory",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum RuntimeWriteScope {
Accessible,
Session,
}
impl RuntimeWriteScope {
#[must_use]
pub fn wire_name(self) -> &'static str {
match self {
Self::Accessible => "accessible",
Self::Session => "session",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum RuntimeWriteCondition {
Always,
WorkdirProvided,
AgentSession,
ScriptSession,
Shell,
PeerFetch,
Spawn,
Delegated,
Sealed,
}
impl RuntimeWriteCondition {
#[must_use]
pub fn wire_name(self) -> &'static str {
match self {
Self::Always => "always",
Self::WorkdirProvided => "workdir-provided",
Self::AgentSession => "agent-session",
Self::ScriptSession => "script-session",
Self::Shell => "shell",
Self::PeerFetch => "peer-fetch",
Self::Spawn => "spawn",
Self::Delegated => "delegated",
Self::Sealed => "sealed",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct RuntimeWriteReport {
pub path: String,
pub kind: RuntimeWriteKind,
pub scope: RuntimeWriteScope,
pub condition: RuntimeWriteCondition,
}
struct DeclaredWrite {
entry: &'static str,
kind: RuntimeWriteKind,
scope: RuntimeWriteScope,
condition: RuntimeWriteCondition,
}
impl DeclaredWrite {
fn report(&self) -> RuntimeWriteReport {
let path = match self.scope {
RuntimeWriteScope::Accessible => self.entry.to_string(),
RuntimeWriteScope::Session => {
format!(".murmur/{SESSION_ID_PLACEHOLDER}/{}", self.entry)
}
};
RuntimeWriteReport {
path,
kind: self.kind,
scope: self.scope,
condition: self.condition,
}
}
#[cfg(test)]
fn first_segment(&self) -> &'static str {
self.entry.split('/').next().unwrap_or(self.entry)
}
}
const DECLARED_WRITES: &[DeclaredWrite] = &[
DeclaredWrite {
entry: ".murmur/<session-id>",
kind: RuntimeWriteKind::Directory,
scope: RuntimeWriteScope::Accessible,
condition: RuntimeWriteCondition::Always,
},
DeclaredWrite {
entry: "task.md",
kind: RuntimeWriteKind::File,
scope: RuntimeWriteScope::Accessible,
condition: RuntimeWriteCondition::Always,
},
DeclaredWrite {
entry: "murmur.yaml",
kind: RuntimeWriteKind::File,
scope: RuntimeWriteScope::Accessible,
condition: RuntimeWriteCondition::WorkdirProvided,
},
DeclaredWrite {
entry: "logs",
kind: RuntimeWriteKind::Directory,
scope: RuntimeWriteScope::Accessible,
condition: RuntimeWriteCondition::Shell,
},
DeclaredWrite {
entry: "peer-in",
kind: RuntimeWriteKind::Directory,
scope: RuntimeWriteScope::Accessible,
condition: RuntimeWriteCondition::PeerFetch,
},
DeclaredWrite {
entry: ".murmur/children",
kind: RuntimeWriteKind::Directory,
scope: RuntimeWriteScope::Accessible,
condition: RuntimeWriteCondition::Spawn,
},
DeclaredWrite {
entry: "out",
kind: RuntimeWriteKind::Directory,
scope: RuntimeWriteScope::Accessible,
condition: RuntimeWriteCondition::ScriptSession,
},
DeclaredWrite {
entry: "completion.json",
kind: RuntimeWriteKind::File,
scope: RuntimeWriteScope::Accessible,
condition: RuntimeWriteCondition::Delegated,
},
DeclaredWrite {
entry: "tools",
kind: RuntimeWriteKind::Directory,
scope: RuntimeWriteScope::Session,
condition: RuntimeWriteCondition::Always,
},
DeclaredWrite {
entry: "trace.jsonl",
kind: RuntimeWriteKind::File,
scope: RuntimeWriteScope::Session,
condition: RuntimeWriteCondition::Always,
},
DeclaredWrite {
entry: "blobs",
kind: RuntimeWriteKind::Directory,
scope: RuntimeWriteScope::Session,
condition: RuntimeWriteCondition::Always,
},
DeclaredWrite {
entry: "logs",
kind: RuntimeWriteKind::Directory,
scope: RuntimeWriteScope::Session,
condition: RuntimeWriteCondition::AgentSession,
},
DeclaredWrite {
entry: "out",
kind: RuntimeWriteKind::Directory,
scope: RuntimeWriteScope::Session,
condition: RuntimeWriteCondition::AgentSession,
},
DeclaredWrite {
entry: ".process-driver",
kind: RuntimeWriteKind::Directory,
scope: RuntimeWriteScope::Session,
condition: RuntimeWriteCondition::AgentSession,
},
DeclaredWrite {
entry: "MURMUR.md",
kind: RuntimeWriteKind::File,
scope: RuntimeWriteScope::Session,
condition: RuntimeWriteCondition::AgentSession,
},
DeclaredWrite {
entry: "plans",
kind: RuntimeWriteKind::Directory,
scope: RuntimeWriteScope::Session,
condition: RuntimeWriteCondition::AgentSession,
},
DeclaredWrite {
entry: ".capsule-home",
kind: RuntimeWriteKind::Directory,
scope: RuntimeWriteScope::Session,
condition: RuntimeWriteCondition::Shell,
},
DeclaredWrite {
entry: ".mur-tmp",
kind: RuntimeWriteKind::Directory,
scope: RuntimeWriteScope::Session,
condition: RuntimeWriteCondition::Sealed,
},
DeclaredWrite {
entry: ".mur-etc",
kind: RuntimeWriteKind::Directory,
scope: RuntimeWriteScope::Session,
condition: RuntimeWriteCondition::Sealed,
},
];
pub(crate) fn runtime_writes(tier: EnforcementTier) -> Vec<RuntimeWriteReport> {
let sealed = tier == EnforcementTier::KernelSealed;
DECLARED_WRITES
.iter()
.filter(|declared| sealed || declared.condition != RuntimeWriteCondition::Sealed)
.map(DeclaredWrite::report)
.collect()
}
#[must_use]
pub fn render_runtime_writes(writes: &[RuntimeWriteReport]) -> String {
let mut out = String::new();
out.push_str(
" paths are relative to the accessible workdir; `<session-id>` is a literal segment, \
replaced by the run's own session id\n",
);
if writes.is_empty() {
out.push_str(" runtime writes: <none>\n");
return out;
}
out.push_str(" runtime writes:\n");
for write in writes {
out.push_str(&format!(
" - {} ({}, {}, {})\n",
write.path,
write.kind.wire_name(),
write.scope.wire_name(),
write.condition.wire_name()
));
}
out
}
#[cfg(test)]
mod tests {
use super::*;
const SCANNED: &[(&str, &str)] = &[
("agent.rs", include_str!("agent.rs")),
("agent/inventory.rs", include_str!("agent/inventory.rs")),
("agent/process.rs", include_str!("agent/process.rs")),
("child_launch.rs", include_str!("child_launch.rs")),
("delegation.rs", include_str!("delegation.rs")),
("hooks.rs", include_str!("hooks.rs")),
("murmur_md.rs", include_str!("murmur_md.rs")),
("otel.rs", include_str!("otel.rs")),
("plan.rs", include_str!("plan.rs")),
("protected_paths.rs", include_str!("protected_paths.rs")),
("recipes.rs", include_str!("recipes.rs")),
("resource_plane.rs", include_str!("resource_plane.rs")),
("runtime.rs", include_str!("runtime.rs")),
("sandbox.rs", include_str!("sandbox.rs")),
("sealed.rs", include_str!("sealed.rs")),
("shell.rs", include_str!("shell.rs")),
("tool_annotations.rs", include_str!("tool_annotations.rs")),
("trace.rs", include_str!("trace.rs")),
("trace_blobs.rs", include_str!("trace_blobs.rs")),
];
const CONSTANTS: &[(&str, &str)] = &[
("MANIFEST_FILENAME", murmur_artifact::MANIFEST_FILENAME),
("COMPLETION_FILE", crate::delegation::COMPLETION_FILE),
("BLOB_DIR_NAME", crate::trace_blobs::BLOB_DIR_NAME),
("SEALED_TMP_DIR_NAME", crate::sealed::SEALED_TMP_DIR_NAME),
(
"SEALED_ETC_STAGING_DIR_NAME",
crate::sealed::SEALED_ETC_STAGING_DIR_NAME,
),
(
"SYNTHETIC_HOME_DIR_NAME",
crate::shell::SYNTHETIC_HOME_DIR_NAME,
),
("FILES_DIR_NAME", crate::agent::process::FILES_DIR_NAME),
];
const EXEMPT: &[(&str, &str)] = &[
(
"input.txt",
"Read, never written: the fallback name `read_task` tries after `task.md`. Source \
text cannot tell a read from a write, so a path the runtime only ever opens for \
reading is exempted here rather than declared as something the runtime produces.",
),
(
"package.json",
"Read, never written: the recipe detector opens it to learn which scripts a workdir \
already declares. Exempt on `input.txt`'s terms.",
),
];
const DYNAMIC_EXEMPT: &[(&str, &str, &str)] = &[
(
"protected_paths.rs",
"&rule.relative",
"A `capabilities.filesystem.read_only` path from the manifest, resolved to decide \
whether a call is refused. The runtime reads it and writes nothing.",
),
(
"protected_paths.rs",
"&lexical",
"The lexically normalised form of a path a tool call named, resolved for the same \
refusal decision.",
),
(
"recipes.rs",
"dir",
"A directory name from the recipe detector's own candidate list, opened to see \
whether the workdir has it.",
),
(
"resource_plane.rs",
"declared_root",
"The `exports.files` / `exports.peer_files` root a manifest declares. The plane \
serves what the capsule put there; it creates nothing of its own.",
),
(
"resource_plane.rs",
"&export.root",
"The same declared root, resolved when a request is served. Exempt on \
`declared_root`'s terms.",
),
(
"runtime.rs",
"&relative",
"A resource path a tool call named, resolved to check it is inside the workdir \
before the file is read.",
),
(
"runtime.rs",
"&stored_path",
"Where a redeemed peer handle's bytes go, composed by `peer_handoff::stored_path_for` \
under `peer-in/`. Declared through that directory's own entry, which is the name a \
consumer excludes.",
),
(
"shell.rs",
"&output_path",
"A demoted command's own log path, `logs/<work-id>.log`, composed by \
`detached::output_path_for` and joined here only to measure the file's size. \
Declared through the `logs` entry.",
),
(
"delegation.rs",
"format!( \".{COMPLETION_FILE}.{}.tmp\", uuid::Uuid::now_v7().simple() )",
"The temporary file `completion.json` is written through and renamed from. It exists \
only between two syscalls in the same function, so a consumer diffing a finished \
run never sees it.",
),
];
fn production_text(source: &str) -> &str {
match source.find("#[cfg(test)]\nmod tests {") {
Some(index) => &source[..index],
None => source,
}
}
fn workdir_join_arguments(source: &str) -> Vec<String> {
let mut found = Vec::new();
let bytes = source.as_bytes();
let mut cursor = 0;
while let Some(offset) = source[cursor..].find(".join(") {
let start = cursor + offset;
cursor = start + ".join(".len();
if !source[..start].trim_end().ends_with("workdir") {
continue;
}
let mut depth = 1usize;
let mut index = cursor;
while index < bytes.len() && depth > 0 {
match bytes[index] {
b'(' => depth += 1,
b')' => depth -= 1,
_ => {}
}
index += 1;
}
assert!(
depth == 0,
"unbalanced parentheses after a join into a workdir"
);
found.push(
source[cursor..index - 1]
.split_whitespace()
.collect::<Vec<_>>()
.join(" "),
);
}
found
}
fn resolve_entry_name(argument: &str) -> Option<String> {
if let Some(rest) = argument.strip_prefix('"') {
return rest.split_once('"').map(|(literal, _)| literal.to_string());
}
let last = argument.rsplit("::").next().unwrap_or(argument);
let is_constant_name = !last.is_empty()
&& last
.chars()
.all(|character| character.is_ascii_uppercase() || character == '_');
if !is_constant_name {
return None;
}
Some(
CONSTANTS
.iter()
.find(|(name, _)| *name == last)
.unwrap_or_else(|| {
panic!(
"a workdir.join({argument}) names the constant {last}, which this test's \
CONSTANTS table does not pair with a value. Add \
(\"{last}\", <path to the constant>) so a rename or a value change is \
caught here rather than silently accepted."
)
})
.1
.to_string(),
)
}
#[test]
fn every_workdir_write_is_declared_or_exempted() {
let declared: Vec<&str> = DECLARED_WRITES
.iter()
.map(DeclaredWrite::first_segment)
.collect();
let mut scanned = 0usize;
for (file, source) in SCANNED {
for argument in workdir_join_arguments(production_text(source)) {
scanned += 1;
let Some(entry) = resolve_entry_name(&argument) else {
assert!(
DYNAMIC_EXEMPT
.iter()
.any(|(exempt_file, exempt_argument, _)| exempt_file == file
&& *exempt_argument == argument),
"{file} writes workdir.join({argument}) into a workdir, and the argument \
is neither a string literal nor a named constant, so this test cannot \
say what it names. Add ({file:?}, {argument:?}, <written reason>) to \
this test's DYNAMIC_EXEMPT table."
);
continue;
};
assert!(
declared.contains(&entry.as_str())
|| EXEMPT.iter().any(|(name, _)| *name == entry),
"{file} writes `{entry}` into a workdir, and no --explain-scope report \
declares it. Either add an entry for `{entry}` to DECLARED_WRITES in \
workdir_writes.rs, so a consumer diffing the workdir can subtract it, or add \
it to this test's EXEMPT table with a written reason — a path the runtime \
only reads is the usual one."
);
}
}
assert!(
scanned >= 50,
"the workdir.join scan found only {scanned} occurrences across {} files, far below \
what the runtime has — the scanner is broken, not the runtime",
SCANNED.len()
);
for (name, _) in EXEMPT {
assert!(
!declared.contains(name),
"`{name}` is listed as exempt and is also declared — remove one"
);
}
}
#[test]
fn every_source_file_that_joins_a_workdir_is_scanned() {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
let mut stack = vec![root.clone()];
let mut checked = 0usize;
while let Some(dir) = stack.pop() {
let entries = std::fs::read_dir(&dir)
.unwrap_or_else(|error| panic!("reading {}: {error}", dir.display()));
for entry in entries {
let path = entry.unwrap().path();
if path.is_dir() {
stack.push(path);
continue;
}
if path.extension().is_some_and(|extension| extension == "rs") {
checked += 1;
if path
.file_name()
.is_some_and(|name| name == "workdir_writes.rs")
{
continue;
}
let source = std::fs::read_to_string(&path).unwrap();
if workdir_join_arguments(production_text(&source)).is_empty() {
continue;
}
let relative = path
.strip_prefix(&root)
.unwrap()
.to_string_lossy()
.replace('\\', "/");
assert!(
SCANNED.iter().any(|(file, _)| *file == relative),
"src/{relative} writes into a workdir but is not in \
every_workdir_write_is_declared_or_exempted's SCANNED list, so nothing \
checks its entries against the declaration. Add \
(\"{relative}\", include_str!(\"{relative}\")) to it."
);
}
}
}
assert!(
checked >= 40,
"walked only {checked} source files under src/ — the walk is broken"
);
}
#[test]
fn the_sealed_tier_adds_exactly_the_two_sealed_session_entries() {
let scoped = runtime_writes(EnforcementTier::KernelFull);
let sealed = runtime_writes(EnforcementTier::KernelSealed);
assert!(
scoped.iter().all(|write| sealed.contains(write)),
"the sealed declaration must be a superset of the scoped one"
);
let added: Vec<&RuntimeWriteReport> = sealed
.iter()
.filter(|write| !scoped.contains(write))
.collect();
assert_eq!(
added
.iter()
.map(|write| write.path.as_str())
.collect::<Vec<_>>(),
vec![
".murmur/<session-id>/.mur-tmp",
".murmur/<session-id>/.mur-etc"
]
);
for write in added {
assert_eq!(write.condition, RuntimeWriteCondition::Sealed);
assert_eq!(write.scope, RuntimeWriteScope::Session);
assert_eq!(write.kind, RuntimeWriteKind::Directory);
}
}
#[test]
fn only_the_sealed_tier_changes_the_declared_set() {
let baseline = runtime_writes(EnforcementTier::EnvironmentOnly);
assert_eq!(baseline, runtime_writes(EnforcementTier::KernelSeccompOnly));
assert_eq!(baseline, runtime_writes(EnforcementTier::KernelFull));
assert!(baseline
.iter()
.all(|write| write.condition != RuntimeWriteCondition::Sealed));
}
#[test]
fn a_serialized_row_carries_exactly_path_kind_scope_and_condition() {
for write in runtime_writes(EnforcementTier::KernelSealed) {
let line = serde_json::to_string(&write).unwrap();
assert_eq!(
line,
format!(
r#"{{"path":"{}","kind":"{}","scope":"{}","condition":"{}"}}"#,
write.path,
write.kind.wire_name(),
write.scope.wire_name(),
write.condition.wire_name()
)
);
}
}
#[test]
fn session_scoped_paths_carry_the_murmur_prefix_and_the_literal_session_id() {
for write in runtime_writes(EnforcementTier::KernelSealed) {
match write.scope {
RuntimeWriteScope::Session => {
let prefix = format!(".murmur/{SESSION_ID_PLACEHOLDER}/");
assert!(
write.path.starts_with(&prefix),
"{} is session-scoped but not under {prefix}",
write.path
);
}
RuntimeWriteScope::Accessible => assert!(
!write
.path
.starts_with(&format!(".murmur/{SESSION_ID_PLACEHOLDER}/")),
"{} is accessible-scoped but sits inside the session directory",
write.path
),
}
assert!(
!write.path.contains("ses_"),
"{} carries a real session id where the placeholder belongs",
write.path
);
}
}
#[test]
fn no_declared_path_is_reported_twice() {
let writes = runtime_writes(EnforcementTier::KernelSealed);
let mut paths: Vec<&str> = writes.iter().map(|write| write.path.as_str()).collect();
paths.sort_unstable();
let count = paths.len();
paths.dedup();
assert_eq!(paths.len(), count, "a path is declared twice");
}
#[test]
fn the_rendering_names_every_declared_path_and_the_placeholder_rule() {
let writes = runtime_writes(EnforcementTier::KernelSealed);
let rendered = render_runtime_writes(&writes);
assert!(rendered.contains("`<session-id>` is a literal segment"));
for write in &writes {
assert!(
rendered.contains(&write.path),
"{} is missing from the rendering",
write.path
);
}
assert!(rendered.ends_with('\n'));
}
}