> Section: [7. Running it](https://jaque.sh/docs/deployment/choose.md)
> Next: deployment/operations
> Index: https://jaque.sh/llms.txt


Authentication in jaque is opt-in. A process started with no
`-auth-config` behaves exactly as it always has: reads are open, and
`-api-token` optionally gates the RPCs that change state. Setting
`-auth-config` turns on real identity, named humans and services, three
roles, a browser session, in front of both the read and the write API.
Neither mechanism is required, and only one may be configured at a time.
Every other surface, the dashboard, `/metrics`, Livestatus and the
external command FIFO, stays open to whatever can reach it regardless
of which you turn on, and secrets arrive as flags or environment
variables that anything inspecting the process can read. The posture
is this: the network a jaque process listens on is the perimeter, and a
reverse proxy is where TLS happens. Read this page before deciding which
network a jaque process is allowed to sit on.

Status: there is no whole-system authentication story and no TLS in
the binary. The gap is architectural rather than a missing flag, and it
changes as the project matures. See [project
status](../project/status.md).

## 1. The token-only gate, off by default

`-api-token` puts a bearer-token check in front of `CommandService`
only: the RPCs that change state, `ProcessCheckResult`,
`AcknowledgeProblem`, `ScheduleDowntime` and the rest of the write
surface. With it set, every command call needs a matching
`Authorization: Bearer <token>` header, compared in constant time.
Left empty, the default, the command API is open. `-api-token` and
`-auth-config` are two different auth models; jaque refuses to start
with both configured at once.

## 2. Everything else is unauthenticated by design

`/metrics` and `/healthz` are unauthenticated HTTP endpoints on the
same listener as the dashboard.

The dashboard's own files (`/`) are served without any check. What they
show is a different question. Every figure on the page comes from the
read API, so with no `-auth-config` the dashboard loads and displays
everything, and with one set it asks for a sign-in and shows nothing
until it gets one. The files being open is not a way around the block.

Livestatus (`-livestatus-listen` and `-livestatus-socket`) has no
authentication at all, `-auth-config` set or not. Anything that can
open the TCP address or the UNIX socket can query it. This is the
protocol as Nagios, Thruk and NagVis already use it; jaque adds nothing
to it in either direction. Both listeners also accept `COMMAND`, so a
reachable Livestatus TCP listener is a command surface exactly as a
writable FIFO directory is.

The external command FIFO (`-command-file`) is created mode `0660`,
inherited unchanged from Nagios, and no `-auth-config` reaches it
either. File permissions and group ownership are the entire
access-control story, and anyone able to write the file can drive
jaque's state.

Event handlers run whatever command or module the `handlers` block
names, on every state change that matches the trigger table, with no
gate of their own: a `legacy` or `command` handler carries exactly a
legacy check's trust (a binary the operator installed, run without a
shell unless the handler sets `shell: true`), and a `wasm` handler runs
in the same closed sandbox a `wasm` check does, with no network and no
filesystem beyond what is mounted (ADR-032). See [Event
handlers](../config/event-handlers.md) for the config shape.

Notification adapter secrets, `-smtp-pass`, `-telegram-token` and the
rest, arrive via flags or their `JAQUE_`-prefixed env vars. They are
visible to anything that can read the process's environment or command
line: `/proc/<pid>/environ`, `ps`, a container platform's own env
inspection. jaque does not fetch them from a secret store.

## 3. Opt-in identity: `-auth-config`

A process started with no `-auth-config` behaves exactly as described
above: reads open, `-api-token` the only optional gate on writes.
Setting `-auth-config` to the path of a separate CUE document turns
both `CommandService` and `QueryService` into identity-checked APIs,
mounts a session-login surface alongside them, and mounts
`AdminService`, the config write surface, absent when `-auth-config`
is not set, since there is then no identity to require `admin` of.
Leaving `-auth-config` out changes nothing else.

Identities live outside `-config`, in their own document, read once at
process boot: changing them requires a restart, and a SIGHUP does not pick them up. The
document's `auth:` block maps a name, human or service, to a role and
exactly one credential:

