Watch
0
0
Fork
You've already forked hyperhive
0

swarm-nats-auth: take the subscribe subjects from the shared definitions

The reader's subscribe allow-list spelled `$SWARM.term.*`,
`$SWARM.term.*.sub.*` and `$SWARM.agent-state.*` as literals, copies of
private prefixes in hive-agent and swarm-controller. A rename on either side
left the policy behind and the stream stopped with only a connection-level
server error.

swarm-queue-client now owns `term_subject`, `agent_state_subject` and their
`*_wildcard` forms, and `subagent_term::subject_wildcard`; hive-agent's
publishers, swarm-controller's relays and the policy all build from them.
Subjects are byte-identical.
This commit is contained in:
atlas 2026-10-07 16:51:53 +02:00
commit 1b061fc9d5
7 changed files with 92 additions and 85 deletions

View file

@ -272,6 +272,47 @@ pub const AGENT_INPUT_SUBJECT_WILDCARD: &str = "$SWARM.input.*";
/// Shared by [`agent_input_subject`] and [`AGENT_INPUT_SUBJECT_WILDCARD`].
const AGENT_INPUT_SUBJECT_PREFIX: &str = "$SWARM.input";
/// The subject `agent` publishes its terminal rows on. One leaf per agent, so
/// a subscriber can follow one agent without filtering the swarm's whole
/// terminal traffic.
///
/// Shared by the publisher (`hive-agent`), the relay that streams it to
/// swarm-ui (`swarm-controller`) and the queue's subscribe grant for readers
/// (`swarm-nats-auth`), which spells [`term_subject_wildcard`] — so a rename
/// moves all three together.
#[must_use]
pub fn term_subject(agent: &str) -> String {
format!("{TERM_SUBJECT_PREFIX}.{agent}")
}
/// Every [`term_subject`] at once: the reader's subscribe grant, which has no
/// roster to enumerate agents from.
#[must_use]
pub fn term_subject_wildcard() -> String {
term_subject("*")
}
/// Shared by [`term_subject`] and [`subagent_term`], whose subjects sit under
/// the same prefix.
const TERM_SUBJECT_PREFIX: &str = "$SWARM.term";
/// The subject `agent` publishes its turn-state header on. Same shape and
/// same three users as [`term_subject`].
#[must_use]
pub fn agent_state_subject(agent: &str) -> String {
format!("{AGENT_STATE_SUBJECT_PREFIX}.{agent}")
}
/// Every [`agent_state_subject`] at once, for the same reason as
/// [`term_subject_wildcard`].
#[must_use]
pub fn agent_state_subject_wildcard() -> String {
agent_state_subject("*")
}
/// Shared by [`agent_state_subject`] and its wildcard.
const AGENT_STATE_SUBJECT_PREFIX: &str = "$SWARM.agent-state";
/// The reply-inbox prefix a client presenting `agent`'s own credential
/// connects with.
///
@ -915,6 +956,16 @@ where
mod tests {
use super::*;
/// The names the publishers, the relays and the queue's grant all spell.
#[test]
fn term_and_agent_state_subjects_are_the_agreed_names() {
assert_eq!(term_subject("mara"), "$SWARM.term.mara");
assert_eq!(term_subject_wildcard(), "$SWARM.term.*");
assert_eq!(agent_state_subject("mara"), "$SWARM.agent-state.mara");
assert_eq!(agent_state_subject_wildcard(), "$SWARM.agent-state.*");
assert_eq!(subagent_term::subject_wildcard(), "$SWARM.term.*.sub.*");
}
#[test]
fn a_tls_client_config_builds_once_the_provider_is_installed() {
install_crypto_provider();

View file

@ -18,8 +18,7 @@
//! queue grant in `swarm-nats.nix`, whose `{agent}` template spells the same
//! subjects.
/// The subject family every agent terminal shares.
const TERM_PREFIX: &str = "$SWARM.term";
use crate::TERM_SUBJECT_PREFIX;
/// The token between the agent and the subagent name. A subagent subject has
/// two more tokens than the agent's own `$SWARM.term.<agent>`, so neither
@ -47,14 +46,21 @@ pub fn stream_name(agent: &str) -> String {
/// The subject `subagent`'s rows are published on.
#[must_use]
pub fn subject(agent: &str, subagent: &str) -> String {
format!("{TERM_PREFIX}.{agent}.{SUBAGENT_TOKEN}.{subagent}")
format!("{TERM_SUBJECT_PREFIX}.{agent}.{SUBAGENT_TOKEN}.{subagent}")
}
/// Every [`subject`] at once: the reader's subscribe grant, which has no
/// roster to enumerate agents or subagents from.
#[must_use]
pub fn subject_wildcard() -> String {
subject("*", "*")
}
/// Every subject [`stream_name`]'s stream captures, which is also the publish
/// grant's shape.
#[must_use]
pub fn stream_subjects(agent: &str) -> String {
format!("{TERM_PREFIX}.{agent}.{SUBAGENT_TOKEN}.>")
format!("{TERM_SUBJECT_PREFIX}.{agent}.{SUBAGENT_TOKEN}.>")
}
/// The subagent a stream subject names, or `None` for a subject that is not
@ -62,7 +68,7 @@ pub fn stream_subjects(agent: &str) -> String {
#[must_use]
pub fn subagent_from_subject<'a>(agent: &str, subject: &'a str) -> Option<&'a str> {
let name = subject
.strip_prefix(TERM_PREFIX)?
.strip_prefix(TERM_SUBJECT_PREFIX)?
.strip_prefix('.')?
.strip_prefix(agent)?
.strip_prefix('.')?