Watch
0
0
Fork
You've already forked hyperhive
0

nix: run the review classifier in a container of its own when a host enables it

deploy.review-classifier.enable (off by default, not derived from
allSwarmServices) puts swarm-review-classifier in its own network
namespace on the bridge, with claude-code and the daemon. A host unit
copies the classifier's store leaf into the container; the daemon reads
its queue client secret and the API key from the store with it.

- glue-bao-tls mints the review-classifier leaf; swarm-bao writes the
  swarm-review-classifier role, reading exactly the API key path and the
  classifier's queue client secret; swarm.nix reserves its CN.
- glue-review-classifier-oidc-client registers the machine client where
  authelia runs, and the publisher carries its secret to the store.
- swarm-nats passes the client id to the responder as
  --review-classifier-client.
- Where the host runs a hive-tier collector, claude exports its metrics
  there; the daemon adds the repo and the PR to the resource attributes.
- module-eval: the new grant and leaf (bao-grants, now sixteen granting
  units), the container's presence and its unit (core-toggle), and the
  client registration and publishing (secret-publisher).
This commit is contained in:
atlas 2026-10-12 03:34:33 +02:00 • committed by mara
commit 5d8ab675cd
14 changed files with 435 additions and 8 deletions

View file

@ -53,6 +53,12 @@ let
deploy.ci-runner.enable = true;
};
# On the store's host, which mints the classifier's store identity.
withClassifier = hive {
deploy.bao.enable = true;
deploy.review-classifier.enable = true;
};
# A priority collision is a property of the *option*, not
# of the merged value's interior — nix throws the moment the value is
# demanded at all, so `seq`-ing each `serviceConfig` value to WHNF is
@ -296,6 +302,32 @@ let
name = "the CI container's unit definitions merge without a priority collision";
ok = forceCiServiceConfigs;
}
{
# It spends money on an operator's key, so no host runs it by default.
name = "the review classifier's container exists only where its host enables it";
ok =
!(bare.containers ? swarm-review-classifier)
&& !(centralToggleOff.containers ? swarm-review-classifier)
&& withClassifier.containers ? swarm-review-classifier;
}
{
# The daemon holds its store leaf and nothing else from the host; the
# key is read per run and reaches only `claude`'s environment. Reading
# the claude path also evaluates the container's unfree allowance.
name = "the review classifier loads only its store leaf and carries no API key in its unit";
ok =
let
svc =
withClassifier.containers.swarm-review-classifier.config.systemd.services.swarm-review-classifier;
env = svc.environment;
in
lib.elem "bao-client.pem:/var/lib/swarm-review-classifier/bao-client.pem" svc.serviceConfig.LoadCredential
&& env.BAO_CLIENT_CERT == "/run/credentials/swarm-review-classifier.service/bao-client.pem"
&& env.SWARM_REVIEW_CLASSIFIER_OIDC_CLIENT_ID == "swarm-review-classifier"
&& lib.hasSuffix "/bin/claude" env.SWARM_REVIEW_CLASSIFIER_CLAUDE
&& !(env ? ANTHROPIC_API_KEY)
&& withClassifier.systemd.services ? swarm-review-classifier-identity;
}
{
# The collector reaches these routes through the gateway now, so each
# store needs an ingest location of its own. Without one the write rides