Seven task files for the implementing model under docs/plans/M1/, with the test files, byte-exact fixtures, Makefile, deny.toml and gate-script self-test they copy into place. All of it was verified against a private reference implementation: the gate passes after every task in order. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
5.1 KiB
M1 task 05: proto grant files
Branch: m1 (run git switch m1; git status --short must be empty, otherwise stop)
Commit subject: Add Grant, Mode and Constraints to proto
Goal
Add the data type for a grant file. A grant is a TOML file the owner writes to allow one kind of
tool call. This task only parses grants. Deciding whether a grant matches a request is brokerd's
job in a later milestone; write no matching logic.
Context
From the design brief: "A grant is a file the owner writes: tool, argument constraints (paths,
hosts, patterns), allowed data classes, expiry, mode (auto, ask, deny)."
From the spec: max_taint is "the highest session taint under which the grant applies".
result_class is the data class of the tool's results and defaults to private. untrusted
says whether results may have been written by someone other than the owner and defaults to true.
A typing mistake in a grant file must be an error, never silently ignored: a misspelt
max_tiant that fell back to a default would widen what the grant allows.
A full grant file:
tool = "http_fetch"
mode = "ask"
max_taint = "secret"
result_class = "public"
untrusted = false
expires = "2026-12-31T00:00:00.000Z"
secret = "example-api-token"
[constraints]
paths = ["/home/kyle/notes/**"]
hosts = ["example.com", "api.example.com"]
patterns = ["^GET "]
Files
- Copy:
crates/proto/tests/grant.rs,crates/proto/tests/fixtures/grant/(6 files) - Create:
crates/proto/src/grant.rs - Modify:
Cargo.toml,crates/proto/Cargo.toml,crates/proto/src/lib.rs,docs/dependencies.md,docs/implementer-log.md,Cargo.lock(generated)
Interfaces
Consumes from task 02: proto::DataClass, proto::Timestamp.
Produces, in crates/proto/src/grant.rs, re-exported from the crate root:
// TOML and JSON: "auto", "ask", "deny"
pub enum Mode { Auto, Ask, Deny }
pub struct Grant {
pub tool: String, // required
pub mode: Mode, // required
pub max_taint: DataClass, // required
pub result_class: DataClass, // default: DataClass::Private
pub untrusted: bool, // default: true
pub expires: Option<Timestamp>, // default: None, meaning no expiry
pub secret: Option<String>, // default: None
pub constraints: Constraints, // default: all three lists empty
}
pub struct Constraints { // also derives Default
pub paths: Vec<String>, // default: empty
pub hosts: Vec<String>, // default: empty
pub patterns: Vec<String>, // default: empty
}
Derives: all three get Debug, Clone, PartialEq, Eq, Serialize, Deserialize. Mode also gets
Copy. Constraints also gets Default.
Rules the tests check: tool, mode and max_taint are required. Unknown keys are errors in the
grant and in [constraints]. An unknown mode is an error. expires is written as a quoted string
in the Timestamp spelling from task 02, not as a bare TOML date.
API notes (verified on docs.rs, 2026-09-17)
toml::from_str::<T>(&str) -> Result<T, toml::de::Error>. Only the test uses thetomlcrate, so it is a dev-dependency.grant.rsitself uses only serde.#[serde(default)]on a field usesDefault::default()when the key is missing.#[serde(default = "function_name")]calls a function in the same module instead; use it forresult_classanduntrusted, whose defaults are not the types'Default.#[serde(deny_unknown_fields)]and#[serde(rename_all = "lowercase")]as in earlier tasks.
Steps
- 1. Copy the test and fixtures, add the dev-dependency.
git switch m1
cp docs/plans/M1/files/crates/proto/tests/grant.rs crates/proto/tests/
cp -r docs/plans/M1/files/crates/proto/tests/fixtures/grant crates/proto/tests/fixtures/
Add to [workspace.dependencies] in the root Cargo.toml:
toml = "1.1.6"
Add a new section at the end of crates/proto/Cargo.toml:
[dev-dependencies]
toml.workspace = true
Add this row to docs/dependencies.md:
| `toml` | 1.1.6 | `proto` tests; `brokerd` and `bxctl` from M3 | Parses grant files. MIT OR Apache-2.0. |
-
2. See the test fail.
cargo test -p proto --test grant. Expected: it does not compile. -
3. Write
grant.rs, and add tolib.rs(alphabetical order):
pub mod grant;
pub use grant::{Constraints, Grant, Mode};
-
4. See the test pass.
cargo test -p proto --test grant. Expected:4 passed; 0 failed. -
5. Run the gate.
make gate. Expected last line:gate: ok. Ifcargo denyreports a licence that is not allowed or a duplicate crate version, stop and report; do not editdeny.toml. -
6. Log and commit.
git add Cargo.toml Cargo.lock crates/proto docs/dependencies.md docs/implementer-log.md
git commit
Done when
cargo test -p proto --test grantreports 4 passed.make gateprintsgate: ok.diff -r crates/proto/tests/fixtures/grant docs/plans/M1/files/crates/proto/tests/fixtures/grantprints nothing.
Stop and report if
cargo denyrejectstomlor one of its dependencies.