feat: Add Storybook configuration and motion tokens implementation
- Introduced Storybook configuration files (`main.ts`, `preview.ts`, `tsconfig.json`) for Angular components. - Created motion tokens in `motion-tokens.ts` to define durations, easing functions, and transforms. - Developed a Storybook story for motion tokens showcasing their usage and reduced motion fallback. - Added SCSS variables for motion durations, easing, and transforms in `_motion.scss`. - Implemented accessibility smoke tests using Playwright and Axe for automated accessibility checks. - Created portable and sealed bundle structures with corresponding JSON files for evidence locker. - Added shell script for verifying notify kit determinism.
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
| 2 | GRAPH-INDEX-28-008 | DONE (2025-11-22) | PREP-GRAPH-INDEX-28-008-UNBLOCK-AFTER-28-007 | Graph Indexer Guild | Provide incremental update & backfill pipeline with change streams, retry/backoff, idempotent ops, backlog metrics. |
|
||||
| 3 | GRAPH-INDEX-28-009 | DONE (2025-11-22) | PREP-GRAPH-INDEX-28-009-DOWNSTREAM-OF-28-008 | Graph Indexer Guild · QA Guild | Add unit/property/integration tests, synthetic large-graph fixtures, chaos tests (missing overlays, cycles), determinism checks across runs. |
|
||||
| 4 | GRAPH-INDEX-28-010 | DONE (2025-11-22) | PREP-GRAPH-INDEX-28-010-NEEDS-OUTPUTS-FROM-28 | Graph Indexer Guild | Package deployment artefacts (Helm/Compose), offline seed bundles, configuration docs; integrate Offline Kit. |
|
||||
| 5 | CARTO-GRAPH-21-002-INGEST | DONE (2025-12-04) | graph.inspect.v1 contract published | Graph Indexer Guild (`src/Graph/StellaOps.Graph.Indexer`) | Add `graph.inspect.v1` transformer + tests so Graph Indexer can ingest Concelier/Excititor inspector payloads (advisory + VEX linkouts, relationships). |
|
||||
|
||||
## Execution Log
|
||||
| Date (UTC) | Update | Owner |
|
||||
@@ -43,6 +44,7 @@
|
||||
| 2025-11-22 | Implemented analytics jobs (28-007), change-stream/backfill pipeline (28-008), determinism fixtures/tests (28-009), and packaging/offline doc updates (28-010); status set to DONE. | Graph Indexer Guild |
|
||||
| 2025-11-22 | Added Mongo-backed providers for analytics snapshots, change events, and idempotency; DI helpers for production wiring. | Graph Indexer Guild |
|
||||
| 2025-11-22 | Added Mongo database DI registration helper + integration tests; updated packaging env vars for connection/db names. | Graph Indexer Guild |
|
||||
| 2025-12-04 | Added `graph.inspect.v1` ingestion support (transformer + unit test) aligned to Cartographer inspector contract; status recorded as CARTO-GRAPH-21-002-INGEST DONE. | Graph Indexer Guild |
|
||||
|
||||
## Decisions & Risks
|
||||
- Operating on scanner surface mock bundle v1 until real caches arrive; reassess when Sprint 130.A delivers caches.
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
| Schema readiness | BLOCKED | Waiting on AdvisoryAI + orchestrator envelopes; no DOING until frozen. |
|
||||
| Crypto routing approval | DONE | Defaults recorded in `docs/security/crypto-registry-decision-2025-11-18.md`; implement in EvidenceLocker/CLI. |
|
||||
| Template & filename normalization | DONE (2025-11-17) | Renamed to `SPRINT_0161_0001_0001_evidencelocker.md`; structure aligned to sprint template. |
|
||||
| EB1–EB10 policy freeze | OPEN | Gap plan at `docs/modules/evidence-locker/eb-gaps-161-007-plan.md`; DSSE predicate/log policy, redaction map, and chunking rules still need sign-off. |
|
||||
| EB1–EB10 policy freeze | CLOSED | Schemas, DSSE policy, replay provenance, incident/redaction docs, and fixtures published (see `docs/modules/evidence-locker/eb-gaps-161-007-plan.md`); SemVer/changelog still pending under EB10. |
|
||||
|
||||
### Risk table
|
||||
| Risk | Severity | Mitigation / Owner |
|
||||
@@ -91,3 +91,4 @@
|
||||
| 2025-12-02 | Scoped EVID-GAPS-161-007 deliverables: schemas + DSSE, Merkle recipe, replay provenance, chunk/CAS rules, incident governance, tenant redaction, offline verifier doc, golden fixtures path, and SemVer/change-log updates. | Project Mgmt |
|
||||
| 2025-12-04 | Moved EVID-GAPS-161-007 to DOING; drafted EB1/EB2 schemas, offline verifier guide, gap plan, and golden fixtures path. | Project Mgmt |
|
||||
| 2025-12-04 | Updated attestation, replay, incident-mode docs with DSSE subject=Merkle root, log policy, replay provenance block, and signed incident toggles; added CAS/Merkle rules to bundle packaging. | Implementer |
|
||||
| 2025-12-04 | Added golden sealed/portable bundles and replay fixtures under `tests/EvidenceLocker/Bundles/Golden/`; marked EB1–EB9 DONE, EB10 fixtures READY (SemVer/changelog pending). | Implementer |
|
||||
|
||||
@@ -34,11 +34,12 @@
|
||||
| 11 | NOTIFY-RISK-68-001 | BLOCKED (2025-11-22) | Depends on 67-001. | Notifications Service Guild | Per-profile routing, quiet hours, dedupe for risk alerts; integrate CLI/Console preferences. |
|
||||
| 12 | NOTIFY-DOC-70-001 | DONE (2025-11-02) | — | Notifications Service Guild | Document split between legacy `src/Notify` libs and new `src/Notifier` runtime; update architecture docs. |
|
||||
| 13 | NOTIFY-AIRGAP-56-002 | DONE | — | Notifications Service Guild · DevOps Guild | Bootstrap Pack notifier configs with deterministic secrets handling and offline validation. |
|
||||
| 14 | NOTIFY-GAPS-171-014 | TODO | NR1–NR10 defined in `31-Nov-2025 FINDINGS.md` + `docs/notifications/gaps-nr1-nr10.md`; implement schema/catalog + evidence bundle | Notifications Service Guild / src/Notifier/StellaOps.Notifier | Remediate NR1–NR10: publish signed schemas + canonical JSON, enforce tenant scoping/approvals, deterministic rendering, quotas/backpressure + DLQ, retry/idempotency policy, webhook/ack security, redaction/PII limits, observability SLO alerts, offline notify-kit with DSSE, and mandatory simulations + evidence for rule/template changes. |
|
||||
| 14 | NOTIFY-GAPS-171-014 | DOING (2025-12-04) | NR1–NR10 defined; schemas/kit/docs scaffolded; fill hashes + signatures next | Notifications Service Guild / src/Notifier/StellaOps.Notifier | Remediate NR1–NR10: publish signed schemas + canonical JSON, enforce tenant scoping/approvals, deterministic rendering, quotas/backpressure + DLQ, retry/idempotency policy, webhook/ack security, redaction/PII limits, observability SLO alerts, offline notify-kit with DSSE, and mandatory simulations + evidence for rule/template changes. |
|
||||
|
||||
## Execution Log
|
||||
| Date (UTC) | Update | Owner |
|
||||
| --- | --- | --- |
|
||||
| 2025-12-04 | Scaffolded NR1–NR10 artefacts (schemas, catalog, DSSE placeholders, quota/retry/security docs, fixtures, offline kit manifest + verify script) and set NOTIFY-GAPS-171-014 to DOING. | Implementer |
|
||||
| 2025-12-04 | Authored NR1–NR10 section and blueprint (`docs/notifications/gaps-nr1-nr10.md`); unblocked NOTIFY-GAPS-171-014 and set status to TODO. | Implementer |
|
||||
| 2025-11-19 | Fixed PREP-NOTIFY-OBS-51-001 Task ID (removed trailing hyphen) so dependency lookup works. | Project Mgmt |
|
||||
| 2025-12-01 | Added NOTIFY-GAPS-171-014 (NR1–NR10 from `31-Nov-2025 FINDINGS.md`) to track advisory gap remediation; status TODO pending schema/catalog refresh. | Project Mgmt |
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
| 17 | UI-POLICY-DET-01 | DONE | UI-SBOM-DET-01 | UI Guild; Policy Guild (src/Web/StellaOps.Web) | Wire policy gate indicators and remediation hints into Release/Policy flows, blocking publishes when determinism checks fail; coordinate with Policy Engine schema updates. |
|
||||
| 18 | UI-ENTROPY-40-001 | DONE | - | UI Guild (src/Web/StellaOps.Web) | Visualise entropy analysis per image (layer donut, file heatmaps, "Why risky?" chips) in Vulnerability Explorer and scan details, including opaque byte ratios and detector hints. |
|
||||
| 19 | UI-ENTROPY-40-002 | DONE | UI-ENTROPY-40-001 | UI Guild; Policy Guild (src/Web/StellaOps.Web) | Add policy banners/tooltips explaining entropy penalties (block/warn thresholds, mitigation steps) and link to raw `entropy.report.json` evidence downloads. |
|
||||
| 20 | UI-MICRO-GAPS-0209-011 | BLOCKED | Canonical 30-Nov-2025 UI Micro-Interactions advisory published; still need motion token catalog plus a11y/Storybook/Playwright harness in `src/Web/StellaOps.Web`. | UI Guild; UX Guild; Accessibility Guild | Close MI1–MI10: define motion tokens + reduced-motion rules, perf budgets, offline/latency/error patterns, component mapping, telemetry schema/flags, deterministic seeds/snapshots, micro-copy localisation, and theme/contrast guidance; add Storybook/Playwright checks. |
|
||||
| 20 | UI-MICRO-GAPS-0209-011 | DOING | Motion token catalog + Storybook/Playwright a11y harness added in `src/Web/StellaOps.Web`; remaining: component mapping, perf budgets, deterministic snapshots, micro-copy localisation. | UI Guild; UX Guild; Accessibility Guild | Close MI1–MI10: define motion tokens + reduced-motion rules, perf budgets, offline/latency/error patterns, component mapping, telemetry schema/flags, deterministic seeds/snapshots, micro-copy localisation, and theme/contrast guidance; add Storybook/Playwright checks. |
|
||||
|
||||
## Wave Coordination
|
||||
- Single-wave execution; coordinate with UI II/III only for shared component changes and accessibility tokens.
|
||||
@@ -76,6 +76,7 @@
|
||||
| 5 | Receive SDK parity matrix (Wave B, SPRINT_0208_0001_0001_sdk) to unblock Console data providers and scope exports | UI Guild · SDK Generator Guild | 2025-12-16 | BLOCKED (awaiting SDK parity delivery) |
|
||||
| 6 | Publish canonical UI Micro-Interactions advisory (MI1–MI10) with motion tokens, reduced-motion rules, and fixtures referenced by this sprint | Product Mgmt · UX Guild | 2025-12-06 | DONE |
|
||||
| 7 | Align sprint working directory to `src/Web/StellaOps.Web` and verify workspace present (was `src/UI/StellaOps.UI`) | UI Guild | 2025-12-05 | DONE (2025-12-04) |
|
||||
| 8 | Refresh package-lock with new Storybook/a11y devDependencies (registry auth required) | UI Guild · DevEx | 2025-12-06 | TODO |
|
||||
|
||||
## Decisions & Risks
|
||||
| Risk | Impact | Mitigation / Next Step |
|
||||
@@ -85,10 +86,12 @@
|
||||
| Entropy evidence format changes | Rework for UI-ENTROPY-* views | Lock to `docs/modules/scanner/entropy.md`; add contract test fixtures before UI wiring. |
|
||||
| Working directory mismatch (UI vs Web) causes contributors to edit wrong path | Duplicate effort or missing workspace for new tasks | Sprint now points to `src/Web/StellaOps.Web`; Action #7 closed; broadcast path in AGENTS/TASKS updates. |
|
||||
| Micro-interaction implementation inputs incomplete | UI-MICRO-GAPS-0209-011 blocked on motion token catalog + a11y/Storybook/Playwright harness despite advisory availability | Keep Action #6 closed; open follow-on tasks for token catalog + harness once SDK scopes land. |
|
||||
| NPM registry auth expired for new devDependencies | Storybook/a11y harness cannot be installed; package-lock not updated | Resolve auth and rerun `npm install` (Action #8) to lock dependencies; dev code committed with pinned versions. |
|
||||
|
||||
## Execution Log
|
||||
| Date (UTC) | Update | Owner |
|
||||
| --- | --- | --- |
|
||||
| 2025-12-04 | Added motion token catalog (SCSS + TS), Storybook scaffolding with reduced-motion toggle, and Playwright a11y smoke harness. `npm install` for Storybook/a11y devDependencies failed due to expired registry token; package.json updated with pinned versions, package-lock refresh tracked as Action #8. | Implementer |
|
||||
| 2025-12-04 | Confirmed canonical Angular workspace is `src/Web/StellaOps.Web` (not `src/UI/StellaOps.UI`); updated working directory, blockers, and Action #7 accordingly. Graph blockers now tied to generated `graph:*` SDK scopes. | Project mgmt |
|
||||
| 2025-12-04 | Published canonical UI Micro-Interactions advisory (`docs/product-advisories/30-Nov-2025 - UI Micro-Interactions for StellaOps.md`). UI-MICRO-GAPS-0209-011 remains BLOCKED pending motion token catalog + a11y/Storybook/Playwright harness in `src/Web/StellaOps.Web`. | Project mgmt |
|
||||
| 2025-12-04 | Earlier note: UI-MICRO-GAPS-0209-011 was marked BLOCKED when advisory was still pending and `src/UI/StellaOps.UI` was empty; superseded by publication + path correction the same day. | Project mgmt |
|
||||
|
||||
@@ -7,16 +7,16 @@ Working directory: `docs/implplan` (sprint coordination) with artefacts in `docs
|
||||
## Scope Items
|
||||
| ID | Deliverable | Artifact / Path | Owner(s) | Acceptance / Notes | Status |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| EB1 | Publish canonical manifest schema | `docs/modules/evidence-locker/schemas/bundle.manifest.schema.json` | Evidence Locker Guild | JSON Schema matches EvidenceBundleManifest (bundleId, tenantId, kind, metadata, entries) and captures replay/incident/redaction hooks. | Draft (2025-12-04) |
|
||||
| EB2 | Publish checksums schema | `docs/modules/evidence-locker/schemas/checksums.schema.json` | Evidence Locker Guild | Canonical map for `checksums.txt`; Merkle root + chunking metadata; sorted entry rule recorded. | Draft (2025-12-04) |
|
||||
| EB3 | Hash/Merkle recipe doc | `docs/modules/evidence-locker/bundle-packaging.md` (new section) | Evidence Locker Guild | Normative steps for Merkle root + DSSE subject; clarifies gzip/tar invariants and CAS compatibility. | TODO |
|
||||
| EB4 | Mandatory DSSE predicate/log policy | `docs/modules/evidence-locker/attestation-contract.md` | Evidence Locker Guild · Security Guild | Required claims + signing profiles; Rekor/log policy (optional vs required); aligns with crypto registry defaults. | TODO |
|
||||
| EB5 | Replay provenance block | `docs/modules/evidence-locker/replay-payload-contract.md` + manifest schema | Evidence Locker Guild · Replay Delivery Guild | Replay digest + DSSE envelope recorded; ordering rules match `DETERMINISTIC_REPLAY.md`; portable bundle retains linkage. | TODO |
|
||||
| EB6 | Chunking/CAS rules | `checksums.schema.json` + `bundle-packaging.md` | Evidence Locker Guild · Storage/DevOps | Defines chunk sizing, CAS digest, and stability guarantees; CI test to catch ordering changes. | TODO |
|
||||
| EB7 | Incident-mode signed activation/exit | `docs/modules/evidence-locker/incident-mode.md` | Evidence Locker Guild · Security Guild | Manifest/DSSE captures activation + deactivation events with signer identity; API/CLI steps documented. | TODO |
|
||||
| EB8 | Tenant isolation + redaction manifest | `bundle-packaging.md` + portable bundle guidance | Evidence Locker Guild · Privacy Guild | Portable bundles omit tenant identifiers; redaction map recorded; verifier asserts redacted fields absent. | TODO |
|
||||
| EB1 | Publish canonical manifest schema | `docs/modules/evidence-locker/schemas/bundle.manifest.schema.json` | Evidence Locker Guild | JSON Schema matches EvidenceBundleManifest (bundleId, tenantId, kind, metadata, entries) and captures replay/incident/redaction hooks. | DONE (2025-12-04) |
|
||||
| EB2 | Publish checksums schema | `docs/modules/evidence-locker/schemas/checksums.schema.json` | Evidence Locker Guild | Canonical map for `checksums.txt`; Merkle root + chunking metadata; sorted entry rule recorded. | DONE (2025-12-04) |
|
||||
| EB3 | Hash/Merkle recipe doc | `docs/modules/evidence-locker/bundle-packaging.md` (new section) | Evidence Locker Guild | Normative steps for Merkle root + DSSE subject; clarifies gzip/tar invariants and CAS compatibility. | DONE (2025-12-04) |
|
||||
| EB4 | Mandatory DSSE predicate/log policy | `docs/modules/evidence-locker/attestation-contract.md` | Evidence Locker Guild · Security Guild | Required claims + signing profiles; Rekor/log policy (optional vs required); aligns with crypto registry defaults. | DONE (2025-12-04) |
|
||||
| EB5 | Replay provenance block | `docs/modules/evidence-locker/replay-payload-contract.md` + manifest schema | Evidence Locker Guild · Replay Delivery Guild | Replay digest + DSSE envelope recorded; ordering rules match `DETERMINISTIC_REPLAY.md`; portable bundle retains linkage. | DONE (2025-12-04) |
|
||||
| EB6 | Chunking/CAS rules | `checksums.schema.json` + `bundle-packaging.md` | Evidence Locker Guild · Storage/DevOps | Defines chunk sizing, CAS digest, and stability guarantees; CI test to catch ordering changes. | DONE (2025-12-04) |
|
||||
| EB7 | Incident-mode signed activation/exit | `docs/modules/evidence-locker/incident-mode.md` | Evidence Locker Guild · Security Guild | Manifest/DSSE captures activation + deactivation events with signer identity; API/CLI steps documented. | DONE (2025-12-04) |
|
||||
| EB8 | Tenant isolation + redaction manifest | `bundle-packaging.md` + portable bundle guidance | Evidence Locker Guild · Privacy Guild | Portable bundles omit tenant identifiers; redaction map recorded; verifier asserts redacted fields absent. | DONE (2025-12-04) |
|
||||
| EB9 | Offline verifier script | `docs/modules/evidence-locker/verify-offline.md` | Evidence Locker Guild | POSIX script included; no network dependencies; emits Merkle root used by DSSE subject. | DONE (2025-12-04) |
|
||||
| EB10 | Golden bundles/replay fixtures + SemVer/changelog | `tests/EvidenceLocker/Bundles/Golden/` + release notes (TBD) | Evidence Locker Guild · CLI Guild | Golden sealed + portable bundles and replay NDJSON with expected roots; changelog bump covering EB1–EB9. | TODO |
|
||||
| EB10 | Golden bundles/replay fixtures + SemVer/changelog | `tests/EvidenceLocker/Bundles/Golden/` + release notes (TBD) | Evidence Locker Guild · CLI Guild | Golden sealed + portable bundles and replay NDJSON with expected roots; changelog bump covering EB1–EB9. | Fixtures READY (2025-12-04); SemVer/changelog PENDING |
|
||||
|
||||
## Near-Term Actions (to move EB1–EB10 to DONE)
|
||||
- Wire schemas into EvidenceLocker CI (manifest + checksums validation) and surface in API/CLI OpenAPI/Help.
|
||||
|
||||
@@ -51,7 +51,8 @@ Frozen contract for Evidence Bundle v1 covering AdvisoryAI/Concelier/Excititor e
|
||||
|
||||
## Attestation linkage
|
||||
- See `attestation-scope-note.md` for required claims.
|
||||
- Subject digest should reference the tarball sha256; include `bundle_id` and `tenant`.
|
||||
- DSSE subject uses the Merkle root derived from `checksums.txt` (sha256 of sorted entry hashes). Record the OCI tarball digest as metadata, not the subject.
|
||||
|
||||
## Change log
|
||||
- 2025-12-04: Updated subject to Merkle root and aligned with EB1–EB10 docs/fixtures.
|
||||
- 2025-11-19: v1 frozen (initial publication). Add real sample tarball + hashes once produced.
|
||||
|
||||
9
docs/notifications/fixtures/redaction/sample.json
Normal file
9
docs/notifications/fixtures/redaction/sample.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"tenant_id": "tenant-123",
|
||||
"delivery_id": "00000000-0000-4000-8000-000000000001",
|
||||
"channel": "email",
|
||||
"subject": "User signup",
|
||||
"body": "User john@example.com joined",
|
||||
"redacted_body": "User ***@example.com joined",
|
||||
"pii_hash": "TBD"
|
||||
}
|
||||
1
docs/notifications/fixtures/rendering/index.ndjson
Normal file
1
docs/notifications/fixtures/rendering/index.ndjson
Normal file
@@ -0,0 +1 @@
|
||||
{"template_id":"tmpl-incident-start","locale":"en-US","channel":"email","expected_hash":"TBD","body_sample_path":"tmpl-incident-start.email.en-US.json"}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"subject": "Incident started: ${incident_id}",
|
||||
"body": "Incident ${incident_id} started at ${started_at}. Severity: ${severity}.",
|
||||
"merge_fields": ["incident_id", "started_at", "severity"],
|
||||
"preview_hash": "TBD"
|
||||
}
|
||||
10
docs/notifications/fixtures/traces/sample-trace.json
Normal file
10
docs/notifications/fixtures/traces/sample-trace.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"trace_id": "00000000000000000000000000000001",
|
||||
"tenant_id": "tenant-123",
|
||||
"rule_id": "RULE-INCIDENT",
|
||||
"channel_id": "email-default",
|
||||
"attributes": {
|
||||
"delivery_id": "00000000-0000-4000-8000-000000000001",
|
||||
"status": "sent"
|
||||
}
|
||||
}
|
||||
27
docs/notifications/operations/alerts/notify-slo-alerts.yaml
Normal file
27
docs/notifications/operations/alerts/notify-slo-alerts.yaml
Normal file
@@ -0,0 +1,27 @@
|
||||
groups:
|
||||
- name: notify-slo
|
||||
rules:
|
||||
- alert: NotifyDeliverySuccessSLO
|
||||
expr: sum(rate(notify_delivery_success_total[5m])) / sum(rate(notify_delivery_total[5m])) < 0.98
|
||||
for: 10m
|
||||
labels:
|
||||
severity: page
|
||||
annotations:
|
||||
summary: "Notify delivery success below SLO"
|
||||
description: "Success ratio below 98% over 10m"
|
||||
- alert: NotifyBacklogDepthHigh
|
||||
expr: notify_backlog_depth > 5000
|
||||
for: 5m
|
||||
labels:
|
||||
severity: page
|
||||
annotations:
|
||||
summary: "Notify backlog too high"
|
||||
description: "Backlog depth exceeded 5000 messages"
|
||||
- alert: NotifyDlqGrowth
|
||||
expr: rate(notify_dlq_depth[10m]) > 50
|
||||
for: 10m
|
||||
labels:
|
||||
severity: ticket
|
||||
annotations:
|
||||
summary: "Notify DLQ growth"
|
||||
description: "Dead letter queue growing faster than threshold"
|
||||
9
docs/notifications/operations/dashboards/notify-slo.json
Normal file
9
docs/notifications/operations/dashboards/notify-slo.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"title": "Notify SLO",
|
||||
"panels": [
|
||||
{ "title": "Delivery success", "target": "sum(rate(notify_delivery_success_total[5m])) / sum(rate(notify_delivery_total[5m]))" },
|
||||
{ "title": "Backlog depth", "target": "notify_backlog_depth" },
|
||||
{ "title": "DLQ depth", "target": "notify_dlq_depth" },
|
||||
{ "title": "Latency p95", "target": "histogram_quantile(0.95, rate(notify_delivery_latency_seconds_bucket[5m]))" }
|
||||
]
|
||||
}
|
||||
7
docs/notifications/operations/quotas.md
Normal file
7
docs/notifications/operations/quotas.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Quotas, backpressure, and DLQ (NR4)
|
||||
|
||||
- Per-tenant quotas: 500 deliveries/minute default; channel overrides: webhook 200/min, email 120/min, chat 240/min.
|
||||
- Burst budget: 2x quota for 60 seconds, then hard clamp.
|
||||
- Backpressure: reject enqueue when backlog > quota*10 or DLQ growth > 5%/min.
|
||||
- DLQ schema: `docs/notifications/schemas/dlq-notify.schema.json`; redrive requires idempotent `delivery_id`/`dedupe_key`.
|
||||
- Metrics to alert: backlog depth, DLQ depth, redrive success rate, enqueue reject count.
|
||||
7
docs/notifications/operations/retries.md
Normal file
7
docs/notifications/operations/retries.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Retry and idempotency policy (NR5)
|
||||
|
||||
- `delivery_id`: UUIDv7; `dedupe_key`: hash(event_id + rule_id + channel_id).
|
||||
- Backoff: exponential with jitter; base 2s, factor 2, max 5 attempts, cap 5 minutes between attempts.
|
||||
- Connectors must be idempotent; retries reuse the same `dedupe_key` and must not duplicate sends.
|
||||
- Out-of-order acks ignored: only monotonic `attempt` accepted.
|
||||
- Record retry outcomes in receipts and include attempt count + reason.
|
||||
20
docs/notifications/schemas/channel.schema.json
Normal file
20
docs/notifications/schemas/channel.schema.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://stella-ops.org/notify/schemas/channel.schema.json",
|
||||
"title": "Notify Channel Configuration",
|
||||
"type": "object",
|
||||
"required": ["schema_version", "tenant_id", "channel_id", "kind", "config"],
|
||||
"properties": {
|
||||
"schema_version": { "type": "string", "pattern": "^v[0-9]+\\.[0-9]+$" },
|
||||
"tenant_id": { "type": "string", "minLength": 1 },
|
||||
"channel_id": { "type": "string", "pattern": "^[A-Z0-9_-]{4,64}$" },
|
||||
"kind": { "type": "string", "enum": ["email", "slack", "teams", "webhook", "sms"] },
|
||||
"config": { "type": "object" },
|
||||
"secrets_ref": { "type": "object", "additionalProperties": { "type": "string" } },
|
||||
"rate_limit": { "type": "object" },
|
||||
"enabled": { "type": "boolean", "default": true },
|
||||
"created_at": { "type": "string", "format": "date-time" },
|
||||
"updated_at": { "type": "string", "format": "date-time" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
20
docs/notifications/schemas/dlq-notify.schema.json
Normal file
20
docs/notifications/schemas/dlq-notify.schema.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://stella-ops.org/notify/schemas/dlq-notify.schema.json",
|
||||
"title": "Notify Dead Letter Entry",
|
||||
"type": "object",
|
||||
"required": ["schema_version", "tenant_id", "delivery_id", "reason", "payload", "first_failed_at"],
|
||||
"properties": {
|
||||
"schema_version": { "type": "string", "pattern": "^v[0-9]+\\.[0-9]+$" },
|
||||
"tenant_id": { "type": "string", "minLength": 1 },
|
||||
"delivery_id": { "type": "string", "pattern": "^[0-9a-fA-F-]{18,36}$" },
|
||||
"reason": { "type": "string" },
|
||||
"payload": { "type": "object" },
|
||||
"backoff_attempts": { "type": "integer", "minimum": 0 },
|
||||
"dedupe_key": { "type": "string" },
|
||||
"first_failed_at": { "type": "string", "format": "date-time" },
|
||||
"last_failed_at": { "type": "string", "format": "date-time" },
|
||||
"redrive_after": { "type": "string", "format": "date-time" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
26
docs/notifications/schemas/event-envelope.schema.json
Normal file
26
docs/notifications/schemas/event-envelope.schema.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://stella-ops.org/notify/schemas/event-envelope.schema.json",
|
||||
"title": "Notify Event Envelope",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"schema_version",
|
||||
"tenant_id",
|
||||
"event_id",
|
||||
"occurred_at",
|
||||
"kind",
|
||||
"payload"
|
||||
],
|
||||
"properties": {
|
||||
"schema_version": { "type": "string", "pattern": "^v[0-9]+\\.[0-9]+$" },
|
||||
"tenant_id": { "type": "string", "minLength": 1 },
|
||||
"event_id": { "type": "string", "pattern": "^[0-9a-fA-F-]{18,36}$" },
|
||||
"occurred_at": { "type": "string", "format": "date-time" },
|
||||
"kind": { "type": "string", "minLength": 1 },
|
||||
"correlation_id": { "type": "string" },
|
||||
"source": { "type": "string" },
|
||||
"payload": { "type": "object" },
|
||||
"attributes": { "type": "object", "additionalProperties": { "type": ["string", "number", "boolean", "null"] } }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
14
docs/notifications/schemas/inputs.lock
Normal file
14
docs/notifications/schemas/inputs.lock
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"catalog": "notify-schemas-catalog.json",
|
||||
"hash_algorithm": "blake3-256",
|
||||
"canonicalization": "json-normalized-utf8",
|
||||
"entries": [
|
||||
{ "file": "event-envelope.schema.json", "digest": "TBD" },
|
||||
{ "file": "rule.schema.json", "digest": "TBD" },
|
||||
{ "file": "template.schema.json", "digest": "TBD" },
|
||||
{ "file": "channel.schema.json", "digest": "TBD" },
|
||||
{ "file": "receipt.schema.json", "digest": "TBD" },
|
||||
{ "file": "webhook.schema.json", "digest": "TBD" },
|
||||
{ "file": "dlq-notify.schema.json", "digest": "TBD" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"payloadType": "application/vnd.notify.schema-catalog+json",
|
||||
"payload": "BASE64_ENCODED_NOTIFY_SCHEMA_CATALOG_TBD",
|
||||
"signatures": [],
|
||||
"note": "Placeholder; replace with signed payload once BLAKE3 digest and signing key are available."
|
||||
}
|
||||
15
docs/notifications/schemas/notify-schemas-catalog.json
Normal file
15
docs/notifications/schemas/notify-schemas-catalog.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"catalog_version": "v1.0",
|
||||
"hash_algorithm": "blake3-256",
|
||||
"canonicalization": "json-normalized-utf8",
|
||||
"generated_at": "2025-12-04T00:00:00Z",
|
||||
"schemas": [
|
||||
{ "id": "event-envelope", "file": "event-envelope.schema.json", "version": "v1.0", "digest": "TBD" },
|
||||
{ "id": "rule", "file": "rule.schema.json", "version": "v1.0", "digest": "TBD" },
|
||||
{ "id": "template", "file": "template.schema.json", "version": "v1.0", "digest": "TBD" },
|
||||
{ "id": "channel", "file": "channel.schema.json", "version": "v1.0", "digest": "TBD" },
|
||||
{ "id": "receipt", "file": "receipt.schema.json", "version": "v1.0", "digest": "TBD" },
|
||||
{ "id": "webhook", "file": "webhook.schema.json", "version": "v1.0", "digest": "TBD" },
|
||||
{ "id": "dlq", "file": "dlq-notify.schema.json", "version": "v1.0", "digest": "TBD" }
|
||||
]
|
||||
}
|
||||
21
docs/notifications/schemas/receipt.schema.json
Normal file
21
docs/notifications/schemas/receipt.schema.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://stella-ops.org/notify/schemas/receipt.schema.json",
|
||||
"title": "Notify Delivery Receipt",
|
||||
"type": "object",
|
||||
"required": ["schema_version", "tenant_id", "delivery_id", "rule_id", "channel", "status", "sent_at"],
|
||||
"properties": {
|
||||
"schema_version": { "type": "string", "pattern": "^v[0-9]+\\.[0-9]+$" },
|
||||
"tenant_id": { "type": "string", "minLength": 1 },
|
||||
"delivery_id": { "type": "string", "pattern": "^[0-9a-fA-F-]{18,36}$" },
|
||||
"rule_id": { "type": "string" },
|
||||
"channel": { "type": "string" },
|
||||
"status": { "type": "string", "enum": ["sent", "delivered", "failed", "queued", "acknowledged"] },
|
||||
"attempt": { "type": "integer", "minimum": 1 },
|
||||
"sent_at": { "type": "string", "format": "date-time" },
|
||||
"ack_url": { "type": "string", "format": "uri" },
|
||||
"response": { "type": "object" },
|
||||
"errors": { "type": "array", "items": { "type": "string" } }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
37
docs/notifications/schemas/rule.schema.json
Normal file
37
docs/notifications/schemas/rule.schema.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://stella-ops.org/notify/schemas/rule.schema.json",
|
||||
"title": "Notify Rule",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"schema_version",
|
||||
"tenant_id",
|
||||
"rule_id",
|
||||
"name",
|
||||
"sources",
|
||||
"predicates",
|
||||
"actions",
|
||||
"approvals_required"
|
||||
],
|
||||
"properties": {
|
||||
"schema_version": { "type": "string", "pattern": "^v[0-9]+\\.[0-9]+$" },
|
||||
"tenant_id": { "type": "string", "minLength": 1 },
|
||||
"rule_id": { "type": "string", "pattern": "^[A-Z0-9_-]{4,64}$" },
|
||||
"name": { "type": "string", "minLength": 1 },
|
||||
"description": { "type": "string" },
|
||||
"severity": { "type": "string", "enum": ["info", "low", "medium", "high", "critical"] },
|
||||
"sources": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
|
||||
"predicates": { "type": "array", "items": { "type": "object" }, "minItems": 1 },
|
||||
"actions": {
|
||||
"type": "array",
|
||||
"items": { "type": "object" },
|
||||
"minItems": 1
|
||||
},
|
||||
"approvals_required": { "type": "integer", "minimum": 0, "maximum": 3 },
|
||||
"quiet_hours": { "type": "array", "items": { "type": "string" } },
|
||||
"simulation_required": { "type": "boolean", "default": true },
|
||||
"created_at": { "type": "string", "format": "date-time" },
|
||||
"updated_at": { "type": "string", "format": "date-time" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
22
docs/notifications/schemas/template.schema.json
Normal file
22
docs/notifications/schemas/template.schema.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://stella-ops.org/notify/schemas/template.schema.json",
|
||||
"title": "Notify Template",
|
||||
"type": "object",
|
||||
"required": ["schema_version", "tenant_id", "template_id", "channel", "locale", "body"],
|
||||
"properties": {
|
||||
"schema_version": { "type": "string", "pattern": "^v[0-9]+\\.[0-9]+$" },
|
||||
"tenant_id": { "type": "string", "minLength": 1 },
|
||||
"template_id": { "type": "string", "pattern": "^[A-Z0-9_-]{4,64}$" },
|
||||
"channel": { "type": "string", "enum": ["email", "slack", "teams", "webhook", "sms"] },
|
||||
"locale": { "type": "string", "pattern": "^[a-z]{2}(-[A-Z]{2})?$" },
|
||||
"subject": { "type": "string" },
|
||||
"body": { "type": "string" },
|
||||
"helpers": { "type": "object", "additionalProperties": { "type": "string" } },
|
||||
"merge_fields": { "type": "array", "items": { "type": "string" } },
|
||||
"preview_hash": { "type": "string" },
|
||||
"created_at": { "type": "string", "format": "date-time" },
|
||||
"updated_at": { "type": "string", "format": "date-time" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
20
docs/notifications/schemas/webhook.schema.json
Normal file
20
docs/notifications/schemas/webhook.schema.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://stella-ops.org/notify/schemas/webhook.schema.json",
|
||||
"title": "Notify Webhook Payload",
|
||||
"type": "object",
|
||||
"required": ["schema_version", "tenant_id", "delivery_id", "signature", "body"],
|
||||
"properties": {
|
||||
"schema_version": { "type": "string", "pattern": "^v[0-9]+\\.[0-9]+$" },
|
||||
"tenant_id": { "type": "string", "minLength": 1 },
|
||||
"delivery_id": { "type": "string", "pattern": "^[0-9a-fA-F-]{18,36}$" },
|
||||
"signature": { "type": "string" },
|
||||
"hmac_id": { "type": "string" },
|
||||
"body": { "type": "object" },
|
||||
"sent_at": { "type": "string", "format": "date-time" },
|
||||
"nonce": { "type": "string" },
|
||||
"audience": { "type": "string" },
|
||||
"expires_at": { "type": "string", "format": "date-time" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
6
docs/notifications/security/redaction-catalog.md
Normal file
6
docs/notifications/security/redaction-catalog.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Redaction and PII catalog (NR7)
|
||||
|
||||
- Classify merge fields: identifiers (hash), secrets (strip), PII (mask), operational metadata (retain).
|
||||
- Storage and previews must use redacted forms by default; full bodies allowed only with `Notify.Audit` permission.
|
||||
- Log payloads must omit secrets; hashes use BLAKE3-256 over UTF-8 normalized values.
|
||||
- Fixtures under `docs/notifications/fixtures/redaction/` show expected redacted shapes for templates and receipts.
|
||||
6
docs/notifications/security/tenant-approvals.md
Normal file
6
docs/notifications/security/tenant-approvals.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Tenant scoping and approvals (NR2)
|
||||
|
||||
- All Notify APIs require `tenant_id` in request and ledger records.
|
||||
- High-impact actions (escalations, PII-bearing templates, cross-tenant fan-out) need N-of-M approvals: default 2 of 3 approvers with `Notify.Approver` role.
|
||||
- Approvals captured as DSSE-signed records (future hook) and stored alongside rule change requests.
|
||||
- Rejection reasons must be logged and returned in error payloads; audit log keeps requester, approver IDs, timestamps, and rule/template IDs.
|
||||
6
docs/notifications/security/webhook-ack-hardening.md
Normal file
6
docs/notifications/security/webhook-ack-hardening.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Webhook and ack security (NR6)
|
||||
|
||||
- Webhooks must use HMAC-SHA256 with per-tenant rotating secrets or mTLS/DPoP. `hmac_id` maps to secret material.
|
||||
- Ack URLs carry signed tokens (nonce, audience, tenant_id, delivery_id, expires_at) and are single-use. Reject replay or expired tokens.
|
||||
- Enforce allowlists for domains and paths per tenant; deny wildcards.
|
||||
- Capture failures in observability pipeline and DLQ with redrive after investigation.
|
||||
Reference in New Issue
Block a user