> Section: [10. Reference](https://jaque.sh/docs/reference/cli-flags.md)
> Next: api/status-json
> Index: https://jaque.sh/llms.txt


`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](https://jaque.sh/docs/deployment/security.md) 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.

```sh
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.

```sh
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](https://jaque.sh/docs/deployment/security.md) gives for every other role.
