Foundation for the feature program (groups are the scoping primitive alerts, ad-hoc
commands, bulk actions and patch/posture will target). Lean by design: named groups,
no hierarchy, no per-group RBAC.
- server_groups (uuid route key, unique name, color = a @theme token key coerced to a real
token on save) + server_server_group M2M pivot (cascade on delete; servers survive a group
delete). Server::groups() + a `scopeInGroup`.
- Servers\Groups full-page component, manage-fleet (admin) only — route can:-mw + mount()
abort_unless + a per-method gate() on every mutation (create/rename/setColor/toggleMember/
confirmDelete/deleteGroup), since /livewire/update does not re-run route mw. create/rename/
recolour/membership audit directly; DELETE goes through the signed single-use ConfirmToken +
the R5 confirm modal (which writes group.delete from the sealed descriptor, so the handler
does not double-audit). 'groupConfirmed' added to ConfirmToken::ACTIONS.
- /servers/groups is registered BEFORE /servers/{server} so "groups" isn't captured as a uuid.
- Servers\Index: a #[Url] group filter (uuid; shareable) — an unknown uuid falls back to all,
never errors. Filtering is open to every role; only management is admin-gated. A "Gruppen"
button (admin) on the fleet header; colour pills use a literal @theme token map (R3).
- lang/{de,en}/groups.php + audit.php group.* labels (de/en parity). No emoji.
19 new tests (model + component: uuid/colour-allow-list/relations/cascade, RBAC route gating,
create/rename/recolour/membership, delete-via-confirmed-token, forged-token no-op, filter).
638 tests green, Pint, lang parity, Codex-reviewed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
From the whole-codebase re-audit (deferred MEDIUM findings, Codex-reviewed):
- poller: CredentialVault enforces a credential's disabled_at lock only at connect(), but
PollMetrics reuses one long-lived SSH connection per server, so a credential disabled
mid-run kept streaming metrics over the already-open session. Add Server::withActiveCredential()
(whereHas credential whereNull disabled_at); the poller selects via it and prunes any cached
client whose server left the active set, so a revocation drops the server on the next tick and
closes its session promptly.
- prod compose: add security_opt no-new-privileges:true to every service + a generous pids_limit
(fork-bomb backstop). cap_drop/read_only are deliberately left out — they need a per-service
prod smoke test (nginx :80 bind, entrypoint chown) before enabling; documented inline.
- Caddyfile + compose: strengthen the TRUSTED_PROXY_CIDR guidance — an over-broad value in
external-TLS mode lets any client forge X-Forwarded-For and defeat the IP-keyed throttles + ban.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>