brokerd: recall and memory, decided under built-in grants

Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
2026-09-25 16:41:44 -07:00
committed by kyle
parent 0c8d136c6b
commit fcfd590687
10 changed files with 267 additions and 9 deletions
+45
View File
@@ -0,0 +1,45 @@
//! The built-in grants of `recall` and `memory` (M5a spec, section 8). Reading and writing memory
//! is what an agent is, so no grant file can turn it off or put questions in front of it; the
//! prompt is protected by `memoryd`, which lets only clean sessions edit identity.
use proto::{Grant, GrantProblem, sha256};
use crate::grants::LoadedGrant;
/// Ids starting with this are the built-ins'; an owner's grant file may not use one.
pub const RESERVED: &str = "builtin-";
/// The built-in grants' text, hashed for the audit log like a grant file's bytes.
pub const RECALL: &str = "tool = \"recall\"\nmode = \"auto\"\nmax_taint = \"secret\"\nresult_class = \"private\"\nuntrusted = false\n";
pub const MEMORY: &str = "tool = \"memory\"\nmode = \"auto\"\nmax_taint = \"secret\"\nresult_class = \"private\"\nuntrusted = false\n";
/// The sentence for an owner's grant naming a memory tool.
pub const BUILT_IN: &str = "the policy of recall and memory is built in; remove this grant";
/// The two built-in grants, `builtin-memory` and `builtin-recall`.
pub fn grants() -> Result<Vec<LoadedGrant>, GrantProblem> {
// For each (id, text) of [("builtin-memory", MEMORY), ("builtin-recall", RECALL)]:
// `toml::from_str::<Grant>(text)` and `sha256(text.as_bytes())`, each error as a GrantProblem {
// file: id, line: None, problem: e.to_string() }; build LoadedGrant { id, grant, sha256 }.
// Collect into Result<Vec<LoadedGrant>, GrantProblem> (the first error wins).
let sources: &[(&str, &str)] = &[("builtin-memory", MEMORY), ("builtin-recall", RECALL)];
let mut out: Vec<LoadedGrant> = Vec::new();
for (id, text) in sources {
let grant = toml::from_str::<Grant>(text).map_err(|error| GrantProblem {
file: id.to_string(),
line: None,
problem: error.to_string(),
})?;
let sha256 = sha256(text.as_bytes()).map_err(|error| GrantProblem {
file: id.to_string(),
line: None,
problem: error.to_string(),
})?;
out.push(LoadedGrant {
id: id.to_string(),
grant,
sha256,
});
}
Ok(out)
}