Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/swarm-logs/README.md
atlas 0f53e9be6c swarm-logs: authenticate as the agent with its own queue secret
swarm-logs reads `swarm/agents/<agent>/queue` from the swarm secret
store with the agent's own store certificate (the read hive-agent makes
for its queue connection) and presents it as
`Bearer swarm-agent.<agent>.<secret>`, which the gateway's
swarm-logs-auth checks. On a 401 it reads the secret again and retries
once, so a secret re-minted between the read and the check does not
fail the query.

The OIDC mint against the hive's shared `hive-<h>-agent` client is gone
from swarm-logs, along with the unused `HIVE_AGENT_OIDC_CA_FILE` anchor.
logs.nix now gates on the store address instead of the queue's token
endpoint and wraps the binary with the paths queue-identity.nix gives
the harness; the host forwards the query URL wherever it forwards a
store identity.

The shared client is still delivered into containers; removing that is

Part of #4459.
2026-10-13 08:38:05 +02:00

74 lines
3.6 KiB
Markdown

# swarm-logs
An agent's CLI for the swarm log store. One verb — `swarm-logs query
'<LogsQL>'` — so reading logs pipes and greps like any other command
instead of being a hand-rolled token request plus a curl, per query.
Read-only and query-only. There is no ingest path here (that is the
collector's), and no `tail`: streaming is a different LogsQL endpoint with a
different response shape, left for a follow-up rather than folded in.
Distinct from `hive-metric`, the other half of the same surface from an
agent's seat: that one _writes_ a custom metric, this one _reads_ logs.
## ⚠️ There is no scoping
The gateway forwards the query unmodified, so **any authenticated caller
reads the whole swarm's logs** — every hive's, not only its own. That is the
rule in force rather than an omission here; when read permissions exist they
attach at the gateway location, not in this binary.
## How it authenticates
As the agent itself. On each query it reads the agent's own queue secret,
`swarm/agents/<agent>/queue`, from the swarm secret store with the agent's
store certificate (`nix/agent-modules/bao.nix`), the same read `hive-agent`
makes for its queue connection. It sends the secret as
`Authorization: Bearer swarm-agent.<agent>.<secret>`, spelled by
`swarm_queue_client::agent_token`.
At the gateway, nginx hands a bearer of that shape to `swarm-logs-auth`, which
compares it with the stored secret (`swarm-agent-verify`, the check the queue's
auth callout also uses). A 401 makes `swarm-logs` read the secret again and
retry once, which covers a secret re-minted between the read and the check.
🩸 The secret is never logged or rendered into an error.
## Configuration
Supplied by `nix/agent-modules/logs.nix`, which wraps the binary — the same
shape `swarmctl` is configured in, and for the same reason: every value is
derived from an option that module owns, so a default here would be an address
we _hope_ points at something.
| variable | what |
| --------------------------- | ------------------------------------------------ |
| `HIVE_AGENT_LOGS_QUERY_URL` | the LogsQL query endpoint |
| `HIVE_AGENT_NAME` | this agent's name; the store path and role |
| `BAO_ADDR` | the secret store's address |
| `BAO_CLIENT_CERT` | path of the agent's live store certificate |
| `BAO_CLIENT_KEY` | path of its key |
| `BAO_CACERT` | path of the store's CA; missing or empty is fine |
With no `HIVE_AGENT_LOGS_QUERY_URL` the binary says there is no log store;
with the URL and no store address it reports a deployment bug by name.
## Output
```console
$ swarm-logs query 'atlas-otel-probe' --limit 5
```
One log message per line by default (the record's `_msg`), which is what a
grep pattern is written against. `--format json` passes the store's NDJSON
through unmodified, for `_time`, `_stream` and `jq`.
A record the projection cannot understand — no string `_msg`, or not JSON at
all — is printed whole rather than dropped. Silently losing a row would make
an incomplete answer indistinguishable from a complete one.
A non-200 is reported with its body and a non-zero exit, never as an empty
result. That is the whole reason the gateway grew a separate machine route:
the operator's browser route answers an unauthenticated caller with authelia's
login page as a **200 with an HTML body**, and a client that reads only the
status code records a query that succeeded and matched nothing.