feat(auth): authorise an allow-listed machine identity #71

Merged
rock merged 2 commits from feat/machine-identity-auth into main 2026-10-28 06:39:22 +00:00
Owner

ZITADEL puts no roles in machine-user tokens here, so a service account cannot satisfy requiredCapability. New opt-in flag admits a caller whose client_id is allow-listed. Still authenticated by a registered private key, off by default.

ZITADEL puts no roles in machine-user tokens here, so a service account cannot satisfy requiredCapability. New opt-in flag admits a caller whose client_id is allow-listed. Still authenticated by a registered private key, off by default.
rock added 2 commits 2026-10-28 06:39:08 +00:00
Clef-flash is a decision model, not a chat model. It serves Clef's own
System One / Jev API at /v1/systemone: a state plus a schema of typed
questions in, one probability per allowed option out, single prefill
pass, no decoding and no free-form text.

Router previously dispatched by body `model` only for
/v1/chat/completions, /v1/embeddings and /v1/rerank. A request to
/v1/systemone therefore fell through to the configured routes, found
none, and answered "no routes configured". Adding the path to the
dispatch predicate is the whole fix: routeByModel already sets
upstreamPath := path, so the upstream receives /v1/systemone unchanged.
There is deliberately no /rerank-style rewrite.

Config: `ornith:35b` is removed with the Ollama predictor, and its GPU
now serves clef-flash. `qwen2.5:3b-instruct` already pointed at qwen-cpu
and is unchanged. Callers of ornith:35b must move to `reasoning`, which
keeps its serving name while running Qwen3-30B-A3B.

Tests cover body dispatch, unknown_model parity with the other three
paths, and that the upstream path is not rewritten.
feat(auth): authorise an allow-listed machine identity without a roles claim
CI / CI (pull_request) Successful in 2m16s
CI / CI (push) Successful in 3m29s
f8788335b8
ZITADEL does not put roles into a machine-user access token on this instance. The
grant exists, the scope asks for urn:zitadel:iam:org:project:roles, and the claim
still never arrives; a colon-free role key makes no difference either. A service
account therefore cannot satisfy requiredCapability by any configuration.

auth.allowListedMachineIdentities admits such a caller when its client_id appears in
zitadelClientIds. The caller is still authenticated: it signed an assertion with a
private key whose public half ZITADEL holds. The allow-list entry is a reviewed line
of configuration, not a wildcard.

The new path is off by default, and a token that does state its roles is judged by the
role check as before, so the weaker rule never applies twice.
rock merged commit f8788335b8 into main 2026-10-28 06:39:22 +00:00
Sign in to join this conversation.