```cue
// auth.cue, passed as -auth-config=auth.cue
auth: {
	alice: {
		role:          "operator"
		password_hash: "<jaque hash-password output>"
	}
	aggregator: {
		role:       "viewer"
		token_hash: "<hex digest of the aggregator's own token>"
	}
}
```

`alice` is a human: she logs in with a password and gets a browser
session. `aggregator` is a service identity: the shape another process
polling jaque's read API uses, a name with a token instead of a
password and no login step. An identity carries one credential, never
both.

`jaque hash-password` prints the hash a `password_hash` field expects.
It reads the plaintext password from stdin, never from a flag or a
positional argument, so it never lands in shell history or a process
listing:

```sh
jaque hash-password <<< 's3cret'
```

Paste its output into the auth document; jaque never sees the
plaintext again.

`-session-secret` signs the browser session issued on login. It is
required the moment `-auth-config` is set, and jaque refuses to start
without it. Sessions carry no separate store: rotating
`-session-secret` invalidates every outstanding one at once, logging
every logged-in human out immediately. Rotate it to force a
sign-out, on suspicion of a leaked session, or on a schedule.

Three roles, each allowing everything the one below it does: `viewer`
can call every `QueryService` read; `operator` additionally calls
every `CommandService` write, `ProcessCheckResult`,
`AcknowledgeProblem`, `ScheduleDowntime` and the rest; `admin` allows
everything `operator` does, plus `AdminService`'s `ValidateConfig` and
`ApplyConfig`, the config write surface, see [Admin
service](../api/admin-service.md). Those two RPCs act on `-config`
only: identities never ride them, in either direction. `viewer` and
`operator` identities are refused `AdminService` calls with a
permission error.

A logged-in human authenticates with a cookie; a service identity
authenticates with an `Authorization: Bearer <token>` header carrying
its own token, distinct from the hash stored in the auth document. See [Command
and query service](../api/command-and-query-service.md) for the
session and bearer-token surface itself.

The command FIFO and Livestatus are unaffected by any of this: neither
reaches `-auth-config`, and both stay open exactly as described above.

## 4. `-read-only`: the posture for an untrusted audience

`-read-only` unmounts `CommandService` and `AdminService` entirely:
every request to either answers 404, whatever `-api-token`,
`-auth-config` or the `admin` role would otherwise allow. It also
refuses every Livestatus `COMMAND`, on either listener, with a 403,
since no command sink is wired when it is set. `QueryService`,
`AuthService`, `/metrics` and the dashboard stay exactly as described
above, and the dashboard hides every command control regardless of
role. It is refused at startup together with `-api-token`
(the token only authorizes commands, which `-read-only` removes) or
`-command-file` (the FIFO is a write surface); `-auth-config` is legal
alongside it, since people can still sign in and browse.

This is the recommended posture for a listener facing a public or
otherwise untrusted audience: a status page shared outside the team, a
demo instance, anything where showing state matters but nobody reaching
it should be able to change it. It removes the write surface at the
process level rather than relying on a role check per request.

## 5. The stated posture

Put a reverse proxy in front of anything exposed beyond localhost and
terminate TLS, authentication and network exposure there; jaque itself
does not do TLS. Treat the network a jaque process listens on as the
perimeter: `-listen`, `-livestatus-listen`, `-coordination` and the
FIFO's containing directory all sit behind whatever boundary (firewall,
private subnet, UNIX socket permissions) keeps untrusted clients out,
because jaque will not turn them away itself. Coordination credentials
are the most consequential of these: anyone who reaches that endpoint
has full control of the monitoring system, reaching well past the
engines' command surface; see the security considerations in
[Cluster and coordination](https://jaque.sh/docs/deployment/cluster-and-coordination.md).
The default, `-coordination memory`, runs in-process and exposes
nothing.

None of the above is a gap to be filed as a bug against a specific
endpoint. It is the current shape of the trust model.

## 6. Security considerations

This page is the security considerations section for the deployment
chapter. The pages that open a listener or handle a secret
([Topologies](https://jaque.sh/docs/deployment/topologies.md),
[Cluster and coordination](https://jaque.sh/docs/deployment/cluster-and-coordination.md),
[Kubernetes](https://jaque.sh/docs/deployment/kubernetes.md),
[Docker](https://jaque.sh/docs/deployment/docker.md)) each end by pointing back here.
