Add Grant, Mode and Constraints to proto
Implemented-By: Laguna S 2.1 (OpenCode)
This commit is contained in:
Generated
+77
@@ -16,6 +16,12 @@ dependencies = [
|
||||
"proto",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "gatewayd"
|
||||
version = "0.1.0"
|
||||
@@ -23,12 +29,28 @@ dependencies = [
|
||||
"proto",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inferproxy"
|
||||
version = "0.1.0"
|
||||
@@ -71,6 +93,7 @@ dependencies = [
|
||||
"humantime",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -125,6 +148,15 @@ dependencies = [
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "3.0.6"
|
||||
@@ -136,6 +168,45 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "1.1.6+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde_core",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_parser",
|
||||
"toml_writer",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "1.1.1+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.1.3+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
|
||||
|
||||
[[package]]
|
||||
name = "toolkit"
|
||||
version = "0.1.0"
|
||||
@@ -149,6 +220,12 @@ version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.23"
|
||||
|
||||
@@ -23,4 +23,5 @@ proto = { path = "crates/proto" }
|
||||
serde = { version = "1.0.229", features = ["derive"] }
|
||||
serde_json = "1.0.151"
|
||||
humantime = "2.4.0"
|
||||
toml = "1.1.6"
|
||||
|
||||
|
||||
@@ -13,3 +13,6 @@ serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
humantime.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
toml.workspace = true
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
//! Grant file schema. A grant is TOML written by the owner to allow one kind of tool call.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{DataClass, Timestamp};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Mode {
|
||||
Auto,
|
||||
Ask,
|
||||
Deny,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase", deny_unknown_fields)]
|
||||
pub struct Constraints {
|
||||
#[serde(default)]
|
||||
pub paths: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub hosts: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub patterns: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Grant {
|
||||
pub tool: String,
|
||||
pub mode: Mode,
|
||||
pub max_taint: DataClass,
|
||||
#[serde(default = "default_result_class")]
|
||||
pub result_class: DataClass,
|
||||
#[serde(default = "default_untrusted")]
|
||||
pub untrusted: bool,
|
||||
#[serde(default)]
|
||||
pub expires: Option<Timestamp>,
|
||||
#[serde(default)]
|
||||
pub secret: Option<String>,
|
||||
#[serde(default)]
|
||||
pub constraints: Constraints,
|
||||
}
|
||||
|
||||
fn default_result_class() -> DataClass {
|
||||
DataClass::Private
|
||||
}
|
||||
|
||||
fn default_untrusted() -> bool {
|
||||
true
|
||||
}
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
pub mod class;
|
||||
pub mod frame;
|
||||
pub mod grant;
|
||||
pub mod ids;
|
||||
pub mod wire;
|
||||
|
||||
pub use class::DataClass;
|
||||
pub use frame::{FrameError, MAX_FRAME, read_frame, write_frame};
|
||||
pub use grant::{Constraints, Grant, Mode};
|
||||
pub use ids::{CallId, Epoch, Hash32, SessionId, Timestamp, ValueError};
|
||||
pub use wire::{
|
||||
DenyReason, Envelope, ErrorCode, Message, PROTOCOL_VERSION, ToolRequest, ToolResponse,
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
tool = "read_file"
|
||||
mode = "always"
|
||||
max_taint = "private"
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
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 "]
|
||||
@@ -0,0 +1,3 @@
|
||||
tool = "read_file"
|
||||
mode = "auto"
|
||||
max_taint = "private"
|
||||
@@ -0,0 +1,2 @@
|
||||
tool = "read_file"
|
||||
mode = "auto"
|
||||
@@ -0,0 +1,6 @@
|
||||
tool = "read_file"
|
||||
mode = "auto"
|
||||
max_taint = "private"
|
||||
|
||||
[constraints]
|
||||
path = ["/etc/**"]
|
||||
@@ -0,0 +1,4 @@
|
||||
tool = "read_file"
|
||||
mode = "auto"
|
||||
max_taint = "private"
|
||||
max_tiant = "secret"
|
||||
@@ -0,0 +1,67 @@
|
||||
//! Tests for grant files. Do not edit these or the fixtures.
|
||||
|
||||
use proto::{Constraints, DataClass, Grant, Mode, Timestamp};
|
||||
|
||||
fn parse(name: &str) -> Result<Grant, toml::de::Error> {
|
||||
let path = format!("{}/tests/fixtures/grant/{name}", env!("CARGO_MANIFEST_DIR"));
|
||||
let text = std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("{path}: {e}"));
|
||||
toml::from_str(&text)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn minimal_grant_gets_safe_defaults() {
|
||||
let want = Grant {
|
||||
tool: "read_file".to_string(),
|
||||
mode: Mode::Auto,
|
||||
max_taint: DataClass::Private,
|
||||
result_class: DataClass::Private,
|
||||
untrusted: true,
|
||||
expires: None,
|
||||
secret: None,
|
||||
constraints: Constraints::default(),
|
||||
};
|
||||
assert_eq!(parse("minimal.toml").unwrap(), want);
|
||||
assert_eq!(Constraints::default().paths, Vec::<String>::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_grant() {
|
||||
let want = Grant {
|
||||
tool: "http_fetch".to_string(),
|
||||
mode: Mode::Ask,
|
||||
max_taint: DataClass::Secret,
|
||||
result_class: DataClass::Public,
|
||||
untrusted: false,
|
||||
expires: Some(Timestamp::parse("2026-12-31T00:00:00.000Z").unwrap()),
|
||||
secret: Some("example-api-token".to_string()),
|
||||
constraints: Constraints {
|
||||
paths: vec!["/home/kyle/notes/**".to_string()],
|
||||
hosts: vec!["example.com".to_string(), "api.example.com".to_string()],
|
||||
patterns: vec!["^GET ".to_string()],
|
||||
},
|
||||
};
|
||||
assert_eq!(parse("full.toml").unwrap(), want);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mistakes_in_grant_files_are_errors() {
|
||||
for name in [
|
||||
"unknown_field.toml",
|
||||
"unknown_constraint.toml",
|
||||
"bad_mode.toml",
|
||||
"missing_max_taint.toml",
|
||||
] {
|
||||
assert!(parse(name).is_err(), "{name} was accepted");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modes_are_lowercase() {
|
||||
for (mode, text) in [
|
||||
(Mode::Auto, "auto"),
|
||||
(Mode::Ask, "ask"),
|
||||
(Mode::Deny, "deny"),
|
||||
] {
|
||||
assert_eq!(serde_json::to_string(&mode).unwrap(), format!("\"{text}\""));
|
||||
}
|
||||
}
|
||||
@@ -7,3 +7,4 @@ Every external crate has a row here. `scripts/check-dep-docs.sh` enforces it.
|
||||
| `serde` | 1.0.229 | `proto` | Derives serialization for every shared type. MIT OR Apache-2.0. |
|
||||
| `serde_json` | 1.0.151 | `proto` | JSON for frames and log files. MIT OR Apache-2.0. |
|
||||
| `humantime` | 2.4.0 | `proto` | RFC 3339 timestamps. No dependencies of its own. MIT OR Apache-2.0. |
|
||||
| `toml` | 1.1.6 | `proto` tests; `brokerd` and `bxctl` from M3 | Parses grant files. MIT OR Apache-2.0. |
|
||||
|
||||
@@ -9,5 +9,6 @@ reviewer adds findings under "Reviews" once per milestone.
|
||||
| M1/02-proto-values | 2026-09-17 | done | 1 | pass | none | Implemented ValueError, SessionId, Epoch, CallId, Hash32 and Timestamp in crates/proto/src/ids.rs and DataClass in class.rs, using serde try_from/into for string-backed JSON validation, a hand-written hex encoder and humantime for RFC 3339 parsing with canonical re-serialization. |
|
||||
| M1/03-proto-wire | 2026-09-17 | done | 2 | pass | none | Added Envelope, Message, WireError, ErrorCode, ToolRequest, ToolResponse and DenyReason in crates/proto/src/wire.rs, re-exported from lib.rs; all 9 fixture tests pass and `make gate` prints `gate: ok`. |
|
||||
| M1/04-proto-frame | 2026-09-17 | done | 2 | fail | none | Added crates/proto/src/frame.rs (MAX_FRAME, FrameError, write_frame, read_frame) re-exported from lib.rs; 13 fixture tests pass. Two compile fixes: mapped read_bytes io::Error to FrameError::Io and annotated serde_json::from_slice::<Envelope>; cargo-fmt reordered the lib.rs re-export lines; `make gate` prints `gate: ok`. |
|
||||
| M1/05-proto-grant | 2026-09-17 | done | 2 | fail | none | Added crates/proto/src/grant.rs (Mode, Constraints with Default, Grant with serde defaults + deny_unknown_fields) re-exported from lib.rs and toml 1.1.6 as a proto dev-dependency (workspace dep + dependencies.md row); 4 fixture tests pass. cargo-fmt reordered the lib.rs re-exports before the gate. |
|
||||
|
||||
## Reviews
|
||||
|
||||
Reference in New Issue
Block a user