From 118ace7cc302a53f8808200292ba9caaf750e811 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Sun, 19 Apr 2026 22:08:38 +0200 Subject: [PATCH] docs(alerting): update app-classes.md for Phase 9 REST controllers (Task 36) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add AlertRuleController, AlertController, AlertSilenceController, AlertNotificationController entries - Document inbox SQL visibility contract (target_user_ids/group_ids/role_names — no broadcast) - Add /api/v1/alerts/notifications/{id}/retry to flat-endpoint allow-list - Update SecurityConfig entry with alerting path matchers - Note attribute-key SQL injection validation contract on AlertRuleController Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/rules/app-classes.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.claude/rules/app-classes.md b/.claude/rules/app-classes.md index 135f4f02..d366e4e6 100644 --- a/.claude/rules/app-classes.md +++ b/.claude/rules/app-classes.md @@ -27,6 +27,7 @@ These paths intentionally stay flat (no `/environments/{envSlug}` prefix). Every | `/api/v1/catalog`, `/api/v1/catalog/{applicationId}` | Cross-env discovery is the purpose. Env is an optional filter via `?environment=`. | | `/api/v1/executions/{execId}`, `/processors/**` | Exchange IDs are globally unique; permalinks. | | `/api/v1/diagrams/{contentHash}/render`, `POST /api/v1/diagrams/render` | Content-addressed or stateless. | +| `/api/v1/alerts/notifications/{id}/retry` | Notification IDs are globally unique; no env routing needed. | | `/api/v1/auth/**` | Pre-auth; no env context exists. | | `/api/v1/health`, `/prometheus`, `/api-docs/**`, `/swagger-ui/**` | Server metadata. | @@ -50,6 +51,10 @@ ClickHouse is shared across tenants. Every ClickHouse query must filter by `tena - `AgentEventsController` — GET `/api/v1/environments/{envSlug}/agents/events` (lifecycle events; cursor-paginated, returns `{ data, nextCursor, hasMore }`; order `(timestamp DESC, insert_id DESC)`; cursor is base64url of `"{timestampIso}|{insert_id_uuid}"` — `insert_id` is a stable UUID column used as a same-millisecond tiebreak). - `AgentMetricsController` — GET `/api/v1/environments/{envSlug}/agents/{agentId}/metrics` (JVM/Camel metrics). Rejects cross-env agents (404) as defence-in-depth. - `DiagramRenderController` — GET `/api/v1/environments/{envSlug}/apps/{appSlug}/routes/{routeId}/diagram` (env-scoped lookup). Also GET `/api/v1/diagrams/{contentHash}/render` (flat — content hashes are globally unique). +- `AlertRuleController` — `/api/v1/environments/{envSlug}/alerts/rules`. GET list / POST create / GET `{id}` / PUT `{id}` / DELETE `{id}` / POST `{id}/enable` / POST `{id}/disable` / POST `{id}/render-preview` / POST `{id}/test-evaluate`. OPERATOR+ for mutations, VIEWER+ for reads. CRITICAL: attribute keys in `ExchangeMatchCondition.filter.attributes` are validated at rule-save time against `^[a-zA-Z0-9._-]+$` — they are later inlined into ClickHouse SQL. Webhook validation: verifies `outboundConnectionId` exists and `isAllowedInEnvironment`. Null notification templates default to `""` (NOT NULL constraint). Audit: `ALERT_RULE_CHANGE`. +- `AlertController` — `/api/v1/environments/{envSlug}/alerts`. GET list (inbox filtered by userId/groupIds/roleNames via `InAppInboxQuery`) / GET `/unread-count` / GET `{id}` / POST `{id}/ack` / POST `{id}/read` / POST `/bulk-read`. VIEWER+ for all. Inbox SQL: `? = ANY(target_user_ids) OR target_group_ids && ? OR target_role_names && ?` — requires at least one matching target (no broadcast concept). +- `AlertSilenceController` — `/api/v1/environments/{envSlug}/alerts/silences`. GET list / POST create / DELETE `{id}`. 422 if `endsAt <= startsAt`. OPERATOR+ for mutations, VIEWER+ for list. Audit: `ALERT_SILENCE_CHANGE`. +- `AlertNotificationController` — Dual-path (no class-level prefix). GET `/api/v1/environments/{envSlug}/alerts/{alertId}/notifications` (VIEWER+); POST `/api/v1/alerts/notifications/{id}/retry` (OPERATOR+, flat — notification IDs globally unique). Retry resets attempts to 0 and sets `nextAttemptAt = now`. ### Env admin (env-slug-parameterized, not env-scoped data) @@ -135,7 +140,7 @@ ClickHouse is shared across tenants. Every ClickHouse query must filter by `tena ## security/ — Spring Security -- `SecurityConfig` — WebSecurityFilterChain, JWT filter, CORS, OIDC conditional. `/api/v1/admin/outbound-connections/**` GETs permit OPERATOR in addition to ADMIN (defense-in-depth at controller level); mutations remain ADMIN-only. +- `SecurityConfig` — WebSecurityFilterChain, JWT filter, CORS, OIDC conditional. `/api/v1/admin/outbound-connections/**` GETs permit OPERATOR in addition to ADMIN (defense-in-depth at controller level); mutations remain ADMIN-only. Alerting matchers: GET `/environments/*/alerts/**` VIEWER+; POST/PUT/DELETE rules and silences OPERATOR+; ack/read/bulk-read VIEWER+; POST `/alerts/notifications/*/retry` OPERATOR+. - `JwtAuthenticationFilter` — OncePerRequestFilter, validates Bearer tokens - `JwtServiceImpl` — HMAC-SHA256 JWT (Nimbus JOSE) - `OidcAuthController` — /api/v1/auth/oidc (login-uri, token-exchange, logout)