AdminService is the one write surface that changes what config is
running, rather than what an object's state is. It carries two RPCs,
ValidateConfig and ApplyConfig, and it exists only when both an
auth: block is configured and the process was not started with
-read-only.
1. Admin-gated, and absent without auth
AdminService requires the admin role: viewer and operator
identities are refused with a permission error, the same way
CommandService refuses an unauthenticated caller. With no auth:
block configured at all, the route is not mounted -- there is no
identity to require, so a config write over the API is never offered to
an unauthenticated caller, the same posture the config file and the
external command FIFO already have for a local operator. See
Security for what auth: looks like and
what the three roles mean.
-read-only unmounts AdminService for the same reason it unmounts
CommandService: a process serving queries and the dashboard only has
no write surface to gate, admin role or not.
2. ValidateConfig -- check without touching anything
ValidateConfig(cue) runs a whole CUE document past the same loader a
SIGHUP reload uses, without writing anything to disk and without
touching the running config. The response carries ok and, when ok
is false, error -- the loader's own diagnostics, naming the field and
the problem, exactly as a rejected SIGHUP logs them.
curl -s -X POST http://127.0.0.1:8080/jaque.v1.AdminService/ValidateConfig \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <admin identity token>' \
-d '{"cue":"hosts: gw: {address: \"192.168.1.1\", check: {type: \"icmp\", host: address}}"}'
3. ApplyConfig -- persist, then hot-swap
ApplyConfig(cue) runs the identical check and, on success, rewrites
the served -config file with cue's bytes and swaps the running
config through the same fanout a SIGHUP reload uses -- every consumer
that reacts to a reload today (the scheduler, the sinks, the notifier's
macro map) sees an apply exactly the same way. The file is rewritten
before the swap: a process that stops between the two steps still
converges on restart to the config it would have been running anyway.
The response carries the same ok/error shape as ValidateConfig. A
rejected apply changes nothing -- the served file is left untouched and
the running config keeps serving what it was serving before the call.
curl -s -X POST http://127.0.0.1:8080/jaque.v1.AdminService/ApplyConfig \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <admin identity token>' \
-d '{"cue":"hosts: gw: {address: \"192.168.1.1\", check: {type: \"icmp\", host: address}}"}'
A missing cue field is the one failure that is a transport error
(CodeInvalidArgument) rather than ok: false -- that is a malformed
request, not a rejected document.
4. Config stays code
Both RPCs take a whole document, never a partial shape for one host or
one check. Pipelines that render CUE and commit it remain the
recommended way to manage a fleet's config in a real estate; this
service is the admin convenience and the playground's upload path, not
a replacement for that. A caller that wants to add one object resolves
the current config, edits its own copy, and calls ApplyConfig with
the whole result.
5. Security considerations
An admin identity that can call ApplyConfig can rewrite the entire
served config -- a strictly larger blast radius than anything an
operator identity can do through CommandService. Treat an admin
session or token with the same care as shell access to the host: rotate
-session-secret on any suspicion a session leaked, the same guidance
Security gives for every other role.