From 1f6a12e4399380dd631fdf2c375b4fe1cf22cd24 Mon Sep 17 00:00:00 2001 From: naps62-yolo Date: Tue, 26 May 2026 20:32:06 +0000 Subject: [PATCH] feat(maestro): add [daemon.policy] allow_inbound to config (NAP-247) New [daemon.policy] section with allow_inbound: Vec. Default empty (deny all peer attaches). The daemon plumbs it through DaemonConfig. Co-Authored-By: Claude Opus 4.7 (1M context) --- crates/maestro-config/src/lib.rs | 23 +++++++++++++++++++++++ crates/maestro-daemon/src/config.rs | 5 +++++ crates/maestro-daemon/src/configure.rs | 3 +++ 3 files changed, 31 insertions(+) diff --git a/crates/maestro-config/src/lib.rs b/crates/maestro-config/src/lib.rs index 8462982..d8c429d 100644 --- a/crates/maestro-config/src/lib.rs +++ b/crates/maestro-config/src/lib.rs @@ -141,6 +141,9 @@ pub struct DaemonSection { /// back to peer daemons as a T2 dial target. #[serde(default, skip_serializing_if = "Option::is_none")] pub private_addr: Option, + /// `[daemon.policy]` — per-daemon attach policy (NAP-247). + #[serde(default, skip_serializing_if = "DaemonPolicySection::is_empty")] + pub policy: DaemonPolicySection, } impl DaemonSection { @@ -153,6 +156,23 @@ impl DaemonSection { && self.worktree_root.is_none() && self.perimeter_id.is_none() && self.private_addr.is_none() + && self.policy.is_empty() + } +} + +/// `[daemon.policy]` — application-layer access control complementing network +/// ACLs. Default deny: an empty `allow_inbound` rejects every cross-daemon +/// attach. Local attach (no JWT, same daemon) always bypasses. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct DaemonPolicySection { + /// daemon-ids permitted to broker-attach into this daemon's sessions. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub allow_inbound: Vec, +} + +impl DaemonPolicySection { + fn is_empty(&self) -> bool { + self.allow_inbound.is_empty() } } @@ -311,6 +331,9 @@ mod tests { worktree_root: Some(PathBuf::from("/var/lib/maestrod/wt")), perimeter_id: Some("wg-naps62".into()), private_addr: Some("10.0.0.7:7070".into()), + policy: DaemonPolicySection { + allow_inbound: vec!["konishi".into()], + }, }, registry: RegistrySection { bind_addr: Some("0.0.0.0:8080".parse().unwrap()), diff --git a/crates/maestro-daemon/src/config.rs b/crates/maestro-daemon/src/config.rs index 88205d7..0e60d18 100644 --- a/crates/maestro-daemon/src/config.rs +++ b/crates/maestro-daemon/src/config.rs @@ -83,6 +83,9 @@ pub struct DaemonConfig { /// Daemon's reachable address on the declared perimeter. Only meaningful /// when `perimeter_id` is set. pub private_addr: Option, + /// daemon-ids permitted to broker-attach to this daemon's sessions. + /// Default empty (deny all). Local attach bypasses. (NAP-247.) + pub allow_inbound: Vec, } impl DaemonConfig { @@ -194,6 +197,7 @@ pub fn load_config(path: &Path, env: EnvOverrides) -> Result Result