Watch
0
0
Fork
You've already forked hyperhive
0

notify router: read custom flows and disabled built-ins from a stored document

This commit is contained in:
atlas 2026-10-24 03:44:00 +02:00 • committed by mara
commit bb08fac33c
11 changed files with 1418 additions and 342 deletions

View file

@ -317,7 +317,7 @@ impl Policy {
/// What the notification router may do: drain its own consumer on the
/// forge events stream, queue todo ops for any agent, keep its own state
/// bucket, and list which agents exist.
/// bucket, read the flows document, and list which agents exist.
///
/// - **Forge events.** Open or create the stream, create its one durable
/// in the extended form whose last tokens are the filter (the server
@ -330,14 +330,18 @@ impl Policy {
/// subjects, which is what makes the router their sole writer.
/// - **Its state bucket.** Create, look up, read and write
/// `notify-router-state`, and no other bucket.
/// - **The flows document.** `STREAM.INFO` and `DIRECT.GET` on
/// `notify-flows`, which it reads and never creates or writes: the
/// controller creates the bucket and is its one writer.
/// - **The roster.** `STREAM.INFO` on the agent-status bucket, which lists
/// its keys (`<hive>.<agent>`) and writes none.
fn notify_router_permissions(client_id: &str) -> Permissions {
use swarm_wire::{forge_events, notify_router, router_todo};
use swarm_wire::{forge_events, notify_flows, notify_router, router_todo};
let events = forge_events::STREAM;
let consumer = forge_events::ROUTER_CONSUMER;
let state = format!("KV_{}", notify_router::STATE_BUCKET);
let flows = format!("KV_{}", notify_flows::BUCKET);
let mut publish = Self::jetstream_minimum().to_vec();
publish.extend([
format!("$JS.API.STREAM.INFO.{events}"),
@ -355,6 +359,8 @@ impl Policy {
format!("$JS.API.STREAM.CREATE.{state}"),
format!("$JS.API.DIRECT.GET.{state}.>"),
format!("$KV.{}.>", notify_router::STATE_BUCKET),
format!("$JS.API.STREAM.INFO.{flows}"),
format!("$JS.API.DIRECT.GET.{flows}.>"),
format!("$JS.API.STREAM.INFO.{}", Self::agent_status_stream()),
Self::inbox(client_id),
]);
@ -1799,7 +1805,7 @@ mod tests {
#[test]
fn the_notify_router_drains_its_own_consumer_and_writes_only_its_own_state() {
use swarm_queue_client::client_inbox_prefix;
use swarm_wire::{forge_events, notify_router, router_todo};
use swarm_wire::{forge_events, notify_flows, notify_router, router_todo};
let p = with_router()
.permissions(NOTIFY_ROUTER)
@ -1807,6 +1813,7 @@ mod tests {
let may = |subject: &str| p.publish.iter().any(|g| covers(g, subject));
let events = forge_events::STREAM;
let state = notify_router::STATE_BUCKET;
let flows = notify_flows::BUCKET;
for allowed in [
format!(
"$JS.API.CONSUMER.CREATE.{events}.notify-router.{}",
@ -1820,6 +1827,9 @@ mod tests {
format!("$KV.{state}.42.5571"),
format!("$JS.API.DIRECT.GET.KV_{state}.$KV.{state}.42.5571"),
"$JS.API.STREAM.INFO.KV_agent-status".to_owned(),
// The flows document: read it.
format!("$JS.API.STREAM.INFO.KV_{flows}"),
format!("$JS.API.DIRECT.GET.KV_{flows}.$KV.{flows}.{}", notify_flows::KEY),
] {
assert!(may(&allowed), "the router may not reach {allowed}");
}
@ -1838,6 +1848,10 @@ mod tests {
forge_events::subject("issues"),
"$KV.agent-status.alpha.atlas".to_owned(),
"$KV.agent-schedules.atlas".to_owned(),
// The flows document: not write it, nor make the bucket.
format!("$KV.{flows}.{}", notify_flows::KEY),
format!("$JS.API.STREAM.CREATE.KV_{flows}"),
format!("$JS.API.STREAM.DELETE.KV_{flows}"),
swarm_queue_client::agent_input_subject("atlas"),
"$JS.API.STREAM.CREATE.KV_agent-status".to_owned(),
] {