choret: enforce discord-only provider type across notifications API and UI
- Added validation to reject non-discord provider types in create, update, test, and preview operations. - Updated the notifications form to automatically normalize non-discord types to discord. - Modified UI to display explicit messaging for deprecated and non-dispatch statuses for non-discord providers. - Enhanced tests to cover new validation logic and UI changes for provider types.
This commit is contained in:
+1
-1
@@ -237,7 +237,7 @@ Watch requests flow through your proxy in real-time. Filter by domain, status co
|
||||
|
||||
### 🔔 Notifications
|
||||
|
||||
Get alerted when it matters. Charon can notify you about certificate expirations, downtime events, and security incidents through multiple channels. Stay informed without constantly watching dashboards.
|
||||
Get alerted when it matters. Charon currently sends notifications through Discord webhooks using the Notify engine only. No legacy fallback path is used at runtime. Additional providers will roll out later in staged updates.
|
||||
|
||||
→ [Learn More](features/notifications.md)
|
||||
|
||||
|
||||
+21
-199
@@ -1,6 +1,6 @@
|
||||
# Notification System
|
||||
|
||||
Charon's notification system keeps you informed about important events in your infrastructure through multiple channels, including Discord, Slack, Gotify, Telegram, and custom webhooks.
|
||||
Charon's notification system keeps you informed about important events in your infrastructure. With flexible JSON templates and support for multiple providers, you can customize how and where you receive alerts.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -11,15 +11,13 @@ Notifications can be triggered by various events:
|
||||
- **Security Events**: WAF blocks, CrowdSec alerts, ACL violations
|
||||
- **System Events**: Configuration changes, backup completions
|
||||
|
||||
## Supported Services
|
||||
## Supported Service (Current Rollout)
|
||||
|
||||
| Service | JSON Templates | Native API | Rich Formatting |
|
||||
|---------|----------------|------------|-----------------|
|
||||
| **Discord** | ✅ Yes | ✅ Webhooks | ✅ Embeds |
|
||||
| **Slack** | ✅ Yes | ✅ Incoming Webhooks | ✅ Block Kit |
|
||||
| **Gotify** | ✅ Yes | ✅ REST API | ✅ Extras |
|
||||
| **Generic Webhook** | ✅ Yes | ✅ HTTP POST | ✅ Custom |
|
||||
| **Telegram** | ❌ No | ✅ Bot API | ⚠️ Markdown |
|
||||
|
||||
Additional providers are planned for later staged releases.
|
||||
|
||||
### Why JSON Templates?
|
||||
|
||||
@@ -43,7 +41,7 @@ JSON templates give you complete control over notification formatting, allowing
|
||||
|
||||
### JSON Template Support
|
||||
|
||||
For services supporting JSON (Discord, Slack, Gotify, Generic, Webhook), you can choose from three template options:
|
||||
For the currently supported service (Discord), you can choose from three template options:
|
||||
|
||||
#### 1. Minimal Template (Default)
|
||||
|
||||
@@ -157,167 +155,11 @@ Discord supports rich embeds with colors, fields, and timestamps.
|
||||
- `16776960` - Yellow (warning)
|
||||
- `3066993` - Green (success)
|
||||
|
||||
### Slack Webhooks
|
||||
## Planned Provider Expansion
|
||||
|
||||
Slack uses Block Kit for rich message formatting.
|
||||
|
||||
#### Basic Block
|
||||
|
||||
```json
|
||||
{
|
||||
"text": "{{.Title}}",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "header",
|
||||
"text": {
|
||||
"type": "plain_text",
|
||||
"text": "{{.Title}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "{{.Message}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Advanced Block with Context
|
||||
|
||||
```json
|
||||
{
|
||||
"text": "{{.Title}}",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "header",
|
||||
"text": {
|
||||
"type": "plain_text",
|
||||
"text": "🔔 {{.Title}}",
|
||||
"emoji": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "*Event:* {{.EventType}}\n*Message:* {{.Message}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"fields": [
|
||||
{
|
||||
"type": "mrkdwn",
|
||||
"text": "*Host:*\n{{.HostName}}"
|
||||
},
|
||||
{
|
||||
"type": "mrkdwn",
|
||||
"text": "*Time:*\n{{.Timestamp}}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "context",
|
||||
"elements": [
|
||||
{
|
||||
"type": "mrkdwn",
|
||||
"text": "Notification from Charon"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Slack Markdown Tips:**
|
||||
|
||||
- `*bold*` for emphasis
|
||||
- `_italic_` for subtle text
|
||||
- `~strike~` for deprecated info
|
||||
- `` `code` `` for technical details
|
||||
- Use `\n` for line breaks
|
||||
|
||||
### Gotify Webhooks
|
||||
|
||||
Gotify supports JSON payloads with priority levels and extras.
|
||||
|
||||
#### Basic Message
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "{{.Title}}",
|
||||
"message": "{{.Message}}",
|
||||
"priority": 5
|
||||
}
|
||||
```
|
||||
|
||||
#### Advanced Message with Extras
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "{{.Title}}",
|
||||
"message": "{{.Message}}",
|
||||
"priority": {{.Priority}},
|
||||
"extras": {
|
||||
"client::display": {
|
||||
"contentType": "text/markdown"
|
||||
},
|
||||
"client::notification": {
|
||||
"click": {
|
||||
"url": "https://your-charon-instance.com"
|
||||
}
|
||||
},
|
||||
"charon": {
|
||||
"event_type": "{{.EventType}}",
|
||||
"host_name": "{{.HostName}}",
|
||||
"timestamp": "{{.Timestamp}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Gotify Priority Levels:**
|
||||
|
||||
- `0` - Very low
|
||||
- `2` - Low
|
||||
- `5` - Normal (default)
|
||||
- `8` - High
|
||||
- `10` - Very high (emergency)
|
||||
|
||||
#### Gotify Token Hygiene (Required)
|
||||
|
||||
- Never echo, log, or return Gotify token values.
|
||||
- Never expose tokenized endpoint query strings (for example,
|
||||
`...?token=...`) in logs, diagnostics, examples, screenshots,
|
||||
tickets, or reports.
|
||||
- Redact or mask query parameters in diagnostics and examples before sharing.
|
||||
- Use write-only token inputs in operator workflows.
|
||||
- Store tokens in environment variables or a secret manager, not plaintext notes.
|
||||
- Validate endpoint connectivity over HTTPS only.
|
||||
- Rotate tokens immediately on suspected exposure.
|
||||
|
||||
### Generic Webhooks
|
||||
|
||||
For custom integrations, use any JSON structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"notification": {
|
||||
"type": "{{.EventType}}",
|
||||
"level": "{{.Severity}}",
|
||||
"title": "{{.Title}}",
|
||||
"body": "{{.Message}}",
|
||||
"metadata": {
|
||||
"host": "{{.HostName}}",
|
||||
"timestamp": "{{.Timestamp}}",
|
||||
"source": "charon"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Additional providers (for example Slack, Gotify, Telegram, and generic webhooks)
|
||||
are planned for later staged releases. This page will be expanded as each
|
||||
provider is validated and released.
|
||||
|
||||
## Template Variables
|
||||
|
||||
@@ -386,12 +228,16 @@ Template: detailed (or custom)
|
||||
4. Test the notification
|
||||
5. Save changes
|
||||
|
||||
If you previously used non-Discord provider types, keep those entries as
|
||||
historical records only. They are not active runtime dispatch paths in the
|
||||
current rollout.
|
||||
|
||||
### Testing Your Template
|
||||
|
||||
Before saving, always test your template:
|
||||
|
||||
1. Click **"Send Test Notification"** in the provider form
|
||||
2. Check your notification channel (Discord/Slack/etc.)
|
||||
2. Check your Discord channel
|
||||
3. Verify formatting, colors, and all fields appear correctly
|
||||
4. Adjust template if needed
|
||||
5. Test again until satisfied
|
||||
@@ -419,7 +265,7 @@ Before saving, always test your template:
|
||||
1. ✅ Provider is enabled
|
||||
2. ✅ Event type is configured for notifications
|
||||
3. ✅ Webhook URL is correct
|
||||
4. ✅ Service (Discord/Slack/etc.) is online
|
||||
4. ✅ Discord is online
|
||||
5. ✅ Test notification succeeds
|
||||
6. ✅ Check Charon logs for errors: `docker logs charon | grep notification`
|
||||
|
||||
@@ -440,26 +286,6 @@ Before saving, always test your template:
|
||||
}
|
||||
```
|
||||
|
||||
### Slack Message Appears Plain
|
||||
|
||||
**Cause:** Block Kit requires specific formatting.
|
||||
|
||||
**Solution:** Use `blocks` array with proper types:
|
||||
|
||||
```json
|
||||
{
|
||||
"blocks": [
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "{{.Message}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Start Simple
|
||||
@@ -481,19 +307,17 @@ Consistent colors help quickly identify severity:
|
||||
|
||||
### 4. Group Related Events
|
||||
|
||||
Configure multiple providers for different event types:
|
||||
Use separate Discord providers for different event types:
|
||||
|
||||
- Critical alerts → Discord (with mentions)
|
||||
- Info notifications → Slack (general channel)
|
||||
- All events → Gotify (personal alerts)
|
||||
- Info notifications → Discord (general channel)
|
||||
- Security events → Discord (security channel)
|
||||
|
||||
### 5. Rate Limit Awareness
|
||||
|
||||
Be mindful of service limits:
|
||||
|
||||
- **Discord**: 5 requests per 2 seconds per webhook
|
||||
- **Slack**: 1 request per second per workspace
|
||||
- **Gotify**: No strict limits (self-hosted)
|
||||
|
||||
### 6. Keep Templates Maintainable
|
||||
|
||||
@@ -503,7 +327,7 @@ Be mindful of service limits:
|
||||
|
||||
## Advanced Use Cases
|
||||
|
||||
### Multi-Channel Routing
|
||||
### Routing by Severity
|
||||
|
||||
Create separate providers for different severity levels:
|
||||
|
||||
@@ -512,11 +336,11 @@ Provider: Discord Critical
|
||||
Events: uptime_down, ssl_failure
|
||||
Template: Custom with @everyone mention
|
||||
|
||||
Provider: Slack Info
|
||||
Provider: Discord Info
|
||||
Events: ssl_renewal, backup_success
|
||||
Template: Minimal
|
||||
|
||||
Provider: Gotify All
|
||||
Provider: Discord All
|
||||
Events: * (all)
|
||||
Template: Detailed
|
||||
```
|
||||
@@ -554,8 +378,6 @@ Forward notifications to automation tools:
|
||||
## Additional Resources
|
||||
|
||||
- [Discord Webhook Documentation](https://discord.com/developers/docs/resources/webhook)
|
||||
- [Slack Block Kit Builder](https://api.slack.com/block-kit)
|
||||
- [Gotify API Documentation](https://gotify.net/docs/)
|
||||
- [Charon Security Guide](../security.md)
|
||||
|
||||
## Need Help?
|
||||
|
||||
+432
-296
@@ -1,384 +1,520 @@
|
||||
---
|
||||
post_title: "Current Spec: Governance Update — DoD GORM Scan + Gotify Token Hygiene"
|
||||
post_title: "Current Spec: Discord Notify-Only Dispatch (No Legacy Fallback)"
|
||||
categories:
|
||||
- actions
|
||||
- testing
|
||||
- security
|
||||
- governance
|
||||
tags:
|
||||
- dod
|
||||
- gorm
|
||||
- gotify
|
||||
- documentation
|
||||
- planning
|
||||
summary: "Governance-only plan to explicitly enforce conditional GORM scanner execution in DoD and add policy/operational controls that prevent Gotify token exposure while preserving token validation workflows."
|
||||
post_date: 2026-02-20
|
||||
- discord
|
||||
- notifications
|
||||
- notify-rollout
|
||||
- migration
|
||||
- e2e
|
||||
summary: "Authoritative implementation plan for Discord Notify-only dispatch with explicit retirement of legacy fallback ambiguity, deterministic migration behavior, and proof-oriented testing."
|
||||
post_date: 2026-02-21
|
||||
---
|
||||
|
||||
## Active Plan: Governance Update — DoD GORM Scan + Gotify Token Hygiene
|
||||
## Active Plan: Discord Notify-Only Dispatch (No Legacy Fallback)
|
||||
|
||||
Date: 2026-02-20
|
||||
Date: 2026-02-21
|
||||
Status: Active and authoritative
|
||||
Scope Type: Documentation/Governance only (no product feature refactor)
|
||||
Scope Type: Product implementation plan (frontend + backend + migration + verification)
|
||||
|
||||
## 1) Introduction
|
||||
|
||||
This plan defines the implementation to satisfy the updated requirement direction:
|
||||
|
||||
- Do not rely on legacy fallback engine for Discord dispatch.
|
||||
- Discord payloads saved in DB must be sent by Notify engine directly.
|
||||
- Remove ambiguity where delivery success could still come through legacy path.
|
||||
- Keep rollout scope focused on Discord-only cutover without unrelated refactors.
|
||||
|
||||
This plan is intentionally scoped to Discord-only dispatch/runtime behavior, fallback retirement, and auditable proof that no hidden legacy path remains.
|
||||
|
||||
## 2) Research Findings
|
||||
|
||||
### 2.1 Frontend provider-type control points (exact files)
|
||||
|
||||
- `frontend/src/pages/Notifications.tsx`
|
||||
- Provider type is normalized to Discord (`DISCORD_PROVIDER_TYPE`) before submit/test/preview.
|
||||
- Provider dropdown currently renders Discord-only option, and request payloads are forced to Discord.
|
||||
- `frontend/src/api/notifications.ts`
|
||||
- `withDiscordType(...)` normalizes outbound payload type to Discord.
|
||||
- Interface remains `type: string`, requiring backend to remain authoritative for no-fallback invariants.
|
||||
- `frontend/src/pages/__tests__/Notifications.test.tsx`
|
||||
- Must verify Discord-only create/edit/test behavior and avoid implying fallback rescue semantics.
|
||||
- `tests/settings/notifications.spec.ts`
|
||||
- Includes coverage for Discord behavior and deprecated non-Discord rendering semantics.
|
||||
|
||||
### 2.2 Backend/provider type validation/default/control points (exact files)
|
||||
|
||||
- `backend/internal/api/handlers/notification_provider_handler.go`
|
||||
- Enforces Discord-only on create/update and blocks enabling deprecated non-Discord providers.
|
||||
- `backend/internal/services/notification_service.go`
|
||||
- Enforces Discord-only for provider lifecycle/test and skips non-Discord dispatch.
|
||||
- Contains explicit legacy-fallback-disabled sentinel/error path that must stay fail-closed.
|
||||
- Includes `EnsureNotifyOnlyProviderMigration(...)` for deterministic cutover state.
|
||||
- `backend/internal/services/enhanced_security_notification_service.go`
|
||||
- `SendViaProviders(...)` dispatches only to Discord providers in current rollout stage.
|
||||
- `dispatchToProvider(...)` still has non-Discord branches and is a key ambiguity-removal target.
|
||||
- `backend/internal/models/notification_provider.go`
|
||||
- `Type` comment still lists non-Discord types.
|
||||
- `Engine` and URL comments still include retired legacy-notifier URL semantics.
|
||||
- `backend/internal/notifications/engine.go`
|
||||
- Defines a retired legacy notification engine constant.
|
||||
- `backend/internal/notifications/router.go`
|
||||
- Legacy engine branch remains.
|
||||
- `backend/internal/notifications/feature_flags.go`
|
||||
- Retired fallback flag key remains in feature-flag wiring.
|
||||
- `backend/internal/api/handlers/feature_flags_handler.go`
|
||||
- Still exposes a retired legacy fallback key in defaults and update path guards.
|
||||
- `backend/internal/api/routes/routes.go`
|
||||
- Notification services are wired via both `NotificationService` and
|
||||
`EnhancedSecurityNotificationService`; migration path still references transitional behavior.
|
||||
|
||||
### 2.3 Exact backend/frontend files impacted by fallback removal
|
||||
|
||||
Backend files:
|
||||
|
||||
### 1) Introduction
|
||||
- `backend/internal/notifications/engine.go`
|
||||
- `backend/internal/notifications/router.go`
|
||||
- `backend/internal/notifications/feature_flags.go`
|
||||
- `backend/internal/api/handlers/feature_flags_handler.go`
|
||||
- `backend/internal/services/notification_service.go`
|
||||
- `backend/internal/services/enhanced_security_notification_service.go`
|
||||
- `backend/internal/api/handlers/notification_provider_handler.go`
|
||||
- `backend/internal/api/routes/routes.go`
|
||||
- `backend/internal/models/notification_provider.go`
|
||||
|
||||
This plan defines a documentation-only governance update for Charon. It focuses on two outcomes:
|
||||
Frontend files:
|
||||
|
||||
1. Confirm and enforce that local Definition of Done (DoD) explicitly requires GORM security scan execution when backend model/database-related changes occur.
|
||||
2. Establish explicit policy and operational guidance to prevent Gotify token exposure while preserving secure token validation.
|
||||
- `frontend/src/pages/Notifications.tsx`
|
||||
- `frontend/src/api/notifications.ts`
|
||||
|
||||
This plan intentionally excludes application feature refactors, endpoint behavior changes, and data model changes.
|
||||
### 2.4 Dependency/runtime current state (legacy notifier)
|
||||
|
||||
### 2) Scope and Assumptions
|
||||
- `backend/go.mod`: no active legacy notifier dependency.
|
||||
- `backend/go.sum`: no active legacy notifier entry found.
|
||||
- Direct import/call in backend source currently not present.
|
||||
- Legacy strings/constants/tests remain in runtime code paths and tests.
|
||||
- Historical artifacts and cached module content may contain legacy notifier references but are not
|
||||
production dependencies.
|
||||
|
||||
#### In Scope
|
||||
### 2.5 Firefox failing specs context
|
||||
|
||||
- Update governance markdown under `.github/instructions/**`.
|
||||
- Update operating-agent markdown under `.github/agents/**` where DoD/security verification behavior is defined.
|
||||
- Update security governance docs outside `.github` only where required to keep policy alignment and operator guidance clear.
|
||||
- Provide explicit wording for:
|
||||
- Conditional GORM scan gate in DoD.
|
||||
- Gotify token secrecy controls (no echo, no logs, no API response exposure).
|
||||
- Practical hardening recommendations.
|
||||
- Artifact `playwright-output/manual-dns-targeted/.last-run.json` contains 16 failed test IDs only; suite attribution must come from Playwright report/traces, not this file alone.
|
||||
- Separate summary `FIREFOX_E2E_FIXES_SUMMARY.md` references prior Firefox remediation work.
|
||||
- User-reported “5 failing Firefox specs” is treated as active triage input and must be
|
||||
classified at execution time into in-scope vs out-of-scope suites (defined below).
|
||||
|
||||
#### Out of Scope
|
||||
## 3) Requirements (EARS)
|
||||
|
||||
- Backend/frontend feature implementation.
|
||||
- API contract/code changes.
|
||||
- DB schema/migration changes.
|
||||
- Test-suite refactors beyond documentation references.
|
||||
- R1: WHEN opening notification provider create/edit UI, THE SYSTEM SHALL offer only `discord`
|
||||
in provider type selection for this rollout.
|
||||
- R2: WHEN create/update/test notification provider API endpoints receive a non-Discord type,
|
||||
THE SYSTEM SHALL reject with deterministic validation error.
|
||||
- R3: WHILE legacy non-Discord providers exist in DB, THE SYSTEM SHALL handle them per explicit
|
||||
compatibility policy without enabling dispatch.
|
||||
- R4: WHEN verifying runtime and dependency state, THE SYSTEM SHALL produce auditable evidence
|
||||
that the retired legacy notifier is not installed, linked, or used by active runtime code paths.
|
||||
- R5: IF additional services are introduced in future, THEN THE SYSTEM SHALL enable them
|
||||
behind explicit validation gates and staged rollout controls.
|
||||
- R6: WHEN Discord notifications are delivered successfully, THE SYSTEM SHALL provide evidence that Notify path handled dispatch and not a legacy fallback path.
|
||||
- R7: IF rollback is required, THEN THE SYSTEM SHALL use explicit rollback controls without reintroducing hidden legacy fallback behavior.
|
||||
|
||||
#### Assumptions
|
||||
## 4) Technical Specification
|
||||
|
||||
- Existing GORM scanner exists and is runnable (`Lint: GORM Security Scan`, `pre-commit ... gorm-security-scan`, `scripts/scan-gorm-security.sh`).
|
||||
- DoD governance is currently distributed across instruction and agent files and must remain consistent.
|
||||
- Gotify continues to be supported in notification docs and needs explicit secret-handling rules documented.
|
||||
### 4.1 Discord-only enforcement strategy (defense in depth)
|
||||
|
||||
### 3) Research Findings
|
||||
#### Layer A: UI/input restriction
|
||||
|
||||
#### Current DoD/GORM Position
|
||||
- `frontend/src/pages/Notifications.tsx`
|
||||
- Replace provider type dropdown options with a single option: `discord`.
|
||||
- Keep visible label semantics intact for accessibility.
|
||||
- Prevent stale-form values from preserving non-Discord types in edit mode.
|
||||
|
||||
- `testing.instructions.md` already includes a GORM Security Validation section and states scanner pass as DoD requirement.
|
||||
- `copilot-instructions.md` DoD section is comprehensive but does not currently elevate the GORM scan as an explicit conditional DoD step tied to backend model/database touchpoints.
|
||||
- Agent-level DoD ownership exists in `Management.agent.md`; however, explicit conditional GORM gate language is incomplete and should be made unambiguous.
|
||||
#### Layer B: Frontend request contract hardening
|
||||
|
||||
#### Current Gotify/Token Guidance Position
|
||||
- `frontend/src/api/notifications.ts`
|
||||
- Frontend request contract SHALL submit `type` as `discord` only; client must reject or normalize any stale non-discord value before submit.
|
||||
|
||||
- Notification/security documentation includes Gotify payload examples but does not centrally enforce a strict token non-exposure policy with explicit “never echo/log/return” phrasing.
|
||||
- Security instruction files contain generic secret guidance but lack Gotify-specific operational controls and validation-safe masking patterns.
|
||||
#### Layer C: API handler validation (authoritative gate)
|
||||
|
||||
#### Governance Surfaces Identified for Update
|
||||
- `backend/internal/api/handlers/notification_provider_handler.go`
|
||||
- Enforce `req.Type == "discord"` for all create/update paths (not only security-event cases).
|
||||
- Return stable error code/message for non-Discord type attempts.
|
||||
|
||||
##### Primary (`.github/instructions/**`)
|
||||
#### Layer D: Service-layer invariant
|
||||
|
||||
1. `.github/instructions/copilot-instructions.md`
|
||||
2. `.github/instructions/testing.instructions.md`
|
||||
3. `.github/instructions/security-and-owasp.instructions.md`
|
||||
- `backend/internal/services/notification_service.go`
|
||||
- Add service-level type guard for create/update/test/send paths to reject non-Discord.
|
||||
- Keep this guard even if handler validation exists (defense in depth).
|
||||
|
||||
##### Primary (`.github/agents/**`)
|
||||
#### Layer E: Dispatch/runtime invariant
|
||||
|
||||
4. `.github/agents/Management.agent.md`
|
||||
5. `.github/agents/Backend_Dev.agent.md`
|
||||
6. `.github/agents/QA_Security.agent.md`
|
||||
- `backend/internal/services/enhanced_security_notification_service.go`
|
||||
- Maintain dispatch as Discord-only.
|
||||
- Remove non-Discord dispatch branches from active runtime logic for this phase.
|
||||
|
||||
##### Additional governance docs required for policy coherence
|
||||
### 4.2 Feature-flag policy change for legacy fallback
|
||||
|
||||
7. `SECURITY.md`
|
||||
8. `docs/security.md`
|
||||
9. `docs/features/notifications.md`
|
||||
Policy for `feature.notifications.legacy.fallback_enabled`:
|
||||
|
||||
### 4) Requirements (EARS)
|
||||
- Deprecate now.
|
||||
- Force false in reads and writes.
|
||||
- Immutable false: never true at runtime.
|
||||
- Reject `true` update attempts with deterministic API error.
|
||||
- Keep compatibility aliases read-only and forced false during transition.
|
||||
- Remove `feature.notifications.legacy.fallback_enabled` from all public/default flag responses in PR-3 of this plan, and remove all retired env aliases in the same PR. No compatibility-window extension is allowed without a new approved spec revision.
|
||||
|
||||
- R1: WHEN a change touches `backend/internal/models/**` or backend database interaction paths, THE SYSTEM SHALL require execution of the GORM security scanner as a local DoD gate before task completion.
|
||||
- R2: IF the GORM scanner reports CRITICAL or HIGH findings, THEN THE SYSTEM SHALL require remediation before DoD can be considered complete.
|
||||
- R3: WHEN documenting or operating Gotify provider configuration, THE SYSTEM SHALL prohibit token echoing, token logging, and token inclusion in API responses.
|
||||
- R4: IF token validation is needed for connectivity or health checks, THEN THE SYSTEM SHALL validate tokens without revealing raw token values and SHALL use masking/redaction in all human-visible outputs.
|
||||
- R5: WHILE governance documentation is updated, THE SYSTEM SHALL keep language consistent across instruction files, agent files, and operator-facing security docs.
|
||||
Migration implications:
|
||||
|
||||
### 5) Technical Specification (Documentation Contract)
|
||||
- Existing settings row for legacy fallback is normalized to false if present.
|
||||
- Transition period allows key visibility as false-only for compatibility.
|
||||
- Final state keeps this key out of runtime dispatch decisions; there is no fallback runtime path.
|
||||
|
||||
This is a policy contract update, not an API/DB contract change.
|
||||
Legacy fallback control surface policy: any API write attempting to set legacy fallback true SHALL return `400` with code `LEGACY_FALLBACK_REMOVED`; any legacy fallback read endpoint/field retained temporarily SHALL return hard-false only and SHALL be removed in PR-3. No hidden route or config path may alter dispatch away from Notify, and Notify failures SHALL NOT reroute to a fallback engine.
|
||||
|
||||
#### 5.1 Policy Contract: Conditional GORM Scan Gate
|
||||
### 4.3 Runtime dispatch contract (Discord-only Notify path)
|
||||
|
||||
Define a single, repeated clause across governance docs:
|
||||
Contract:
|
||||
|
||||
- Trigger: backend model/database-related changes.
|
||||
- Required actions:
|
||||
- Run scanner via one approved command path.
|
||||
- Treat scanner as DoD process-blocking gate even when automation stage is manual (soft launch).
|
||||
- Use `--check` mode for gate decisions and pass/fail outcomes.
|
||||
- Block completion on unresolved CRITICAL/HIGH findings.
|
||||
- Discord dispatch source of truth is Notify engine path (`notify_v1`) only.
|
||||
- `NotificationService.SendExternal(...)` and `EnhancedSecurityNotificationService.SendViaProviders(...)` must fail closed for non-Discord delivery attempts.
|
||||
- No implicit retry/reroute to legacy engine on Notify failure.
|
||||
- Any successful Discord delivery during rollout must be attributable to Notify path execution.
|
||||
|
||||
Approved command references remain:
|
||||
#### Layer F: Optional DB invariant (recommended)
|
||||
|
||||
- `Lint: GORM Security Scan` (must execute scanner in check/gating mode)
|
||||
- `pre-commit run --hook-stage manual gorm-security-scan --all-files` (gating decision maps to check semantics)
|
||||
- `./scripts/scan-gorm-security.sh --check` (canonical gate command)
|
||||
- Add migration with DB-level check constraint for notification provider type to `discord`
|
||||
for newly created/updated rows in this rollout.
|
||||
- If DB constraint is deferred, service/API guards remain mandatory.
|
||||
|
||||
#### 5.1.1 Conditional Trigger Matrix (Include/Exclude)
|
||||
### 4.4 Data migration and compatibility policy for existing non-Discord providers
|
||||
|
||||
| Change Type | Trigger GORM Gate? | Rule |
|
||||
| --- | --- | --- |
|
||||
| `backend/internal/models/**` | Yes | Include |
|
||||
| Backend services/repositories with GORM query logic | Yes | Include |
|
||||
| DB migrations/seeding that change persistence behavior | Yes | Include |
|
||||
| Docs-only changes (`**/*.md`, governance docs) | No | Exclude |
|
||||
| Frontend-only changes (`frontend/**`) | No | Exclude |
|
||||
Policy decision for this rollout: **Deprecate + read-only visibility + non-dispatch**.
|
||||
|
||||
Gate decision rule:
|
||||
- Existing non-Discord rows are preserved for audit/history but cannot be newly created
|
||||
or converted back to active dispatch in this phase.
|
||||
- Compatibility behavior:
|
||||
- `enabled` forced false for non-Discord providers during migration reconciliation.
|
||||
- `migration_state` remains failed/deprecated for non-supported providers.
|
||||
- UI list behavior: render non-Discord rows as deprecated read-only rows with a clear non-dispatch badge.
|
||||
- API contract: non-Discord existing rows are always non-dispatch, non-enable, deletable, and return deterministic validation error on enable/type mutation attempts.
|
||||
- Explicitly block enable and type mutation attempts for non-Discord rows via API with deterministic validation errors.
|
||||
- Allow deletion of deprecated rows.
|
||||
|
||||
- IF any Include row matches, THEN scanner execution in check mode is mandatory DoD gate.
|
||||
- IF only Exclude rows match, THEN GORM gate is not required for that change set.
|
||||
Migration safety requirements:
|
||||
|
||||
#### 5.2 Policy Contract: Gotify Token Non-Exposure
|
||||
- Migration SHALL be idempotent.
|
||||
- Migration SHALL execute within an explicit transaction boundary.
|
||||
- Migration SHALL require a pre-migration backup before mutating provider rows.
|
||||
- Migration SHALL define and document a rollback procedure.
|
||||
- Migration SHALL persist audit log fields for every mutated provider row (including provider identifier, previous values, new values, mutation timestamp, and operation identifier).
|
||||
|
||||
Define explicit non-negotiables:
|
||||
Migration implementation candidates:
|
||||
|
||||
- Never print token to terminal output.
|
||||
- Never write token to application logs, debug logs, test output, screenshots, or reports.
|
||||
- Never include token in API response bodies, errors, or serialized DTOs.
|
||||
- Never expose tokenized endpoint query strings (for example `...?token=...`) in docs, diagnostics, examples, or logs.
|
||||
- Always redact URL query parameters in diagnostics/examples/log output before display or storage.
|
||||
- Validation operations must be non-revealing:
|
||||
- Use token length/prefix-independent masking in UX and diagnostics.
|
||||
- Store and process token as secret only.
|
||||
- Return generic validation outcomes (`valid`/`invalid` + reason category without token value).
|
||||
- `backend/internal/services/notification_service.go` (`EnsureNotifyOnlyProviderMigration`)
|
||||
- `backend/internal/services/enhanced_security_notification_service.go`
|
||||
- new migration file under backend migration path if schema/data migration is introduced.
|
||||
|
||||
#### 5.3 Cross-Document Precedence and Canonical Language
|
||||
### 4.5 Legacy notifier retirement/removal scope
|
||||
|
||||
To prevent policy drift, define precedence and reconciliation behavior:
|
||||
Targets to remove from active runtime/config surface:
|
||||
|
||||
1. Canonical governance source: `.github/instructions/testing.instructions.md` and `.github/instructions/security-and-owasp.instructions.md`
|
||||
2. Agent execution source: `.github/agents/**`
|
||||
3. Operator-facing/security guidance: `SECURITY.md`, `docs/security.md`, `docs/features/notifications.md`
|
||||
- `backend/internal/notifications/engine.go` legacy engine constant
|
||||
- `backend/internal/notifications/router.go` legacy engine branch
|
||||
- `backend/internal/notifications/feature_flags.go` legacy flag constant
|
||||
- `backend/internal/api/handlers/feature_flags_handler.go` retired legacy flag exposure
|
||||
- `backend/internal/models/notification_provider.go` retired legacy notifier comments/engine semantics
|
||||
- `backend/internal/services/notification_service.go` legacy naming/comments/hooks
|
||||
- `backend/internal/services/enhanced_security_notification_service.go` non-Discord dispatch branches in `dispatchToProvider`
|
||||
- `frontend/src/pages/Notifications.tsx` and `frontend/src/api/notifications.ts` to keep Discord-only UX/request contract aligned with backend fallback retirement
|
||||
|
||||
Reconciliation behavior:
|
||||
Out of scope for blocking completion:
|
||||
|
||||
- IF lower-precedence text conflicts with higher-precedence canonical language, THEN lower-precedence text must be updated to match canonical wording in the same PR.
|
||||
- Canonical terms must be reused verbatim for gate semantics:
|
||||
- “DoD process-blocking even in manual soft launch”
|
||||
- “check mode (`--check`) decides pass/fail gate”
|
||||
- “no tokenized URL/query exposure; redact query parameters in all diagnostics/examples/logs”
|
||||
- Archived docs/history artifacts under `docs/plans/archive/**` and similar archive/report folders.
|
||||
|
||||
#### 5.4 Operational Hardening Recommendations
|
||||
## 5) Dependency and Runtime Audit Plan (verify Discord-only runtime)
|
||||
|
||||
Document practical recommendations:
|
||||
Run and record all commands in implementation evidence.
|
||||
|
||||
- Use secret storage source (env var or secret manager), not plaintext docs/snippets.
|
||||
- Apply structured log redaction for keys containing `token`, `apikey`, `authorization`.
|
||||
- Use one-way visibility in UI forms (write-only fields, masked placeholders).
|
||||
- Rotate Gotify tokens on suspected exposure.
|
||||
- Validate over HTTPS only and avoid proxy/debug middleware that logs headers/bodies with secrets.
|
||||
Task-aligned proof gates (mandatory):
|
||||
|
||||
### 6) Exact Files and Sections to Change + Precise Wording Recommendations
|
||||
- Run task `Security: Verify SBOM` and capture output evidence.
|
||||
- Run task `Security: Scan Docker Image (Local)` and capture output evidence.
|
||||
- Store reproducibility evidence under `test-results/notify-rollout-audit/` (task outputs, command transcripts, and generated reports).
|
||||
|
||||
#### 6.1 `.github/instructions/copilot-instructions.md`
|
||||
### 5.1 Dependency manifests
|
||||
|
||||
Section: `## ✅ Task Completion Protocol (Definition of Done)`
|
||||
- `cd /projects/Charon/backend && go mod graph | grep -i "legacy_shoutrrr|feature.notifications.legacy.fallback_enabled|EngineLegacy|legacySendFunc|shoutrrr" || true`
|
||||
- `grep -i "legacy_shoutrrr|feature.notifications.legacy.fallback_enabled|EngineLegacy|legacySendFunc|shoutrrr" /projects/Charon/backend/go.mod /projects/Charon/backend/go.sum || true`
|
||||
- `grep -i "legacy_shoutrrr|feature.notifications.legacy.fallback_enabled|EngineLegacy|legacySendFunc|shoutrrr" /projects/Charon/package-lock.json /projects/Charon/frontend/package-lock.json 2>/dev/null || true`
|
||||
|
||||
Recommended insertion (new dedicated step after security scans or adjacent to testing gates):
|
||||
### 5.2 Source/runtime references (non-archive)
|
||||
|
||||
- **GORM Security Scan (Conditional, BLOCKING)**:
|
||||
- Trigger this step when changes include backend models or database interaction logic (for example: `backend/internal/models/**`, GORM query/service layers, migrations).
|
||||
- Run one of:
|
||||
- `Lint: GORM Security Scan`
|
||||
- `pre-commit run --hook-stage manual gorm-security-scan --all-files`
|
||||
- `./scripts/scan-gorm-security.sh --check`
|
||||
- DoD is blocked until scanner reports zero CRITICAL/HIGH findings.
|
||||
- `grep -RIn --exclude-dir=.git --exclude-dir=.cache --exclude-dir=docs --exclude-dir=playwright-output --exclude-dir=test-results "legacy_shoutrrr|feature.notifications.legacy.fallback_enabled|EngineLegacy|legacySendFunc|shoutrrr" /projects/Charon/backend /projects/Charon/frontend || true`
|
||||
|
||||
#### 6.2 `.github/instructions/testing.instructions.md`
|
||||
### 5.3 Built artifact/container runtime checks
|
||||
|
||||
Section: `## 4. GORM Security Validation (Manual Stage)`
|
||||
- Build image: `docker build -t charon:discord-only-audit /projects/Charon`
|
||||
- Module metadata in binary:
|
||||
- `docker run --rm charon:discord-only-audit sh -lc 'go version -m /app/charon 2>/dev/null | grep -i "legacy_shoutrrr|feature.notifications.legacy.fallback_enabled|EngineLegacy|legacySendFunc|shoutrrr" || true'`
|
||||
- Runtime filesystem/strings scan:
|
||||
- `docker run --rm charon:discord-only-audit sh -lc 'find /app /usr/local/bin -maxdepth 4 -type f | xargs grep -Iin "legacy_shoutrrr|feature.notifications.legacy.fallback_enabled|EngineLegacy|legacySendFunc|shoutrrr" 2>/dev/null || true'`
|
||||
|
||||
Recommended wording tighten (replace opening requirement sentence):
|
||||
### 5.4 Documentation references (active docs only)
|
||||
|
||||
- **Requirement:** For any change that touches backend models or database-related logic, the GORM Security Scanner is a mandatory local DoD gate and must pass with zero CRITICAL/HIGH findings.
|
||||
- `grep -RIn "legacy_shoutrrr|feature.notifications.legacy.fallback_enabled|EngineLegacy|legacySendFunc|shoutrrr" /projects/Charon/README.md /projects/Charon/ARCHITECTURE.md /projects/Charon/docs/features /projects/Charon/docs/security* || true`
|
||||
|
||||
Policy-vs-automation reconciliation clause (explicitly add under manual stage text):
|
||||
Audit pass criteria:
|
||||
|
||||
- “Manual stage” describes execution mechanism only; policy enforcement remains process-blocking for DoD. Gate decisions must use check semantics (`./scripts/scan-gorm-security.sh --check` or equivalent task wiring).
|
||||
- No active dependency references.
|
||||
- No active runtime/path references.
|
||||
- No active user-facing docs claiming retired multi-service notifier behavior for this rollout.
|
||||
|
||||
Recommended addition under “When to Run”:
|
||||
## 6) Implementation Plan (phased)
|
||||
|
||||
- **Mandatory Trigger Paths:**
|
||||
- `backend/internal/models/**`
|
||||
- Database interaction/services using GORM queries
|
||||
- Migration/seeding logic affecting model persistence behavior
|
||||
- **Explicit Exclusions:**
|
||||
- Docs-only changes
|
||||
- Frontend-only changes
|
||||
### Phase 1: Docker E2E rebuild decision and environment prep
|
||||
|
||||
#### 6.3 `.github/instructions/security-and-owasp.instructions.md`
|
||||
- Apply repo testing-rule decision for E2E container rebuild based on changed paths.
|
||||
- Rebuild/start E2E environment when required before any test execution.
|
||||
|
||||
Section: add new subsection under “General Guidelines”:
|
||||
### Phase 2: E2E first and failing-spec triage baseline
|
||||
|
||||
- **Gotify Token Protection (Explicit):**
|
||||
- Treat Gotify application tokens as secrets.
|
||||
- Do not echo tokens in CLI output.
|
||||
- Do not log tokens (application logs, debug logs, test logs).
|
||||
- Do not return tokens in API responses or error payloads.
|
||||
- For token validation, return only non-sensitive status and redact/mask any token-derived diagnostics.
|
||||
- Capture exact Firefox failures for current branch:
|
||||
- `cd /projects/Charon && npx playwright test --project=firefox`
|
||||
- Classify failing specs:
|
||||
- **In-scope**: notification provider tests (`tests/settings/notifications.spec.ts` and related).
|
||||
- **Out-of-scope**: manual DNS provider suites unless directly regressed by this change.
|
||||
- If user-reported 5 failing specs are not notifications, create/attach separate tracking item and
|
||||
do not block Discord-only implementation on unrelated suite failures.
|
||||
|
||||
#### 6.4 `.github/agents/Management.agent.md`
|
||||
### Phase 3: Local Patch Report preflight (mandatory before unit/coverage)
|
||||
|
||||
Section: `## DEFINITION OF DONE ##`
|
||||
- Run local patch coverage preflight and generate required artifacts:
|
||||
- `test-results/local-patch-report.md`
|
||||
- `test-results/local-patch-report.json`
|
||||
|
||||
Recommended insertion as explicit checklist item:
|
||||
### Phase 4: Backend hard gate (authoritative)
|
||||
|
||||
- **GORM Security Scan (Conditional Gate):**
|
||||
- If implementation touched backend models or database-interaction paths, confirm `QA_Security` (or responsible subagent) ran the GORM scanner and resolved all CRITICAL/HIGH findings before accepting completion.
|
||||
- Implement Discord-only validation in API handlers and service layer.
|
||||
- Remove/retire active legacy engine and flag exposure from runtime path.
|
||||
- Apply migration policy for existing non-Discord rows.
|
||||
|
||||
#### 6.5 `.github/agents/Backend_Dev.agent.md`
|
||||
### Phase 5: Conditional GORM security check gate
|
||||
|
||||
Section: `3. **Verification (Definition of Done)**`
|
||||
- Run GORM security scanner in check mode when backend model/database trigger paths are modified.
|
||||
- Gate completion on zero CRITICAL/HIGH findings for triggered changes.
|
||||
|
||||
Recommended insertion:
|
||||
### Phase 6: Frontend Discord-only UX
|
||||
|
||||
- **Conditional GORM Gate:** If task changes include model/database-related files or GORM query logic, run `pre-commit run --hook-stage manual gorm-security-scan --all-files` (or `./scripts/scan-gorm-security.sh --check`) and treat CRITICAL/HIGH findings as blocking.
|
||||
- Restrict dropdown/options to Discord only.
|
||||
- Align frontend type contract and submission behavior.
|
||||
- Ensure edit/create forms cannot persist stale non-Discord values.
|
||||
|
||||
#### 6.6 `.github/agents/QA_Security.agent.md`
|
||||
### Phase 7: Legacy notifier retirement cleanup + audit evidence
|
||||
|
||||
Sections: `<workflow>` Step 4 (Security Scanning) and reporting expectations
|
||||
- Remove active runtime legacy references listed in Section 4.5.
|
||||
- Execute dependency/runtime/doc audit commands in Section 5 and save evidence.
|
||||
|
||||
Recommended insertion:
|
||||
### Phase 8: Validation and release readiness
|
||||
|
||||
- Add explicit conditional check:
|
||||
- When backend model/database-related changes are in scope, run GORM scanner and report pass/fail as DoD gate.
|
||||
- Add Gotify token review check:
|
||||
- Verify no token appears in logs, test artifacts, screenshots, API examples, report output, or tokenized URL query strings.
|
||||
- Verify URL query parameters are redacted in diagnostics/examples/log artifacts.
|
||||
- Run validation sequence in required order (Section 7).
|
||||
- Confirm rollout note: additional providers remain disabled pending per-provider validation PRs.
|
||||
|
||||
#### 6.7 `SECURITY.md`
|
||||
## 7) Targeted Testing Strategy
|
||||
|
||||
Section candidate: under “Security Best Practices” or “Notification/Webhook Security” area
|
||||
Mandatory repo QA/test order for this plan:
|
||||
|
||||
Recommended addition:
|
||||
1. Docker E2E rebuild decision and required rebuild/start.
|
||||
2. E2E first (Firefox baseline + targeted notification suite).
|
||||
3. Local Patch Report preflight artifact generation.
|
||||
4. Conditional GORM security check gate (`--check` semantics when trigger paths change).
|
||||
5. CodeQL CI-aligned scans (Go and JS).
|
||||
6. Coverage gates (backend/frontend coverage thresholds and patch coverage review).
|
||||
7. Frontend type-check and backend/frontend build verification.
|
||||
|
||||
- **Gotify Token Hygiene:**
|
||||
- Gotify tokens are secrets and must never be echoed, logged, or returned by API responses.
|
||||
- Validation endpoints and troubleshooting guidance must use masked outputs only.
|
||||
- Rotate token immediately if exposure is suspected.
|
||||
### 7.1 Backend tests (in scope)
|
||||
|
||||
#### 6.8 `docs/security.md`
|
||||
|
||||
Section candidate: existing “Security Notifications” / “Gotify JSON Payload Example” area
|
||||
|
||||
Recommended addition:
|
||||
|
||||
- Add operational note block:
|
||||
- “Use write-only token input. Do not paste raw tokens into logs, screenshots, tickets, or chat.”
|
||||
- “Validation should confirm connectivity without exposing token value.”
|
||||
|
||||
#### 6.9 `docs/features/notifications.md`
|
||||
|
||||
Section candidate: “Gotify Webhooks” / setup + troubleshooting sections
|
||||
|
||||
Recommended addition:
|
||||
|
||||
- Add concise “Token Safety” subsection:
|
||||
- Never expose token in templates, payload examples, or troubleshooting snippets.
|
||||
- Use masked token representation in examples (e.g., `gtf_********`).
|
||||
- Prefer token rotation and re-test when compromise is suspected.
|
||||
|
||||
### 7) Implementation Plan (Phased)
|
||||
|
||||
#### Phase 1: Governance Baseline Diff (Documentation Research Validation)
|
||||
|
||||
- Validate each target file and section exists.
|
||||
- Map exact insertion points.
|
||||
- Confirm no overlap with archived docs to avoid scope creep.
|
||||
|
||||
Complexity: Low
|
||||
|
||||
#### Phase 2: DoD Enforcement Text Updates
|
||||
|
||||
- Apply conditional GORM scan language to instruction + agent DoD surfaces.
|
||||
- Ensure wording consistency between `.github/instructions/**` and `.github/agents/**`.
|
||||
|
||||
Complexity: Medium
|
||||
|
||||
#### Phase 3: Gotify Token Exposure Policy Updates
|
||||
|
||||
- Add explicit no echo/no log/no API exposure policy text.
|
||||
- Add practical validation-safe and hardening guidance.
|
||||
|
||||
Complexity: Medium
|
||||
|
||||
#### Phase 4: Governance Consistency Pass
|
||||
|
||||
- Verify terminology consistency:
|
||||
- “conditional gate”, “CRITICAL/HIGH blocking”, “masked/redacted output”.
|
||||
- Check that all cross-doc references remain valid.
|
||||
|
||||
Complexity: Low
|
||||
|
||||
#### Phase 5: Validation and Handoff
|
||||
|
||||
- Perform markdown lint/readability pass.
|
||||
- Confirm plan scope is governance-only (no implementation drift).
|
||||
- Prepare for Supervisor review.
|
||||
|
||||
Complexity: Low
|
||||
|
||||
### 8) Acceptance Criteria
|
||||
|
||||
1. `docs/plans/current_spec.md` is fully replaced with this governance-only plan.
|
||||
2. Plan lists exact target files under `.github/instructions/**` and `.github/agents/**` plus required additional governance docs.
|
||||
3. Plan includes explicit, precise wording recommendations per file/section.
|
||||
4. Plan defines conditional DoD enforcement for GORM scan tied to backend model/database-related changes.
|
||||
5. Plan defines explicit Gotify token non-exposure rules (no echo, no logging, no API response exposure) and validation-safe behavior.
|
||||
6. Plan includes practical hardening recommendations.
|
||||
7. Plan includes PR slicing strategy with explicit single-PR decision rationale.
|
||||
8. Plan excludes code-feature refactors and remains tightly scoped to governance/documentation updates.
|
||||
9. Plan explicitly defines policy-vs-automation behavior: manual scanner stage does not weaken DoD gate enforcement; check mode semantics are mandatory for gate decisions.
|
||||
10. Plan includes include/exclude matrix for conditional GORM scanner triggering.
|
||||
11. Plan includes explicit reconciliation edits for `testing.instructions` manual-soft-launch wording and `QA_Security.agent.md` Gotify token artifact checks.
|
||||
|
||||
### 9) PR Slicing Strategy
|
||||
|
||||
Decision: **Single PR**
|
||||
|
||||
#### Rationale
|
||||
|
||||
- Scope is text-only governance alignment across related markdown files.
|
||||
- Changes are logically cohesive (DoD gate clarity + token secrecy policy).
|
||||
- Splitting would increase review overhead and risk inconsistent policy wording.
|
||||
|
||||
#### Single-PR Scope
|
||||
|
||||
- All files in Section 6.
|
||||
- No code changes.
|
||||
- One validation pass for consistency.
|
||||
|
||||
#### Validation Gates (within the PR)
|
||||
|
||||
- Markdown renders cleanly.
|
||||
- Wording consistency across instruction/agent/operator docs.
|
||||
- No contradiction between DoD policies.
|
||||
|
||||
#### Rollback/Contingency
|
||||
|
||||
- If wording introduces confusion, rollback via single revert commit of doc-only changes.
|
||||
- If partial disagreement occurs, retain DoD gate updates and isolate Gotify wording revisions in follow-up doc PR.
|
||||
|
||||
### 10) Risks and Mitigations
|
||||
|
||||
- **Risk 1: Policy drift between instruction and agent docs**
|
||||
Mitigation: Use shared wording patterns and perform explicit consistency pass in Phase 4.
|
||||
|
||||
- **Risk 2: Overly broad wording accidentally implies code changes**
|
||||
Mitigation: Keep language explicitly “documentation governance only” and avoid implementation directives beyond policy.
|
||||
|
||||
- **Risk 3: Sensitive token examples accidentally remain in docs**
|
||||
Mitigation: Add explicit masked-example rule and require post-edit grep check for token-like literals.
|
||||
|
||||
- **Risk 4: Reviewer ambiguity on conditional trigger**
|
||||
Mitigation: Define concrete trigger paths (`backend/internal/models/**`, GORM DB logic, migrations).
|
||||
|
||||
### 11) Supervisor Handoff Notes
|
||||
|
||||
This plan is ready for Supervisor review as a governance-only documentation update. Review focus should verify:
|
||||
|
||||
- Clarity of conditional DoD gate for GORM scanner.
|
||||
- Sufficiency and practicality of Gotify token non-exposure policy, including token-in-URL query redaction requirements.
|
||||
- Cross-file wording consistency and absence of scope creep.
|
||||
- `backend/internal/api/handlers/notification_provider_blocker3_test.go`
|
||||
- Expand from security-event-only gating to global Discord-only gating.
|
||||
- `backend/internal/services/notification_service_test.go`
|
||||
- Ensure non-Discord create/update/test/send rejection.
|
||||
- `backend/internal/notifications/router_test.go`
|
||||
- Prove legacy fallback decision remains disabled and cannot be toggled into active use.
|
||||
- `backend/internal/services/notification_service_discord_only_test.go`
|
||||
- Prove Discord-only dispatch behavior and no successful fallback path.
|
||||
- `backend/internal/services/security_notification_service_test.go`
|
||||
- Prove security notifications route through Discord-only provider dispatch in rollout mode.
|
||||
- Add/adjust tests for migration policy behavior on existing non-Discord rows.
|
||||
|
||||
### 7.1.1 Notify-vs-fallback evidence points (required)
|
||||
|
||||
- Unit proof SHALL assert legacy dispatch hook call-count is exactly 0 for all Discord-success and Discord-failure scenarios.
|
||||
- Unit/integration proof SHALL assert Notify dispatch hook/request path is called for Discord deliveries (including `Content-Type: application/json` and deterministic provider-type guard behavior).
|
||||
- Negative proof SHALL assert non-Discord create/update/test attempts fail with stable error codes and cannot produce a successful delivery event.
|
||||
- CI gate SHALL fail if any test demonstrates delivery success through a legacy fallback path.
|
||||
|
||||
### 7.2 Frontend unit tests (in scope)
|
||||
|
||||
- `frontend/src/pages/__tests__/Notifications.test.tsx`
|
||||
- Assert only Discord option exists in provider-type select.
|
||||
- Remove/adjust non-Discord create assumptions.
|
||||
|
||||
### 7.3 E2E tests (in scope)
|
||||
|
||||
- `tests/settings/notifications.spec.ts`
|
||||
- Keep Discord create/edit/test flows.
|
||||
- Replace or retire non-Discord CRUD expectations for this phase.
|
||||
|
||||
### 7.4 Firefox failing specs handling (required context)
|
||||
|
||||
- If the current failing 5 specs are notification-related, they are in scope and must be fixed
|
||||
within this PR.
|
||||
- If they are unrelated (for example manual-dns-provider failures), track separately and avoid
|
||||
scope creep; do not mark as fixed under this provider-type change.
|
||||
|
||||
### 7.5 Suggested verification command set
|
||||
|
||||
- `cd /projects/Charon && npx playwright test --project=firefox tests/settings/notifications.spec.ts`
|
||||
- `cd /projects/Charon && npx playwright test --project=firefox --grep "Notification Providers"`
|
||||
- `cd /projects/Charon && bash scripts/local-patch-report.sh`
|
||||
- `cd /projects/Charon && ./scripts/scan-gorm-security.sh --check` (conditional on trigger paths)
|
||||
- `cd /projects/Charon && pre-commit run codeql-go-scan --all-files`
|
||||
- `cd /projects/Charon && pre-commit run codeql-js-scan --all-files`
|
||||
- `cd /projects/Charon && go test ./backend/internal/api/handlers -run Blocker3 -v`
|
||||
- `cd /projects/Charon && go test ./backend/internal/services -run Notification -v`
|
||||
- `cd /projects/Charon && scripts/go-test-coverage.sh`
|
||||
- `cd /projects/Charon/frontend && npm test -- Notifications`
|
||||
- `cd /projects/Charon && scripts/frontend-test-coverage.sh`
|
||||
- `cd /projects/Charon/frontend && npm run type-check && npm run build`
|
||||
- `cd /projects/Charon/backend && go build ./...`
|
||||
|
||||
## 8) Review of `.gitignore`, `.dockerignore`, `codecov.yml`, `Dockerfile`
|
||||
|
||||
### Findings and required updates decision
|
||||
|
||||
- `.gitignore`
|
||||
- No mandatory change required for feature behavior.
|
||||
- Optional: ensure any new audit evidence artifacts (if added) are either committed intentionally
|
||||
or ignored consistently.
|
||||
- `.dockerignore`
|
||||
- No mandatory change required for Discord-only behavior.
|
||||
- If adding migration/audit helper scripts needed at build time, ensure they are not excluded.
|
||||
- `codecov.yml`
|
||||
- No mandatory change required.
|
||||
- If new test files are added, verify they are not accidentally ignored by broad patterns.
|
||||
- `Dockerfile`
|
||||
- No direct package-level retired notifier dependency is declared.
|
||||
- No mandatory Dockerfile change for Discord-only behavior unless runtime audit shows residual
|
||||
retired notifier-linked binaries or references.
|
||||
|
||||
## 9) PR Slicing Strategy
|
||||
|
||||
Decision: **Multiple PRs (3 slices)** for safer rollout and easier rollback.
|
||||
|
||||
### PR-1: Backend guardrails + migration policy
|
||||
|
||||
- Scope:
|
||||
- API/service Discord-only validation.
|
||||
- Existing non-Discord compatibility policy implementation.
|
||||
- Legacy flag/engine runtime retirement in backend.
|
||||
- Primary files:
|
||||
- `backend/internal/api/handlers/notification_provider_handler.go`
|
||||
- `backend/internal/services/notification_service.go`
|
||||
- `backend/internal/services/enhanced_security_notification_service.go`
|
||||
- `backend/internal/notifications/engine.go`
|
||||
- `backend/internal/notifications/router.go`
|
||||
- `backend/internal/notifications/feature_flags.go`
|
||||
- `backend/internal/api/handlers/feature_flags_handler.go`
|
||||
- `backend/internal/models/notification_provider.go`
|
||||
- Gate:
|
||||
- Backend tests pass, API rejects non-Discord, migration behavior verified.
|
||||
- Rollback:
|
||||
- Revert PR-1 if existing-provider compatibility regresses.
|
||||
- Do not reintroduce fallback by setting legacy fallback flag true or restoring legacy dispatch routing as a hotfix.
|
||||
|
||||
### PR-2: Frontend Discord-only UX and tests
|
||||
|
||||
- Scope:
|
||||
- Dropdown/UI restriction to Discord only.
|
||||
- Frontend test updates for new provider-type policy.
|
||||
- Primary files:
|
||||
- `frontend/src/pages/Notifications.tsx`
|
||||
- `frontend/src/api/notifications.ts`
|
||||
- `frontend/src/pages/__tests__/Notifications.test.tsx`
|
||||
- `tests/settings/notifications.spec.ts`
|
||||
- Gate:
|
||||
- Dependency: PR-2 entry is blocked until PR-1 is merged.
|
||||
- Firefox notifications E2E targeted suite passes.
|
||||
- Exit: frontend request contract submits `type=discord` only and backend rejects non-Discord mutations.
|
||||
- Rollback:
|
||||
- Revert PR-2 without reverting backend safeguards.
|
||||
- No rollback action may re-enable hidden fallback behavior.
|
||||
|
||||
### PR-3: Legacy notifier retirement evidence + docs alignment
|
||||
|
||||
- Scope:
|
||||
- Execute audit commands, capture evidence, update active docs if needed.
|
||||
- Primary files:
|
||||
- `README.md`, `ARCHITECTURE.md`, relevant `docs/features/**` and `docs/security*` files (only if active references remain).
|
||||
- Gate:
|
||||
- Dependency: PR-3 entry is blocked until PR-2 is merged.
|
||||
- Entry blocker: runtime/dependency audit must pass before PR-3 can proceed.
|
||||
- Dependency/runtime/doc audit pass criteria met.
|
||||
- Exit: docs must reflect final backend/frontend state after PR-1 and PR-2 behavior.
|
||||
- Rollback:
|
||||
- Revert docs-only evidence changes without impacting runtime behavior.
|
||||
- Preserve explicit no-fallback runtime guarantees from earlier slices.
|
||||
|
||||
## 10) Rollback Strategy (No Hidden Fallback Ambiguity)
|
||||
|
||||
Allowed rollback mechanisms:
|
||||
|
||||
1. Revert entire rollout slice(s) to previous known-good commit/tag.
|
||||
2. Pause notifications by disabling delivery globally while keeping legacy fallback permanently disabled (`feature.notifications.legacy.fallback_enabled=false`, immutable). Rollback SHALL revert to a prior release tag/DB snapshot only; it SHALL NOT re-enable or simulate legacy fallback.
|
||||
3. Restore DB snapshot if migration state must be reverted.
|
||||
|
||||
Disallowed rollback mechanisms:
|
||||
|
||||
- Re-enable `feature.notifications.legacy.fallback_enabled` for runtime dispatch.
|
||||
- Reintroduce runtime `EngineLegacy` or implicit fallback retry paths.
|
||||
- Apply emergency patches that create dual-path ambiguity for Discord success attribution.
|
||||
|
||||
## 11) Risks and Mitigations
|
||||
|
||||
- Risk: Existing non-Discord providers silently break user expectations.
|
||||
- Mitigation: explicit deprecated/read-only policy and clear API errors.
|
||||
- Risk: Hidden runtime legacy paths remain while UI appears Discord-only.
|
||||
- Mitigation: backend-first enforcement and runtime audit commands.
|
||||
- Risk: Scope creep from unrelated Firefox failures.
|
||||
- Mitigation: strict in-scope/out-of-scope classification in Phase 1.
|
||||
- Risk: Future provider rollout pressure bypasses validation discipline.
|
||||
- Mitigation: staged enablement policy with per-provider validation gate.
|
||||
|
||||
## 12) Acceptance Criteria
|
||||
|
||||
1. Provider type dropdown in notifications UI exposes only Discord.
|
||||
2. API create/update/test rejects any non-Discord provider type with deterministic error.
|
||||
3. Service/runtime dispatch is Discord-only for this rollout and attributable to Notify path.
|
||||
4. Existing non-Discord DB rows follow explicit compatibility policy (deprecated read-only and non-dispatch).
|
||||
5. Retired notifier references are absent from active dependency manifests (`go.mod`, `go.sum`, package lock files).
|
||||
6. Runtime audit confirms no active retired-notifier install/link/reference in executable/runtime paths.
|
||||
7. Active docs no longer claim multi-service notifier behavior for the current rollout.
|
||||
8. Firefox failing-spec triage clearly distinguishes in-scope notification failures from separately tracked suites.
|
||||
9. `.gitignore`, `.dockerignore`, `codecov.yml`, and `Dockerfile` are reviewed and updated only if required by actual implementation deltas.
|
||||
10. Additional provider services remain disabled pending one-by-one validated rollout PRs.
|
||||
11. Legacy fallback feature-flag behavior is force-false and non-revivable through standard API operations.
|
||||
12. Rollback procedures preserve no-fallback ambiguity constraints.
|
||||
|
||||
## 13) Handoff to Supervisor
|
||||
|
||||
This spec is ready for Supervisor review and implementation assignment.
|
||||
|
||||
Review focus:
|
||||
|
||||
- Verify defense-in-depth layering is complete (UI + API + service + runtime).
|
||||
- Confirm migration policy is explicit and reversible.
|
||||
- Confirm audit command set is sufficient to prove legacy notifier retirement.
|
||||
- Confirm PR slicing minimizes risk and isolates rollback paths.
|
||||
|
||||
Reference in New Issue
Block a user