Split out of #101. Composition
settled by ADR-0019:
Prometheus, Loki, Grafana, Alloy.
ADR-0007 named three services and, in the same sentence, said config.alloy is
reused unchanged with "the two *_URL variables the only difference". That file
has two sinks — loki.write "grafana_loki" and prometheus.remote_write "metrics_service", lines 386 and 395. With no lab Prometheus the second has
nowhere to point but 10.0.99.20, which the Decision forbids. The three-item
list was an omission.
Blocked behind the guest to run it on, and behind the validators, both of which
are separate issues.
What it is
stacks/lab/compose.yaml, name: lab, following
stacks/observability/compose.yaml's shape — the shared x-service-defaults,
the pinned-plus-digested images, cap_drop: [ALL] and cgroup: host on
Alloy, the reasoning comments that make the settings survivable.
stacks/lab/alloy/ — config.alloy and docker.alloy, not
syslog.alloy: that listener belongs to the monitoring host alone.
secrets/lab.example.yaml and secrets/lab.sops.yaml.
stacks/lab/.env.example, stacks/lab/README.md.
- Rows in
docs/architecture.md and docs/observability.md.
The traps, in the order they will be hit
.sops.yaml needs a second creation_rule, and the tooling walks you past
it. The one rule there matches secrets/.*\.sops\.ya?ml$, so a recipient
added to it decrypts every file under secrets/ — the estate's SNMP
communities and Grafana admin password included. make secrets-init STACK=lab on the lab guest finds a different recipient already present and
prints "Add this key as an additional recipient by hand"
(scripts/bootstrap.sh:61-63), which is right for a second estate host and
exactly wrong here. A lab host that can decrypt the estate's credentials
inverts the trust direction ADR-0007 exists to protect. Wanted: a rule scoped
to the lab file, ordered before the general one, with only the guest's key.
render-config.sh's REQUIRED array is not stack-aware. It demands all
four SNMP communities and all four Alertmanager URLs from any stack, and the
lab has neither an snmp-exporter nor an Alertmanager. The renders themselves
are already guarded by if [[ -f ... ]]; the required-key list is not.
- Certificates. Grafana serves https from a leaf issued by the lab CA, and
render-config.sh refuses to render without it. A second Grafana means a
second leaf — from the same CA, not a second one.
- Retention has to be sized down. The estate budgets 12 GiB of Prometheus
and 30 days of Loki on a host that does nothing else. This one shares a
mirrored pair of 7.2K disks with a Windows domain, Wazuh, Velociraptor and
PBS. ADR-0007: "the fleet is sized against spindles, not RAM." Do not start
from the estate's numbers.
- Dashboards. The estate's seven are built on the estate's datasources and
metrics. What the lab needs is not a copy, and a copy is what will happen if
nobody decides otherwise first.
Done when
make up STACK=lab on the guest brings up four services, make validate
checks them, and the lab's Grafana is reachable from Hicks over the existing
50 → 30 rule with no new firewall rule anywhere.
Split out of #101. Composition
settled by ADR-0019:
Prometheus, Loki, Grafana, Alloy.
ADR-0007 named three services and, in the same sentence, said
config.alloyisreused unchanged with "the two
*_URLvariables the only difference". That filehas two sinks —
loki.write "grafana_loki"andprometheus.remote_write "metrics_service", lines 386 and 395. With no lab Prometheus the second hasnowhere to point but
10.0.99.20, which the Decision forbids. The three-itemlist was an omission.
Blocked behind the guest to run it on, and behind the validators, both of which
are separate issues.
What it is
stacks/lab/compose.yaml,name: lab, followingstacks/observability/compose.yaml's shape — the sharedx-service-defaults,the pinned-plus-digested images,
cap_drop: [ALL]andcgroup: hostonAlloy, the reasoning comments that make the settings survivable.
stacks/lab/alloy/—config.alloyanddocker.alloy, notsyslog.alloy: that listener belongs to the monitoring host alone.secrets/lab.example.yamlandsecrets/lab.sops.yaml.stacks/lab/.env.example,stacks/lab/README.md.docs/architecture.mdanddocs/observability.md.The traps, in the order they will be hit
.sops.yamlneeds a secondcreation_rule, and the tooling walks you pastit. The one rule there matches
secrets/.*\.sops\.ya?ml$, so a recipientadded to it decrypts every file under
secrets/— the estate's SNMPcommunities and Grafana admin password included.
make secrets-init STACK=labon the lab guest finds a different recipient already present andprints "Add this key as an additional recipient by hand"
(
scripts/bootstrap.sh:61-63), which is right for a second estate host andexactly wrong here. A lab host that can decrypt the estate's credentials
inverts the trust direction ADR-0007 exists to protect. Wanted: a rule scoped
to the lab file, ordered before the general one, with only the guest's key.
render-config.sh'sREQUIREDarray is not stack-aware. It demands allfour SNMP communities and all four Alertmanager URLs from any stack, and the
lab has neither an snmp-exporter nor an Alertmanager. The renders themselves
are already guarded by
if [[ -f ... ]]; the required-key list is not.render-config.shrefuses to render without it. A second Grafana means asecond leaf — from the same CA, not a second one.
and 30 days of Loki on a host that does nothing else. This one shares a
mirrored pair of 7.2K disks with a Windows domain, Wazuh, Velociraptor and
PBS. ADR-0007: "the fleet is sized against spindles, not RAM." Do not start
from the estate's numbers.
metrics. What the lab needs is not a copy, and a copy is what will happen if
nobody decides otherwise first.
Done when
make up STACK=labon the guest brings up four services,make validatechecks them, and the lab's Grafana is reachable from Hicks over the existing
50 → 30 rule with no new firewall rule anywhere.