Repository navigation
Web Push channel: alerts to paired phones without a third-party messenger - #177
Merged
Merged
Conversation
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nger (D-327 to D-330) Provider WebhookProviderType.Push = 50 in both channel scopes. It carries no destination; NotificationChannelDispatcher takes a NotificationScope from every caller and the push transport resolves the scope's paired devices at send time (table-backed Admin/Operator, GlobalAdmin with matching identity binding; Viewer and Global Reader never). The receiver never signs in: a signed-in Admin/Operator creates a one-shot pairing code (11 Crockford chars, hashed, 10 min, ETag one-shot, five-failure burn), the phone redeems it anonymously, the portal session confirms. The phone's only credential is the device token in X-Push-Device-Token, good for status, re-subscribe and unpair of its own row (10 calls per minute per device; the anonymous pairing routes 10 per minute per IP). Payloads are RFC 8291 per device (new BCL-only library src/Push with the RFC vector pinned byte for byte) with a VAPID key ring and the kid on every device row; the projection reads an allow-list only (never Sections, DataJson, Failure Reason, Last Failure or the ops message), serial masked, 2 KB. 404/410 mark a device Stale instead of deleting it and the receiver re-arms it on its next open (the same PUT lifts a delivery-failures pause); devices pause when their owner stops signing in (PushOwners stamp in auth/me) through one pause path and resume by themselves; an owner without a stamp is paused, never deleted outright; role loss revokes with a wipe push and deletes the row (there is no Revoked state). PushDevices and PushPairingGrants are credential-bearing: raw-table deny-list, no backup, redacted dependency rows. Receiver app under /push/ (module service worker, Declarative Web Push shape, local history, pairing and status pages, manifest + icons), portal UI (Push provider in the channel editor, pairing dialog with QR code, device lists for tenant and platform scope, "Notify me when done" on the session page — the watch row records the watcher's own scopes, so a Global Admin watching a customer session is notified on the platform-paired phone), 22 catalogued routes, ops events PushDeliveryFailed and PushEndpointRefused (deduplicated per instance), maintenance sweep inside the existing run, settings template. The tenant-scope provider stays Global-Admin-only until the customer release (same ValidateModel gate as Telegram). Backend 7371, library 125, MCP 997, web 2061 tests. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…327 to D-330, markers, VAPID generator) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
GHSA-hp3w-g68c-fv3c (sprintf-js <= 1.1.3, denial of service through unbounded precision specifiers) has no patched release, so both chains are cut instead. MCP (#117): sprintf-js arrived via @huggingface/transformers -> onnxruntime-node -> global-agent 3 -> roarr. global-agent is only loaded by onnxruntime-node's CUDA install script, which the image never runs (npm ci --ignore-scripts). The override global-agent ^4.1.3 drops roarr; 4.x keeps the bootstrap export that script calls, and onnxruntime-node 1.30 depends on the same range. Rules tooling (#114): ajv-cli 5.0.0 (no release since 2023) pulled js-yaml 3 -> argparse 1 -> sprintf-js. rules/scripts/validate.js now checks the four rule sets against their schemas with ajv (draft 2020-12) and ajv-formats directly, and combine-rules.yml runs it in one step instead of four ajv-cli loops. The lockfile shrinks from 26 to 6 packages; the fast-json-patch override and the legacy-peer-deps .npmrc (only there for ajv-cli's ts-node peer) are gone. Verified: validate.js accepts all 149 rule files plus guardrails.json exactly as ajv-cli did and rejects a corrupted copy (missing field, broken JSON, U+2028 in a guardrail value) with exit 1; clean npm ci + combine.js leave the generated outputs unchanged; MCP build and 997 tests green; zizmor clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…i: B-gzv, rule-builder fallback) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Web) - sharp 0.35.4 -> 0.35.5 (GHSA-wq5f-xc86-pv6w, librsvg): the existing sharp override in both projects now floors at ^0.35.5 so the fixed build cannot be undercut by a parent range. - proxy-addr 2.0.7 -> 2.0.8 (GHSA-jqcg-44mw-7w3h, via express; MCP only) and source-map-js 1.2.1 -> 1.2.2 (GHSA-68fv-2mgg-jv7q, via postcss) are lockfile-only bumps inside the ranges their parents declare. No other lockfile entry moved. npm audit in the MCP server is clean. Verified: MCP build and 997 tests green; Web tsc clean, 2061 tests green, next build (static export) succeeds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…requisite, EU app facts) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
16.3.8 closes GHSA-3w37-wq28-93x7, GHSA-4jqv-mc3x-m676, GHSA-39w2-rjm5-chcv, GHSA-f87g-xv8r-7p7x, GHSA-mcj8-r9mp-w47p and GHSA-cjq9-62q9-8jv4 (affected 16.0.0 - 16.3.7). Under output:"export" the cache, ISR and image-optimization paths never run on the SWA; the dev-server disclosure is local-only. The patch is taken anyway. next, eslint-config-next and @next/bundle-analyzer stay aligned and floor at ^16.3.8; the lockfile is pinned to 16.3.8, not the 16.4.0 minor the caret would resolve to. What npm audit still reports (braces, micromatch, chokidar, fast-glob, postcss-selector-parser) comes through Tailwind 3 and eslint-config-next, build-time only; braces has no fix, the rest needs Tailwind 4 (backlog). Verified: tsc clean, eslint 0 findings, 2061 tests green, next build (16.3.8, 102 static pages) succeeds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ys on 3 (D-326) main brought the Dependabot bumps #173-#176 (sharp, proxy-addr, source-map-js, and Tailwind 3 -> 4); the branch had bumped the same packages by hand. Resolution per B-dzs: both sides' overrides kept (MCP sharp ^0.35.5 + global-agent ^4.1.3; web sharp ^0.35.5 + postcss-selector-parser ^7.1.6), tailwindcss back to ^3.4.19 (the v4 bump breaks the build, D-326), next held at 16.3.8, both lockfiles regenerated with npm install --ignore-scripts instead of merged by hand. Verified: web tsc 0, eslint 0, vitest 2061, next build green; MCP 997 tests, build, audit clean; web audit shows only the Tailwind-3 / eslint-config-next chain (B-hxz). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
A Web Push notification channel: alerts reach paired phones and browsers directly from the backend, end-to-end encrypted per device (RFC 8291), without a third-party messenger. Decisions D-327 to D-330.
Push(50) in both channel scopes. The channel carries no destination;NotificationChannelDispatchernow takes aNotificationScopefrom every caller and the push transport resolves the scope's paired devices at send time (table-backed Admin/Operator in a tenant, GlobalAdmin with matching identity binding on the platform). The tenant-scope provider stays Global-Admin-only until the customer release, behind the same validation gate as Telegram.src/Push(BCL only, no packages): VAPID key ring with akidon every device row, RFC 8291aes128gcmencryption with the RFC's Appendix A vector pinned byte for byte, an SSRF-gated HTTP client and an endpoint host policy.Staleinstead of deleting it (the receiver re-arms itself on open); devices pause when their owner stops signing in and resume by themselves; role loss sends a wipe push and deletes the row.PushDevicesandPushPairingGrantsare credential-bearing: raw-table deny-list, no backup, redacted dependency telemetry./push/(service worker, Declarative Web Push shape, local history, pairing and status pages, manifest and icons) and portal UI (provider in the channel editor, pairing dialog with QR, device lists for tenant and platform scope, "Notify me when done" on a session).PushDeliveryFailedandPushEndpointRefused, maintenance sweep inside the existing run, settings template, health markers.A second review pass (35 confirmed findings) is folded in: Global-Admin session watches fan out over the watcher's own scopes, per-device throttle on the token routes, one pause path for send time and maintenance, no
Revokedstate anywhere, CAS retry loop, re-arm after transport-failure pauses, de-duplicated ops events.Verification
tsc0,eslint0ci.ymlBefore the first push (operator)
Push__Vapid__ActiveKeyis set (generator script in the infra repo)./api/pushto the Function App's client-certificate exclusion paths. Platform CORS already reflects theX-Push-Device-Tokenheader; without the exclusion the push routes answer 403 from the platform before any code runs.PushDeliverycustom event to the Action Group e-mail, so a push outage is not reported only over push.Still open
Push.Testshas only run on Windows so far; production is Linux.The branch also carries four dependency commits made on it in parallel (sprintf-js removal, sharp/proxy-addr/source-map-js bumps,
validate.jsreplacing ajv-cli).🤖 Generated with Claude Code