hyperhive/swarm-controller/Cargo.toml
atlas b2596097d8 feat(#3255): receive swarm-wide forge webhooks in the controller
A Forgejo webhook has one target_url, so every hive registering the same
swarm-wide hooks is last-writer-wins rather than idempotent. The controller
is the only swarm-wide thing in the deployment, so it becomes the receiver.

It verifies the HMAC and treats the payload as opaque bytes keyed by the hook
kind in the URL path; it deliberately does not parse the payload, because the
hives' existing handlers already decide what a delivery means.

Nothing is registered against the endpoint yet. The replacement path is built
and observable before anything takes the old one away, so the swarm's single
target_url never points at a receiver that forwards nowhere.
2026-08-18 12:28:09 +02:00

75 lines
3.4 KiB
TOML

[package]
name = "swarm-controller"
version.workspace = true
readme = "README.md"
edition.workspace = true
[[bin]]
name = "swarm-controller"
path = "src/main.rs"
[dependencies]
anyhow.workspace = true
# `kv` (which pulls `jetstream`) on top of the workspace's feature set: the
# queue is this daemon's *store*, not just its transport - a hive's last
# status snapshot is read out of a JetStream KV bucket. Declared here rather
# than in the workspace entry so the auth-callout responder, which speaks
# neither, does not claim to need them.
async-nats = { workspace = true, features = ["kv"] }
axum.workspace = true
# swarm-controller's own forge client (`forge.rs`) — self-contained,
# deliberately not sharing code with `hive-c0re::forge` across the crate
# boundary (see #3306's design discussion: forcing that split now, over a
# few idempotent CRUD-ish calls, is premature plumbing).
forgejo-api.workspace = true
# Only for base64-encoding file content for `forge.rs`'s
# `repo_change_files` calls — forgejo's content API takes base64, never
# raw bytes.
base64.workspace = true
futures-util.workspace = true
# RFC 9457 `application/problem+json` error bodies. Same version + `axum`
# feature as hive-c0re: the two daemons answer the same operator UIs, so a
# reader that handles one's failures has to handle the other's.
problem_details = { version = "0.9.0", features = ["axum"] }
# The graph itself, held directly rather than behind a c0re-style wrapper
# module — that layering (`hive-c0re::job_queue`) is partially legacy (predates
# `hive-jobq`'s extraction into its own crate) and this daemon does not need it
# repeated. Driven by `hive_jobq::scheduler::Scheduler` (`spawn_jobq_worker`),
# same shape `hive-c0re/src/job_queue/scheduler.rs` uses over its own graph.
hive-jobq.workspace = true
hive-jobq-wire.workspace = true
# The forge webhook HMAC (`webhook.rs`). Kept in this crate rather than
# shared with hive-c0re's equivalent: c0re's copy is scheduled to be deleted
# with its webhook routes once registration moves here, so the second holder
# is departing, not arriving — see that module's docs.
hmac.workspace = true
sha2.workspace = true
# Validates `POST /api/agents`' `name` before it becomes `agent`/`repo`
# everywhere downstream — see `create_agent`'s doc comment for why this is
# defense-in-depth, not the only gate (per an argus review finding).
hive-types.workspace = true
# `auth`'s bridge client — same crate the bridge itself uses to define the
# request/response shape, so the two ends cannot drift. `forge.rs` also
# uses this directly for `StatusCode` in its error-classification helpers.
reqwest.workspace = true
serde.workspace = true
serde_json.workspace = true
swarm-authelia-bridge-sock.workspace = true
# The queue connect (token mint + auth callback + reconnect) is shared with
# every other participant - a hive publishing its own status runs the same
# code with a different client id. Two copies of credential handling is one
# token-refresh fix that has to be found twice.
#
# `kv` for the same reason one level in: the status bucket's name and
# creation config are shared with the hive that writes it, so this end does
# not get to declare them privately.
swarm-queue-client = { workspace = true, features = ["kv"] }
tokio.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
url.workspace = true
utoipa.workspace = true
utoipa-axum.workspace = true
[lints]
workspace = true