A new swarm service, swarm-notify-router, in its own container on the host
that sets deploy.notify-router.enable (defaulted on by allSwarmServices where
the host has the router's store leaf, like the claude login minter).
- swarm-controller registers instance-wide hooks for `issues` +
`issue_assign` and `pull_request` + `pull_request_assign`, updates the
event list of a hook already registered with fewer (leaving its active flag as it is), and keeps each
verified body unchanged in the SWARM_EVENTS stream on
$SWARM.events.forge.<X-Forgejo-Event-Type>, deduped by X-Forgejo-Delivery.
A delivery the stream did not keep answers 503.
- swarm-notify-router drains its durable consumer on that stream, runs its
flows (source -> match -> todo sink, stored as nodes plus edges and checked
to be a tree) and queues each op on $SWARM.router.todo.<agent> in the
work-queue stream ROUTER_TODOS. Built-in flows: assigned, unassigned (same
key, rewritten) and closed (prefix-clears every holder's router todos). A
per-issue snapshot in its notify-router-state bucket holds the last
assignees and the todo holders; only agents in the agent-status bucket get
ops, and an assignee that was not one is left out of the snapshot so the
next assign event retries it. After the first event that must be retried,
the rest of the batch is nakked so redelivery keeps stream order.
- hive-agent creates its own durable consumer todo-<agent> and applies each
op to its todo store as subsystem `router`, then acks; a refused op stops
the rest of its batch the same way. The store's (subsystem, key) dedup
makes a redelivery harmless. New Todos::clear_prefix.
- swarm-nats-auth: a --notify-router-client arm; the controller may publish
$SWARM.events.forge.*; each agent gets three {agent} templates (extended
consumer create whose filter the server checks, MSG.NEXT, ACK).
- Credentials: a notify-router store leaf minted by glue-bao-tls, a
swarm-notify-router bao policy (read on its own queue client secret), an
authelia machine client, the secret publisher entry, a journal forwarder
route. No manual step.
hive-forge-notify is unchanged, so agents also still get forge's own
notifications.
Refs #5571
114 lines
5.8 KiB
TOML
114 lines
5.8 KiB
TOML
[package]
|
|
name = "swarm-queue-client"
|
|
version.workspace = true
|
|
readme = "README.md"
|
|
edition.workspace = true
|
|
|
|
[features]
|
|
# OFF by default, and that default is the point: the auth-callout responder
|
|
# consumes this crate for the connect alone and speaks neither `jetstream`
|
|
# nor `kv`. A consumer that needs the status bucket says so in its own
|
|
# Cargo.toml, so the requirement stays visible where it is incurred.
|
|
#
|
|
# What is behind the flag is deliberately narrow - the *name and shape* of
|
|
# one bucket two crates open from opposite ends (`src/status.rs`), not a
|
|
# general "KV support" surface. The crate's job still ends at a connected
|
|
# client; the exception exists because an agreement between two crates has
|
|
# to live in one of them, and neither end of that bucket is senior to the
|
|
# other.
|
|
kv = ["async-nats/kv"]
|
|
# 🩸 `jetstream` is NOT in async-nats's default feature set here — the
|
|
# workspace-level dependency turns default features off entirely (see
|
|
# root `Cargo.toml`: `server_2_14`/`nkeys`/`ring` only). `kv` above works
|
|
# standalone only because async-nats's own `kv` feature pulls `jetstream`
|
|
# in transitively; `notices.rs` uses `async_nats::jetstream` directly and
|
|
# needs the same request explicitly, or it only compiles by accident when
|
|
# something else in the same build happens to also enable `kv` (which is
|
|
# exactly how this went unnoticed: `cargo test` at the workspace level
|
|
# unifies features across every crate being built, so `hive-c0re`'s own
|
|
# `kv` request silently carried `notices.rs` until a single-crate
|
|
# `cargo check -p swarm-nats-auth` — no `kv` anywhere in that build —
|
|
# surfaced it as `cannot find jetstream in async_nats`).
|
|
notices = ["async-nats/jetstream"]
|
|
# `subagent_term::open_or_create`, for swarm-controller, which creates every
|
|
# agent's subagent stream. Explicit for the same reason as `notices`.
|
|
subagent-term = ["async-nats/jetstream"]
|
|
# `agent_term::open_or_create` and the replay consumer, for swarm-controller,
|
|
# which creates every agent's terminal stream and replays it to swarm-ui.
|
|
# Explicit for the same reason as `notices`.
|
|
agent-term = ["async-nats/jetstream"]
|
|
# `agent_schedules::read_own`, for the agent harness, which reads its own
|
|
# schedule key without opening the bucket. Explicit for the same reason as
|
|
# `notices`.
|
|
agent-schedules = ["async-nats/jetstream"]
|
|
# `agent_inference::read_own`, for the agent harness, which reads its own
|
|
# inference key the same way. Explicit for the same reason as `notices`.
|
|
agent-inference = ["async-nats/jetstream"]
|
|
# `operator_profiles::read`, for the agent harness, which reads the operator
|
|
# list with one direct get when it renders its system prompt. Explicit for the
|
|
# same reason as `notices`.
|
|
operator-profiles = ["async-nats/jetstream"]
|
|
# `agent_limits::read`, for hive-c0re, which reads the key of each agent it
|
|
# runs the same way. Explicit for the same reason as `notices`.
|
|
agent-limits = ["async-nats/jetstream"]
|
|
# `agent_permissions::read` and `HiveCopy`, for hive-c0re, the agent
|
|
# harness, the subagent daemon and swarm-controller. Pulls `hive-types` for the
|
|
# `Permission` names, so the crates that only need the bucket name do not link
|
|
# it.
|
|
agent-permissions = ["dep:hive-types", "async-nats/jetstream"]
|
|
# `agent_archetype::read`, for hive-c0re, which reads the archetype of each
|
|
# agent it provisions or rebuilds. Explicit for the same reason as `notices`.
|
|
agent-archetype = ["async-nats/jetstream"]
|
|
# `agents_md_initial::read_own`, for the agent harness, which reads its own
|
|
# initial `AGENTS.md` text with one direct get. Explicit for the same reason as
|
|
# `notices`.
|
|
agents-md-initial = ["async-nats/jetstream"]
|
|
# `forge_events::open_or_create`, for swarm-controller, which publishes verified
|
|
# forge deliveries, and the notification router, which reads them. Explicit
|
|
# for the same reason as `notices`.
|
|
forge-events = ["async-nats/jetstream"]
|
|
# `router_todo`, for the notification router, which creates the stream, and
|
|
# the agent harness, which drains its own consumer. Explicit for the same
|
|
# reason as `notices`.
|
|
router-todo = ["async-nats/jetstream"]
|
|
|
|
[dependencies]
|
|
# Bare (no `kv`/`jetstream`) unless a consumer opts into the `kv` feature
|
|
# above - the connect itself needs none of them.
|
|
async-nats.workspace = true
|
|
hive-types = { workspace = true, optional = true }
|
|
# `blocking` on top of the workspace default (`form`/`json`/`rustls`) —
|
|
# `mint_token_for_blocking` needs `reqwest::blocking::Client` for a caller
|
|
# with no tokio reactor to `.await` an async request on (an OTLP exporter's
|
|
# `HttpClient` impl, see that function's doc). Declared here rather than
|
|
# left to arrive transitively from a consumer that happens to pull in
|
|
# `reqwest`'s blocking feature some other way — this crate already has a
|
|
# recorded case of exactly that kind of accidental compile (see the `kv`
|
|
# feature's comment above), and `cargo check -p swarm-queue-client` alone
|
|
# must not depend on what else is in the build.
|
|
reqwest = { workspace = true, features = ["blocking"] }
|
|
serde.workspace = true
|
|
serde_json.workspace = true
|
|
# `stored_secret::StoredSecret`, the held client secret, and the rustls
|
|
# provider `connect` installs.
|
|
swarm-secret-client.workspace = true
|
|
# The inference bucket's stored types are in `swarm-wire`; this crate holds
|
|
# the connection side.
|
|
swarm-wire.workspace = true
|
|
# A library, so its errors are a matchable enum rather than an opaque
|
|
# `anyhow::Error`. The binaries that consume this keep anyhow; `?` converts.
|
|
thiserror.workspace = true
|
|
tokio.workspace = true
|
|
tracing.workspace = true
|
|
|
|
[dev-dependencies]
|
|
# A loopback stand-in token endpoint for the client secret re-read tests.
|
|
axum.workspace = true
|
|
data-encoding.workspace = true
|
|
# The ports-file directory of the throwaway `nats-server` the stream tests run.
|
|
tempfile = "3"
|
|
# StreamExt::next on a pulled batch in those tests.
|
|
futures-util.workspace = true
|
|
|
|
[lints]
|
|
workspace = true
|