The standing root token this doc described is retired. Replaced the
2-flow table with the real 3-flow model now in place: routine AppRole
auth, a narrowly-scoped provisioner AppRole for occasional bootstrap
work, and a break-glass generate-root path for the rare cases the
provisioner deliberately can't cover. Alerting row corrected to note
the audit-log check fires on any root-policy token generically, not
a specific standing one.
Two command-block "evidence" snippets added earlier today pasted real,
live-captured values verbatim: the exact cert file path, the fleet's
actual CA fingerprint, today's real issuance timestamp, and the live
Vault role/principal name. All of it identified real infrastructure
in a way the rest of this repo's established redaction convention
(see docs/firewall-and-network.md's [admin workstation]-style
placeholders) already exists specifically to avoid. Redacted to
bracket placeholders; the structural/format evidence (cert type,
extensions, TTLs) stays genuine.
"No immediate disruption" was true for TLS certs and app secrets but not
for the SSH certificate refresh, which has no static-key fallback and
eventually locks the automation identity out entirely during a sustained
Vault outage. The "always talks through a local agent" law also had an
unstated exception worth naming rather than leaving implicit.
Vault's ssh-client-signer mount was undocumented here entirely. Adds it
as flow 2/5 rather than merging into Certificate Flow, since the actual
mechanism differs (own systemd timer, bypasses Vault Agent's render
cycle, only reuses its cached token) - and explains why that distinction
matters for debugging. Diagram updated to match: a third data-plane box
feeding fleet-wide sshd trust instead of a container.
The SSH CA mount (ssh-client-signer/) existed live in Vault but was
missing from this doc's capability list entirely. Adds it as a fourth
"What It Does" bullet with real role config and a live screenshot of
the secrets engines list, and refreshes the stale ACL policies
screenshot (was showing 15 policies, live is 18).
The old claim described two flows sharing one keypair under different
usernames as a deliberate scoping decision. That was actually a stale,
half-migrated credential mismatch, not a design choice - the real fix was
giving Kestra a dedicated keypair with zero relation to the ansible
automation identity's own key, so a leak of one can never expose the other.
Kestra's SSH plugin has no certificate support at all (a plain Java client,
confirmed via its own plugin schema), which is the actual reason it needs
its own static key regardless of what the fleet's ansible identity does.
Added the real current flow config (kestra_local_user/kestra_local_key) and
a screenshot of the live KV Store showing exactly those two entries. Also
refreshed the automation-self-update.timer example, which had gone stale.
Small, precise clarification - the table row was always worded
generically ('a host', not 'a VM') so it was never technically wrong,
just aspirational for LXC hosts before Terraform could provision them
natively. Now genuinely true for both, so it says so.
Live re-ran agent_control -l against the real Wazuh manager - the
netbird host (ID 013) is now enrolled and Active, matching the fleet's
real current host count.
Live-verified against the new NetBird host: firewalld genuinely locked
to 3 explicit rich-rules (zero blanket ports), yet 3 container-published
ports (netbird-proxy, netbird-server, hawser) sit completely outside
those rules and are reachable LAN-wide via Docker's own NAT - the exact
gap the doc already documented, now with a second real example on a
host provisioned this same week instead of just the one from before.
Fleet host count was stale (11, now 12 with the NetBird control plane).
Traffic law 8 (Management) now reflects the real new off-LAN admin path
via the zero-trust mesh, alongside the existing LAN-scoped firewall
rules - accurate about what's affected if the mesh goes down (on-LAN
access unaffected, off-LAN access specifically loses its only path).
README: real dedicated 'Zero Trust, Actually Implemented' section (general
concept -> NetBird as the concrete implementation -> link out), host/doc
count bumped to 12/16, NetBird tool bullet.
netbird-zero-trust.md: dropped the LXC-provisioning tangent (not relevant
to a doc about NetBird itself), added self-hosted-vs-SaaS rationale, a
real Addressing table, and expanded split-DNS mechanism detail. Also
genericized personal-device specifics (device types/exact split of
access) that were too specific for the public repo.
New file covering NetBird - a self-hosted WireGuard mesh giving every
fleet host and personal device a stable overlay identity, with a
genuinely-implemented zero-trust policy layer (deny-by-default, groups,
scoped setup keys, peer approval, posture checks).
- Why a mesh over a classic VPN concentrator, illustrated with a real
topology diagram (control plane handles coordination only; actual
data flows peer-to-peer, bypassing it entirely - the literal
distinction from a concentrator, drawn not just asserted)
- Ties back to today's iac-pipeline.md update: this LXC is the first
host ever built through the new Terraform LXC-native path, start to
finish, no manual provisioning step
- Real bring-up incident: gRPC/h2c requirements broke through a naive
proxy chain, and a proxying CDN 403s the agent outright - both root
causes documented, not just the fixes
- Evidence: 3 real command blocks (docker ps, live Caddyfile splitter
config, netbird status --detail from an enrolled peer), 2 real
screenshots (dashboard Peers list, Access Control Policies - port
counts in the screenshot match the real policy detail in the text)
- Second diagram (edge routing chain) for the specific gRPC/h2c/path
split detail, kept separate from the topology diagram since they show
genuinely different shapes of information
- Honest, non-rounded-up rollout status table - several phases still
explicitly open
- Sanitize-checked: no internal IPs/domains/personal email
Also: fixes iac-pipeline.md's forward-reference to this doc to use the
repo's actual link convention ([docs/x.md](x.md)), not descriptive
prose link text.
Terraform gained a second provider (bpg/proxmox) alongside the existing
telmate/proxmox, via a new containers.tf / proxmox_virtual_environment_container
resource - real, live-verified against the actual repo files. Corrects
the doc's previous "Terraform only manages virtual machines... cannot
create or manage LXC containers" claim, which is no longer true.
- Terraform section: VM/LXC comparison table, real containers.tf
excerpt, explains why LXCs need an extra pct-exec bootstrap step
(stock template has no sshd, since it never went through Packer)
- WARNING section: new paragraph noting the LXC-provisioning gap is
closed for new hosts going forward (the three existing exceptions
stay as preserved history, not retroactively fixed) - forward-links
to docs/netbird-zero-trust.md (next commit)
- Fixed now-stale section heading ("one resource type" -> "two
resource types")
- Sanitize-checked: no internal IPs/domains
- Replaced both mermaid diagrams with real Nextcloud diagrams; the
Ansible-stages rebuild caught a real omission - the old diagram showed
only 2 stages (init/compliance) when there are genuinely 3
(provisioning.yml exists too, currently just clamav/rocky-docker)
- Live-verified against the real iac repo: roles_metadata.yml (9
cis_hardening + 6 other compliance roles, 1 provisioning role),
kickstart partitioning/packages/SSH config, both Packer bootstrap
scripts, terraform.tfvars, pct list on both hypervisors, a real
rendered host_tags.yml
- KB fix: LXC Strategy note's PBS row was wrong, not just stale - PBS
genuinely has a populated vm_tags list and live compliance roles
(confirmed via real artifacts on the host itself), not 'never
Ansible-managed' as it said; corrected and re-verified via re-fetch
- Added real content not previously in the doc: the deliberately
permissive build-time SSH window closed by ssh_hardening, the
admin/ansible identity split baked into the image at build time, and a
third pipeline exception (PBS) that's genuinely different in kind from
the other two, including a real Rocky-vs-Debian PAM ordering bug found
onboarding it
- Considered two screenshot candidates (Proxmox template list, VM/LXC
mix) - declined as low-value; command evidence covers this
source-driven topic well on its own
- Sanitize-checked: no internal IPs/MACs/domains/personal info/forbidden
app names
- Replaced mermaid diagram with real Nextcloud diagram; rebuild caught a
real bug in the old one (showed Kopia/Bazarr flowing through the App
Router, which flows.json disproves - they bypass it entirely)
- Live-verified Mosquitto broker config/ACL, Node-RED flow routing (16
switch rules = 15 apps + unknown fallback, 2 dedicated endpoints),
mqtt-monitor.sh check count against the real script
- Named NetBox and Kestra directly (previously generic) after the real
Node-RED screenshot made them visible anyway; Postiz/nocodb-content
stay generic per standing instruction
- Added real evidence: routing-table command output, the real
topic-agnostic Inventory Parse function source, real Mosquitto
ACL/config, real check-count grep, a real Node-RED flow canvas
screenshot, and a real Mosquitto connection log screenshot (the
kestra-notify client is live proof of the MQTT-not-HTTP claim)
- KB fix: Ansible mqtt_client Role note was missing a 23rd real check
(crash-looping systemd units, distinct from the failed-units check,
added to the script after a real vault-agent crash-loop incident)
- Sanitize-checked: no internal IPs/domains/personal info/forbidden app
names
1. Live-verify: journald persistent-storage config confirmed on two
hosts (1G vs 500M cap, matches the doc's claim exactly), Alloy
active confirmed across all 11 fleet hosts.
2. Fix KB: Alloy Agent Model was stale - version had moved from
v1.18.0 to v1.18.1 on all 8 'newer' hosts since last checked,
role's own default still unreconciled at 1.17.1. Fixed and
confirmed via re-fetch.
3. Pulled the full Observability Pipeline tree: Alloy Agent Model,
Log Flow, Metrics Flow.
4. Replaced both mermaid diagrams (broke this repo's Nextcloud-SVG
convention) with real diagrams built through the proper round-trip.
5. Evidence: real journald config dumps, plus 3 real Grafana
screenshots (host metrics dashboard, detailed Loki log stream
showing job=technitium-dns/docker/system, and the fleet-wide log
volume dashboard) - one screenshot round caught and fixed an
unredacted internal IP before being used.
6/7. Compelling-proof test passes; sanitize-check clean.
Step 5 re-audit, completing the checklist gap that started this whole
detour. Added: the real hyper2 host_vars comment (matches the WARNING
callout's reasoning almost word for word), the real automation-self-
update.timer schedule, the real Kestra flow list (visually confirms
13 total flows), and app-update's real execution topology (visually
confirms wait_for_approval as an actual node, not just a diagram
concept).
Step 5 re-audit. The table itself (the file's central correction from
earlier this session) had no embedded evidence - only the corrected
count was asserted. Added the real, freshly-repulled provider list
confirming all 10 by actual model type, not just name.
Step 5 re-audit. The wazuh_agent_tuning root-cause comment adds a
specific fact not previously in the doc (9 of the fleet's agents
affected). The SCA dashboard screenshot is the doc's first visual
evidence - everything else was CLI/text - and shows a real 87% score,
directly proving the 'below 100% is an accepted tradeoff' claim with
an actual number instead of just asserting the policy.
Step 5 re-audit. Caddyfile contrast (skip-verify default vs Vault's
real cert verification block) shown as text, IP/domain genericized.
The cert-rebuild hook script's real 19-day-expiry story is shown as a
screenshot instead of a text excerpt - the script has a hardcoded P12
password in its body, so a screenshot with that one line blurred shows
the actual guard logic in full without a text-copy risk of the
password ever landing in a file.
Step 5 re-audit. Both verified live earlier this session but never
shown: the registry-token shared template entry (proves the shared-
path mechanism), and the technitium_api_token lookup (proves the
flagged admin/runtime-split exception with the actual Jinja syntax).
Step 5 re-audit. Both were verified live earlier this session but
never shown - the strategy breakdown backs the doc's own headline
stat directly, and the mariadb-latest entry proves the specific
shared-mirror mechanism rather than just describing it.
Two fixes from the step 5 re-audit:
1. The joplin/public-tool evidence blocks showed bracketed placeholders
inside function arguments ('joplin.[app-to-app zone]') - not valid
syntax, wouldn't produce the shown NXDOMAIN for the claimed reason.
Replaced with result tables showing the same real data honestly.
2. Added the real Technitium screenshot of the cleaned-up reverse zone.
Getting this screenshot caught a real methodology error: an earlier
claim of '2 real PTR records (automation, caddy)' was wrong - the
automation entry only resolved because I tested it by SSHing into
automation itself, which satisfied the lookup via its own local
/etc/hosts, never actually querying Technitium. Querying from a
different host returns NXDOMAIN, matching the screenshot's real
3-record zone (NS, SOA, one PTR) exactly. Only 1 real PTR record
exists. Same error existed in the KB, fixed there too.
Step 5 re-audit - real screenshot of the Vault tile's expanded detail
panel (UNSEALED, real lease/token/KV/secret-engine counts, TTL
distribution, CA cert expiry), showing the specialty-widget behavior
the Layout section describes but the existing screenshot doesn't show.
Captured after fixing the metrics-reader token issue found while
requesting this screenshot - first attempt genuinely showed a false
SEALED state, now shows the real healthy panel.
The block added earlier this pass showed a grep pattern with a
bracketed placeholder embedded inside the regex itself
('\.[service-to-service zone]') - not valid regex syntax, would never
produce the shown output if actually run. Replaced with the real
command (matching the _URL constant-naming pattern, which doesn't
need the domain at all) and its real output, domain suffix genericized
in output only. Separated the Authentik/Slack/fleetAudit/pfSense
reasoning into prose, not disguised as more grep output.
Step 5 re-audit - real screenshot showing the underlying table data
(50 records, all columns) that the Dashy heatmap is rendered from,
distinct from the two existing rendered-widget screenshots.
The two blocks added earlier this pass showed 'curl | jq' syntax that
was never actually run - the real command was 'curl | python3 -c'
via SSH. The output text was genuinely real either way, but the
command shown wasn't what produced it. Replaced with the literal
command actually executed, re-run fresh to confirm the output still
matches.
Step 5 re-pass under the strengthened evidence gate. The 14-source
count was the doc's own opening framing claim but had no evidence
attached - I'd verified it live earlier this session (grepping the
real function for every upstream host it calls) but never actually
showed the work. Added it.
Step 5 re-pass under the strengthened evidence gate. Both the
app-update duration correction and the Authentik 404 finding were
narrative-only, backed by data I'd already verified live but never
actually shown. Added the real NocoDB row notes as evidence blocks.
Left the other three bullets as-is - the file already has 2 real
screenshots plus these 2 data blocks, judged sufficient rather than
padding every bullet for its own sake.
Live-checked every flow's actual Kestra definition for a Pause task.
Only app-update has one; the other 6 main flows (os-update-vms,
container-update, disk-cleanup, os-update-pbs, os-update-pve,
os-update-pve-verify) run fully automatically with no human checkpoint
at all - their safety net is the automated health-check/rollback path,
not human approval. The doc previously presented check->pause->approve
as the universal pattern for all 13 flows and stated outright that
'nothing gets changed on the fleet without a human explicitly resuming
a paused flow', which was false for the flows that actually change
the fleet. The KB was already correct on this point.
Also replaced the mermaid diagram (broke this repo's established
Nextcloud-SVG convention) with a real diagram showing both patterns,
plus live evidence of the Pause-task check itself.
Live-queried Authentik's API directly: firefly (and emby, not in this
doc's table anyway) are both Proxy Provider only, no native OIDC/OAuth2
provider exists for either. Real count is 10 apps with native OIDC,
not the 11/12 both this doc and the KB previously implied. Same gap
existed in the Authentik/SSO Integration KB note, fixed there too.
Live-checked docker_stacks.yml directly. Total/dockerfile-strategy
counts (41/36) and the pinned_manual holdouts were already accurate.
But docker-socket-proxy and kodi's mariadb service were wrongly listed
as still hitting Docker Hub directly - both actually float a :latest
pull against a dedicated in-fleet mirror stack (docker-socket-proxy-
mirror, mariadb-latest) built via the normal dockerfile pattern. Fixed
the table and documented the real mechanism. Same gap existed in the
KB note, fixed there too.