Replace the runner stub with the Runtime seam and RunSpec

Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
2026-09-19 16:43:56 -07:00
parent ded7eb8c50
commit 57734ebb9a
4 changed files with 378 additions and 6 deletions
+175
View File
@@ -0,0 +1,175 @@
//! The runner seam: what `run` puts in the `RunSpec` for each tool, and what it answers. Do not
//! edit.
#[path = "support/build.rs"]
mod build;
#[path = "support/runtime.rs"]
mod runtime;
use brokerd::args::{ToolArgs, ToolName};
use brokerd::policy::{Decision, Outcome, SessionState, decide};
use brokerd::runner::{Mount, REFUSING, Refusing, RunError, RunOutput, run};
use build::{grant, now, read, request, set};
use proto::{DataClass, Mode, ToolRequest, ToolResponse};
use runtime::Recording;
fn allowed(grants: Vec<build::Build>, request: ToolRequest) -> Decision {
match decide(request, &set(grants), SessionState::default(), now()) {
Outcome::Allowed(decision) => decision,
other => panic!("the test's call is not allowed: {other:?}"),
}
}
fn mount(path: &str, writable: bool) -> Mount {
Mount {
path: path.to_string(),
writable,
}
}
#[test]
fn read_file_mounts_the_matched_path_read_only_and_has_no_network() {
let d = allowed(
vec![grant("notes", "read_file", Mode::Auto).paths(&["/h/notes", "/h/notes/deep"])],
read("/h/notes/deep/a.md"),
);
let rt = Recording::answering("text");
run(d, rt.as_ref());
let seen = rt.seen();
assert_eq!(seen.len(), 1);
assert_eq!(seen[0].tool, ToolName::ReadFile);
assert_eq!(
seen[0].arguments,
ToolArgs::ReadFile {
path: "/h/notes/deep/a.md".to_string()
}
);
// The longest path that holds the argument, and only that one.
assert_eq!(seen[0].mounts, [mount("/h/notes/deep", false)]);
assert_eq!(seen[0].egress, None);
}
#[test]
fn write_file_mounts_the_matched_path_writable() {
// A grant path equal to the argument does not count, so `/s/out` is written through `/s`.
let d = allowed(
vec![grant("s", "write_file", Mode::Auto).paths(&["/s", "/s/out"])],
request("write_file", r#"{"path":"/s/out","content":"x"}"#),
);
let rt = Recording::answering("");
run(d, rt.as_ref());
let seen = rt.seen();
assert_eq!(seen[0].tool, ToolName::WriteFile);
assert_eq!(seen[0].mounts, [mount("/s", true)]);
assert_eq!(seen[0].egress, None);
}
#[test]
fn shell_mounts_every_path_of_the_grant_writable() {
let d = allowed(
vec![grant("sh", "shell", Mode::Auto).paths(&["/a", "/b/c"])],
request("shell", r#"{"command":"ls","cwd":"/b/c/d"}"#),
);
let rt = Recording::answering("");
run(d, rt.as_ref());
let seen = rt.seen();
assert_eq!(seen[0].tool, ToolName::Shell);
assert_eq!(seen[0].mounts, [mount("/a", true), mount("/b/c", true)]);
assert_eq!(seen[0].egress, None);
}
#[test]
fn shell_without_paths_mounts_nothing() {
let d = allowed(
vec![grant("sh", "shell", Mode::Auto)],
request("shell", r#"{"command":"date"}"#),
);
let rt = Recording::answering("");
run(d, rt.as_ref());
let seen = rt.seen();
assert_eq!(seen[0].mounts, []);
assert_eq!(seen[0].egress, None);
}
#[test]
fn http_fetch_mounts_nothing_and_may_reach_the_grants_hosts_only() {
let d = allowed(
vec![grant("web", "http_fetch", Mode::Auto).hosts(&["example.com", "*.example.org"])],
request("http_fetch", r#"{"url":"https://www.example.org/x"}"#),
);
let rt = Recording::answering("");
run(d, rt.as_ref());
let seen = rt.seen();
assert_eq!(seen[0].tool, ToolName::HttpFetch);
assert_eq!(seen[0].mounts, []);
assert_eq!(
seen[0].egress,
Some(vec!["example.com".to_string(), "*.example.org".to_string()])
);
}
#[test]
fn a_result_carries_the_label_combined_over_every_matching_grant() {
// `b-keys` has the longer path and wins the mount; the label is the highest class of both
// grants, and untrusted because `a-home` says so.
let d = allowed(
vec![
grant("a-home", "read_file", Mode::Auto)
.paths(&["/home/kyle"])
.class(DataClass::Private),
grant("b-keys", "read_file", Mode::Auto)
.paths(&["/home/kyle/keys"])
.class(DataClass::Secret)
.trusted(),
],
read("/home/kyle/keys/id"),
);
let rt = Recording::with(Ok(RunOutput {
content: "key".to_string(),
truncated: true,
}));
let answer = run(d, rt.as_ref());
assert_eq!(
answer,
ToolResponse::Result {
content: "key".to_string(),
class: DataClass::Secret,
untrusted: true,
truncated: true,
}
);
assert_eq!(rt.seen()[0].mounts, [mount("/home/kyle/keys", false)]);
}
#[test]
fn a_run_error_is_a_failure_with_the_runtimes_sentence() {
for error in [
RunError::Failed("the tool timed out".to_string()),
RunError::Unavailable("the container could not start".to_string()),
] {
let d = allowed(
vec![grant("n", "read_file", Mode::Auto).paths(&["/n"])],
read("/n/a"),
);
let text = match &error {
RunError::Failed(t) | RunError::Unavailable(t) => t.clone(),
};
let rt = Recording::with(Err(error));
assert_eq!(run(d, rt.as_ref()), ToolResponse::Failed { message: text });
}
}
#[test]
fn the_production_runtime_refuses_every_call() {
assert_eq!(REFUSING, "the runner arrives in M3b");
let d = allowed(
vec![grant("n", "read_file", Mode::Auto).paths(&["/n"])],
read("/n/a"),
);
assert_eq!(
run(d, &Refusing),
ToolResponse::Failed {
message: REFUSING.to_string()
}
);
}