chore(release): pending release v0.85.0#1156
Merged
Merged
Conversation
) * chore(gateway): remove stale planning taxonomy from config comments Strip internal unit/requirement identifiers (leftover planning-doc references) from gateway config comments and test titles; the comments now describe behavior directly. * feat(gateway): add operator push contract and VAPID config foundation Add the Gateway-side operator Web Push contract DTOs (VAPID public key, subscription metadata, subscribe/unsubscribe requests) and the VAPID configuration surface, disabled by default. No dispatch, routes, or web-push dependency yet. The VAPID private key is validated by fixed-reason validators and held as a non-enumerable config property so it never lands in a serialized config object or a thrown error. The operator contract version is intentionally unchanged: push adds an additive REST surface that the dashboard SSE drift gate does not observe, so the exact-match contract version stays put. * fix(gateway): harden operator-push config parsing and add contract-guard tests * fix(gateway): reject addressless VAPID subject and identical rotation keypair
Co-authored-by: fro-bot[bot] <109017866+fro-bot[bot]@users.noreply.github.com>
* feat(gateway): add durable operator push subscription store Add the Gateway-owned subscription record store: one durable record per browser endpoint, bound to one operator, with endpoint/p256dh/auth held as write-only secret fields never returned by any listing or export surface. Ownership transfer between operators sharing a device is a single atomic compare-and-swap that bumps a monotonic ownership generation, and the dispatch path re-validates ownership immediately before send so a stale read cannot deliver one operator's notification to another. Privacy delete writes a secret-free tombstone at a separate key and physically removes the subscription object; the active-read path excludes any tombstoned endpoint, so restoring a subscription object alone cannot resurrect a deleted record. A startup compare-and-swap self-test fails the push surface closed on a store without real conditional-write semantics. * fix(gateway): harden subscription store ownership, delete safety, and error classification - markDead now enforces the record owner, closing a cross-account deactivation path before the dispatch layer wires it. - deleteForOperator only counts a record deleted once the object is physically gone, rolls back its tombstone if the record was transferred mid-delete, and reports skipped records so a partial privacy delete is observable rather than silent. - Error classification checks structured object-store error fields (status/code) before falling back to message matching, so a CAS conflict or missing key can't be misread from error wording. - deactivateForOperator reports skipped records on retry exhaustion; the startup self-test key moves outside the subscription scan prefix; and single-clock capture keeps updatedAt and deactivatedAt consistent. * fix(gateway): roll back delete tombstone whenever the raced record stays active Widen the lost-physical-delete rollback so it fires whenever the re-read record is active, not only on an ownership change. A same-operator re-subscribe that won the delete race left an active record shadowed by the just-written tombstone until a later prune; the invariant is that a tombstone exists only when the endpoint has no active record for any operator. Add a same-owner race regression test, an eventual-prune reclaim test, and a transfer-clears-tombstone test, plus comments noting the direct-scan paths deliberately skip the read-side tombstone filter.
Co-authored-by: fro-bot[bot] <109017866+fro-bot[bot]@users.noreply.github.com>
- Added 'Gateway Image Smoke Test' and 'Workspace Image Smoke Test' to the list of required status checks. - Modified 'Test' job to only depend on 'setup'. - Adjusted 'Release' job dependencies to include only 'lint', 'test', and 'build'.
…d self-test (#1160) * feat(gateway): add authenticated operator push routes behind a fail-closed self-test Register GET /operator/push/vapid-key, POST /operator/push/subscriptions, POST /operator/push/subscriptions/unsubscribe, and GET /operator/push/subscriptions as privileged operator routes, mounted only when push config is present and the subscription store's startup compare-and-swap self-test passes. A failed self-test disables the push surface without crashing gateway startup. Subscribe validates the browser endpoint URL against an SSRF-conservative posture (HTTPS only; no loopback, private, or link-local hosts; no bare local names) before any value reaches the store, and never echoes the endpoint in a response or log. Mutations carry the same CSRF, origin, and rate-limit posture as the launch and cancel routes, and every denial returns the same no-oracle response. * fix(gateway): close push endpoint SSRF gaps, harden self-test gate, cap subscriptions - validate-endpoint.ts: reject IPv4-mapped IPv6 loopback/private/link-local/ unspecified addresses in both dotted and WHATWG hex-compressed form, 0.0.0.0, IPv6 ::, and trailing-dot FQDNs (metadata.google.internal.); document the parse-time-only boundary vs DNS rebinding - program.ts: replace Effect.promise with Effect.tryPromise around pushStore.selfTestCas() so a throwing adapter degrades to the same fail-closed 'push disabled' path as a Result-typed self-test failure, instead of crashing the whole gateway at startup - subscription-store.ts: wrap the self-test cleanup getObject read in try/catch so a throw during cleanup cannot turn a successful self-test into a rejected promise - add parseOperatorPushUnsubscribeRequest contract guard, export it from the operator-contract barrel, and switch subscription-route.ts to use it in place of the local parseUnsubscribeBody - subscription-route.ts: add a per-operator active-subscription cap (MAX_ACTIVE_SUBSCRIPTIONS_PER_OPERATOR = 20), exempting re-subscribes of an endpoint the operator already owns - add coverage: hardened SSRF rejection/pass cases, program.ts self-test success/failure/throw/absent branches, subscription-route store-error and unauthenticated-unsubscribe paths, and per-level (info/warn/error/ debug) endpoint-non-logging assertions - fix a pre-existing program.test.ts type error (selfTestCasMock's return type inferred as always-success, rejecting the failure-branch fixture) * fix(gateway): reject all IPv6-literal push endpoints, soften subscription cap wording * refactor(gateway): make IPv6 endpoint classifier total, drop dead guards
Co-authored-by: fro-bot[bot] <109017866+fro-bot[bot]@users.noreply.github.com>
Co-authored-by: fro-bot[bot] <109017866+fro-bot[bot]@users.noreply.github.com>
…rd (#1162) * feat(gateway): add web-push dependency and guarded push sender adapter * feat(gateway): add push payload builder, trigger policy, and dedupe cache * feat(gateway): add push dispatcher with linearizable transfer check * fix(gateway): harden push dispatch — exhaustive relay switch, per-operator dedupe, SSRF label fix * build: regenerate third-party notices for web-push dependency tree * build: sync dist bundle chunk hashes after dependency updates * fix(gateway): handle autoSelectFamily all:true in push SSRF lookup guard
…logout (#1163) * feat(gateway): add listAllActiveRecords to push subscription store * refactor(gateway): reshape push dispatcher to broadcast to all active subscriptions * feat(gateway): dispatch operator push on pending approvals, failed runs, and logout * fix(gateway): guard push dedupe on broadcast failure, surface revoke deactivation gaps * chore: remove stray review artifact and ignore root output.json
…1164) * chore: strip plan-unit taxonomy from source comments and test labels * chore: correct approval-detail comment and restore cancel.ts docblock indent
Co-authored-by: fro-bot[bot] <109017866+fro-bot[bot]@users.noreply.github.com>
… audit events (#1165) * feat(gateway): add coarse audit events for the operator push lifecycle * feat(gateway): emit push audit events on subscribe, dispatch, deactivate, and disable * docs: add operator push privacy/retention policy and surface documentation * test(gateway): harden push audit tests — assert event absence on denied paths, real key-leak check - dispatcher.ts: add default/never exhaustiveness guard to the broadcast outcome tally switch - dispatcher.test.ts: replace tautological 'auth-value' substring check with distinctive endpoint/p256dh/auth secret values that actually catch a leak into logs or audit events - subscription-route.test.ts: assert push.subscribed/push.unsubscribed audit events are absent on all denied subscribe/unsubscribe gate paths - program.test.ts: assert push.subscription.deactivated is absent on the skipped (updated=0) and failed deactivation paths - server.ts: document that the push route mount guard requires auditLogger, guaranteeing every push route action is audited * test(gateway): pin all-skipped broadcast emits a zero-count dispatch event
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.
Pending Release: v0.85.0
This PR tracks changes pending release. Released on the next auto-release cycle (Sunday/Wednesday) or via manual dispatch.
Merge this PR to trigger a release. Releases also run automatically on Sunday/Wednesday at 20:00 UTC, or via manual workflow dispatch.
Commits Since Last Release
Auto-generated by the release pipeline. Updated: 2026-07-11 00:19 UTC