<#1008 Alerting system Stage 4: management API, in...
# osctrl
g
#1008 Alerting system Stage 4: management API, instant hot-reload, and CLI Pull request opened by javuto Alerting system Stage 4: management API, instant hot-reload, and CLI Problem Stages 1–3 delivered the full alerting pipeline (ingest → match → cooldown → deliver → history) gated behind
--alerts-enabled
, but rules and channels could only be changed by direct database edits, and those edits waited on the 5-minute snapshot refresh ticker. This PR makes the subsystem operator-manageable: a full CRUD API in osctrl-api, a service command that turns rule/channel edits into an instant hot-reload in osctrl-tls, and a CLI command group. Change Management API — new
cmd/api/handlers/alerts.go
(mirrors the log-sinks handler surface): • Rules: list / get / create / update / delete —
GET|POST /api/v1/alerts/rules
,
GET|PUT|DELETE /api/v1/alerts/rules/{id}
• Channels: list / get / types / create / update / delete — `GET /api/v1/alerts/channels[...]",
POST ...
, etc., plus
/channels/types
serving the registry so the SPA can render channel forms generically • History:
GET /api/v1/alerts/history?limit=
(newest first, capped at 500) • Secret handling identical to log sinks: channel configs redacted to
***
in reads unless an admin passes `reveal=1`; an update that submits the
***
placeholder merges the previously stored secret • All routes admin-only, audit-logged, and registered only when
--alerts-enabled
is set (nil manager → routes absent; direct handler calls → 503). The apply endpoint reuses the restart rate limiter — an alerts reload is the same class of privileged service-command operation • Request/response shapes added to
pkg/types
(
AlertRuleCreateRequest
,
AlertChannelCreateRequest
,
AlertChannelTypeSpec
,
AlertFieldSpec
) Instant hot-reload — service command plumbing: • `pkg/servicecommands`: new
reload-alerts
action in the allowlist • `cmd/api`:
POST /api/v1/alerts/apply
queues the command for osctrl-tls (202 + command ID, same response shape as log-sinks apply) • `cmd/tls`: the existing service-command watcher consumes the action — reloads the alert rule snapshot and resets the channel sender cache atomically (copy-on-write, in-flight matches finish against the old ruleset); warns and no-ops when alerting is disabled. The 5-minute ticker remains as a fallback path CLI — new
osctrl-cli alert
command group
: •
rules
/
rule-create
/
rule-delete
,
channels
/
channel-create
/
channel-delete
,
apply
(trigger hot-reload) • json / csv / pretty output consistent with the rest of the CLI; channel configs validated client-side against the registry before the POST; db-mode attempts fail with a clear "requires --api" error Supporting changes in
pkg/alerts
: • Exported
DecodeChannelIDs
/
EncodeChannelIDs
for API and CLI use •
RedactedChannelConfig
+
MergeChannelSecrets
(channel-registry equivalents of the logsinks helpers) •
ErrInvalidChannelConfig
sentinel, mapped to 400 by the API error router Validation •
go build ./...
,
go vet
,
gofmt
— clean •
golangci-lint
on all touched packages — 0 new issues (5 pre-existing in untouched files) • 10 new handler tests, all passing: non-admin 403 (rules + channels), disabled 503, full rule CRUD round-trip (create → 409 duplicate → list → get → update → delete → 404), invalid source 400, channel CRUD with redaction / reveal / secret-merge round-trip, malformed config 400, types endpoint, history listing, and apply → command consumable by osctrl-tls (asserted via the real servicecommands manager) • Full affected suites green:
cmd/api/handlers
,
cmd/api
,
cmd/cli
,
cmd/alerts
,
cmd/tls
,
pkg/servicecommands
• CLI smoke:
osctrl-cli alert --help
renders the full group Roadmap position Stage 4 of 7 (
alerts-api
). The alerting system is now fully operable end-to-end: rules and channels are managed via API/CLI, edits hot-reload into osctrl-tls within one poll cycle, and delivery lands in email/webhooks with per-channel audit history. Remaining: Stage 5 (node-inactive watcher), Stage 6 (SPA "Alerts" section), Stage 7 (load hardening + retention sweep). jmpsec/osctrl