swarm: name the deploy event and grant the controller its publish
The subject and its payload live in `swarm-queue-client` for the reason the knowledge event's already does: three crates have to agree on the string, and the one that agrees hardest — the auth-callout responder, which decides whether the publish is permitted at all — speaks neither `jetstream` nor `kv`. Swarm-wide rather than a `$SWARM.deploy.<hive>` family. That family would look like isolation and provide none: this responder scopes publish only, leaving `sub` unrestricted, so a hive could subscribe to another's subject as easily as to its own. Until `sub` is scoped the split costs a wider grant and buys nothing, so the addressing goes in the payload and each hive filters on its own name. Unlike the knowledge event the message is addressed, so it carries a payload — a trigger, never the config. The hive already tracks the agent's config repo; desired state on the wire would make this a second source of truth for something git owns, and a hive that missed a message would be wrong rather than late. Both test arms mirrored from the knowledge event. The negative one matters more here: a forged knowledge event makes a hive re-read a repo, a forged deploy event makes it rebuild and restart a named agent.
This commit is contained in:
parent
5f197a38ee
commit
93c7454bf5
2 changed files with 73 additions and 0 deletions
|
|
@ -278,6 +278,12 @@ impl Policy {
|
|||
// about a change, with nothing in the controller's log to say a
|
||||
// permission was the reason.
|
||||
swarm_queue_client::KNOWLEDGE_SUBJECT.to_owned(),
|
||||
// The deploy event, same shape and the same failure mode as the
|
||||
// knowledge event above — one literal subject, one writer. Also
|
||||
// deliberately not a per-hive family: this responder scopes
|
||||
// publish only, so a per-hive subject would not stop a hive
|
||||
// reading another's. See the const's own doc.
|
||||
swarm_queue_client::DEPLOY_SUBJECT.to_owned(),
|
||||
]);
|
||||
subjects
|
||||
}
|
||||
|
|
@ -373,6 +379,39 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_reader_may_publish_the_deploy_event() {
|
||||
let p = policy().permissions("swarm-controller").expect("a reader");
|
||||
assert!(
|
||||
p.publish
|
||||
.contains(&swarm_queue_client::DEPLOY_SUBJECT.to_owned()),
|
||||
"the controller is the only publisher of this event; without the \
|
||||
grant its publish is refused, and a refusal arrives as a timeout"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_hive_may_not_publish_the_deploy_event_to_anyone_including_itself() {
|
||||
// Same arm as the knowledge event's, and it matters more here: a forged
|
||||
// knowledge event makes a hive re-read a repo, while a forged deploy
|
||||
// event makes it rebuild and restart a named agent. One shared subject
|
||||
// means a single forged message reaches every hive in the swarm.
|
||||
//
|
||||
// Note this is the half `sub` scoping would not fix even once it lands:
|
||||
// reading another hive's deploy message is a confidentiality question,
|
||||
// *sending* one is this.
|
||||
let p = policy()
|
||||
.permissions("hive-alpha")
|
||||
.expect("a hive is admitted");
|
||||
assert!(
|
||||
!p.publish
|
||||
.iter()
|
||||
.any(|s| s == swarm_queue_client::DEPLOY_SUBJECT),
|
||||
"a hive must not publish the deploy event: {:?}",
|
||||
p.publish
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_hive_grant_never_includes_the_jetstream_wildcard() {
|
||||
// `$JS.API.>` also covers `$JS.API.STREAM.DELETE.KV_hive-status`, with
|
||||
|
|
|
|||
Loading…
Reference in a new issue