# 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: ```toml 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: ```rust // 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, // default: None, meaning no expiry pub secret: Option, // default: None pub constraints: Constraints, // default: all three lists empty } pub struct Constraints { // also derives Default pub paths: Vec, // default: empty pub hosts: Vec, // default: empty pub patterns: Vec, // 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::(&str) -> Result`. Only the test uses the `toml` crate, so it is a dev-dependency. `grant.rs` itself uses only serde. - `#[serde(default)]` on a field uses `Default::default()` when the key is missing. `#[serde(default = "function_name")]` calls a function in the same module instead; use it for `result_class` and `untrusted`, 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.** ```sh 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 toml = "1.1.6" ``` Add a new section at the end of `crates/proto/Cargo.toml`: ```toml [dev-dependencies] toml.workspace = true ``` Add this row to `docs/dependencies.md`: ```markdown | `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 to `lib.rs` (alphabetical order): ```rust 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`. If `cargo deny` reports a licence that is not allowed or a duplicate crate version, stop and report; do not edit `deny.toml`. - [ ] **6. Log and commit.** ```sh git add Cargo.toml Cargo.lock crates/proto docs/dependencies.md docs/implementer-log.md git commit ``` ## Done when - `cargo test -p proto --test grant` reports 4 passed. - `make gate` prints `gate: ok`. - `diff -r crates/proto/tests/fixtures/grant docs/plans/M1/files/crates/proto/tests/fixtures/grant` prints nothing. ## Stop and report if - `cargo deny` rejects `toml` or one of its dependencies.