Watch
0
0
Fork
You've already forked hyperhive
0

swarm-ui: set the review classifier's keys from the classifier settings

The classifier's Anthropic and ACP provider keys were written by hand in the
secret store's UI. The controller now stores them: GET
/api/review-classifier/keys says whether each is set and when it changed, and
PUT /api/review-classifier/keys/{runtime} writes one. No route answers a key.

The controller's store policy gains create and update on the two leaves with
no read of their values, and a metadata read for the set state. The swarm UI's
classifier settings card gets a key form beside the settings, saved apart
because it goes to the secret store.
This commit is contained in:
iris 2026-11-11 18:29:29 +01:00 • committed by mara
commit 6875a4f0f8
3 changed files with 628 additions and 20 deletions

View file

@ -638,7 +638,7 @@ in
description = ''
Run the review classifier in a `swarm-review-classifier` container on
this host. It answers the swarm UI's review try-outs with `claude` on
the Anthropic API key the operator writes to the secret store at
the Anthropic API key the operator sets in the swarm UI, or writes to the secret store at
`secret/swarm/services/review-classifier/anthropic-api-key`, field
`api_key`.

View file

@ -562,6 +562,22 @@ let
capabilities = ["create", "read", "update"]
}
path "${credentialMountPath}/data/${classifierApiKeyLeaf}" {
capabilities = ["create", "update"]
}
path "${credentialMountPath}/metadata/${classifierApiKeyLeaf}" {
capabilities = ["read"]
}
path "${credentialMountPath}/data/${classifierAcpKeyLeaf}" {
capabilities = ["create", "update"]
}
path "${credentialMountPath}/metadata/${classifierAcpKeyLeaf}" {
capabilities = ["read"]
}
path "${credentialMountPath}/data/${builderForgeTokenLeaf}" {
capabilities = ["create", "read", "update"]
}
@ -775,7 +791,10 @@ let
classifierForgeTokenLeaf = "swarm/services/review-classifier/forge/token";
# The review classifier's Anthropic API key. The operator writes it through
# the UI's OIDC login; the classifier reads it on every run.
# the UI's OIDC login, or the swarm UI's classifier settings, which the
# controller writes for them: `create` and `update` with no `read`, and a
# metadata `read` for whether one is set and when it changed. The classifier
# reads it on every run.
classifierApiKeyLeaf = "swarm/services/review-classifier/anthropic-api-key";
# The swarm builder's forge token, the nix half of

View file

@ -1934,15 +1934,18 @@ let
# `forge::classifier_account` mints the review classifier's forge token
# and reads it back to decide on a re-mint and to post reviews. Pinned
# as the whole stanza, and spelled to the leaf, for the reason above.
name = "the controller's grant on the review classifier is its forge token alone";
name = "the controller's grant on the review classifier is its forge token, and a write-only inference key each";
ok =
let
s = baoGrantHere.systemd.services.swarm-bao-controller-policy.script;
writeOnly = leaf: [
"path \"secret/data/swarm/services/review-classifier/${leaf}\" {\n capabilities = [\"create\", \"update\"]\n}"
"path \"secret/metadata/swarm/services/review-classifier/${leaf}\" {\n capabilities = [\"read\"]\n}"
];
in
lib.hasInfix "path \"secret/data/swarm/services/review-classifier/forge/token\" {\n capabilities = [\"create\", \"read\", \"update\"]\n}" s
&& !(lib.hasInfix "secret/data/swarm/services/review-classifier/*" s)
&& !(lib.hasInfix "secret/data/swarm/services/review-classifier/anthropic-api-key" s)
&& !(lib.hasInfix "secret/data/swarm/services/review-classifier/acp-provider" s);
&& lib.all (p: lib.hasInfix p s) (writeOnly "anthropic-api-key" ++ writeOnly "acp-provider")
&& !(lib.hasInfix "secret/data/swarm/services/review-classifier/*" s);
}
{
# The runner's role is the token's only reader. Its policy is that leaf