Skip to content

feat(extension): opted-in installs send observation v1, with disclosures - #1610

Merged
JOY (JOY) merged 2 commits into
devfrom
feat/1606-extension-v1-switch
Nov 4, 2026
Merged

JOY (JOY) merged 2 commits into
devfrom
feat/1606-extension-v1-switch

Conversation

@JOY

@JOY JOY (JOY) commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Refs #1606
Refs #1175

This is step 4 of JOY's "Duyệt M2" (2026-11-04): opted-in installs switch to ExtensionObservation v1, and every disclosure is updated. The web flag RATE_EXTENSION_OBSERVATIONS stays off, so the observation routes answer 404 and nothing is stored until JOY turns them on. Nothing is published to a store.

What changes

  • The opt-in sends v1. It now goes to POST /api/v1/rate/extension/observation, instead of the legacy crowd ingest.

    • The payload is the store, the cleaned URL, the item and shop ids, the SKU, the variant, the price, the list price, the stock status and quantity, the time seen and the extension version.
    • There is no title and no ratings.
    • Each send carries this install's random contributor id, from POST /api/v1/rate/extension/submitter.
  • observation-client.ts runs register, send and erase on one chain:

    • Two product pages register one id.
    • An opt-out waits for a send in flight. An id issued after the opt-out is erased at once, and its observation is never sent.
    • Erasures stay queued until the server confirms (200). They are retried at start-up and on the next send.
    • The stored id remembers which server issued it, and an id from another server is erased there.
    • A 429 holds for its Retry-After. Routes that are off (404 NOT_ENABLED) or a 503 hold for an hour, so opted-in installs stop asking while the flag is off.
  • Turning the opt-in off erases. DELETE /api/v1/rate/extension/submitter deletes the id and every observation sent with it.

  • Consent version 2. An opt-in given to the older crowd signal is not consent to v1. It reads as off, and an update reopens the consent page.

  • Disclosures, tied to the v1 keys by a test:

    • the opt-in text and the consent page (vi, en, cs);
    • the privacy policy (vi, en, cs);
    • CHROMEWEBSTORE.md;
    • the RELEASE.md changelog and a test step for erasure.

    They name every field, the contributor id, the network-range hash and its retention (28 days for the hash, 180 for observations), erasure on opt-out, and the uninstall case. The IP sentence now says the hash is derived from the IP and the IP itself is never stored.

Tests

pnpm test, tsc, eslint and pnpm build pass in apps/extension.

New behavioural tests drive the client with stubbed storage and fetch:

  • one register, then a send;
  • id reuse;
  • concurrent pages registering one id;
  • the routes-off hold;
  • UNKNOWN_SUBMITTER;
  • a 429 hold;
  • an erasure kept and retried;
  • an opt-out during registration;
  • opted out, nothing sent;
  • an id from another server;
  • a body that is not JSON.

Review

Two fresh-context Claude reviews were run on 1625f16: privacy and consent, and the worker logic against the server contract. Neither blocks while the flag is off. Fixed in 254f608:

  • the lost or late erasure;
  • the opt-out race;
  • concurrent ids;
  • no backoff;
  • an id not tied to its server;
  • the consent version;
  • the IP sentence;
  • the stock quantity wording;
  • the server-derived hash wording;
  • the uninstall case;
  • one name for the id;
  • the release step.

Left as is: buildCrowdIngestPayload stays exported for the web ingest route's tests. The extension no longer calls it, and esbuild drops it.

Codex not run.

🤖 Generated with Claude Code


📌 TL;DR

This PR replaces the legacy "crowd signal" opt-in with a new ExtensionObservation v1 system that sends minimal, pseudonymous product data (price, stock, SKU) instead of ratings. It introduces a robust ObservationClient to manage a random submitter ID, handles strict data erasure (GDPR-style) when users opt out, and updates all privacy disclosures and localization files to reflect the new data retention and hashing policies.

🎯 Type of Change

  • 🚀 New feature
  • 🐛 Bugfix
  • 🧹 Refactor
  • ⚡ Performance
  • 📚 Documentation
  • ⚙️ CI / Configuration

🔍 Changes Walkthrough

File Summary of Changes
apps/extension/CHROMEWEBSTORE.md Updated privacy documentation to describe the new "Opt-in observation" flow, including the submitter ID, network-range hashing, and data retention periods (28 days for hash, 180 days for observations).
apps/extension/RELEASE.md Updated release notes and QA checklist to verify the new contribution erasure flow and updated the user-facing description of what data is sent.
apps/extension/src/_locales/{cs,en,vi}/messages.json Updated optInBody and onboardingOptInBody strings to list the new fields (SKU, stock, timestamp, submitter ID) and explicitly state that ratings are no longer sent. Added new keys for observation field labels.
apps/extension/src/background/observation-client.ts New File. Implements the ObservationClient class. Handles:
1. Registering a random submitter ID.
2. Sending observations with the ID.
3. Managing a "pending erasure" queue to ensure data is deleted from the server when the user opts out (retrying on failure).
4. Handling rate limits (429) and service unavailability (503/404).
apps/extension/src/background/service-worker.ts Integrated ObservationClient.
1. Replaced buildCrowdIngestPayload with buildExtensionObservationV1.
2. Added consent versioning (OPT_IN_CONSENT_VERSION = 2) to force re-consent for users who opted in to the old system.
3. Triggered observationClient.forget() when the user toggles the opt-in off.
4. Added onStartup listener to retry pending erasures.
apps/extension/src/security-guards.ts Updated buildExtensionObservationV1 documentation. Added OBSERVATION_FIELD_LABELS and OBSERVATION_EXTRA_LABELS constants to map observation fields to i18n keys for testing and disclosure verification.
apps/extension/src/test-extension-pipeline.ts Updated tests to verify:
1. The new observation payload structure.
2. That all i18n locales disclose the new fields.
3. That the ObservationClient correctly registers, sends, and erases data (including retry logic and concurrent request handling).
apps/extension/src/types.ts Added type definitions for chrome.runtime.onStartup and chrome.storage.local.remove to support the new client logic.
apps/web/locales/{cs,en,vi}.json Updated the website's privacy policy section (privacyS5Desc) to accurately describe the new extension data collection, hashing, and erasure mechanisms.

📊 Architectural Flow

sequenceDiagram
    participant User
    participant Extension as Extension (Service Worker)
    participant Client as ObservationClient
    participant Storage as Chrome Storage
    participant Server as Rate.vn API

    Note over User, Extension: User views product page (Opt-in ON)
    
    User->>Extension: View Product
    Extension->>Client: send(apiBaseUrl, observation)
    
    Client->>Storage: Check pending erasures
    alt Pending Erasures Exist
        Client->>Server: DELETE /submitter (retry)
        Server-->>Client: 200 OK
        Client->>Storage: Clear pending erasures
    end

    Client->>Storage: Get Submitter ID
    alt No ID Found
        Client->>Server: POST /submitter (Register)
        Server-->>Client: 200 OK (submitterId)
        Client->>Storage: Save Submitter ID
    end

    Client->>Server: POST /observation (with ID)
    Server-->>Client: 200 OK (Quarantined)

    Note over User, Extension: User turns Opt-in OFF
    
    User->>Extension: Toggle Opt-in OFF
    Extension->>Client: forget()
    Client->>Storage: Get Submitter ID
    Client->>Storage: Remove Submitter ID
    Client->>Storage: Queue Erasure (ID)
    Client->>Server: DELETE /submitter
    Server-->>Client: 200 OK
    Client->>Storage: Clear Pending Erasures
Loading

Summary by CodeRabbit

  • New Features
    • Optional contributions now share listing details such as SKU, price, stock status and quantity, along with observation time and extension version.
    • Contributions use a random installation identifier. Turning contributions off removes the identifier and requests deletion of associated submissions.
  • Documentation
    • Privacy information now explains what contribution data is sent, how long data is retained, and how it is deleted. Network-range hashes are retained for 28 days and observations for 180 days.
  • Changes
    • Existing contributors must review and renew their consent to continue contributing.

JOY (JOY) and others added 2 commits November 4, 2026 20:30
The opt-in now sends ExtensionObservation v1 to the observation route
with a random submitter id from the submitter route, instead of the
legacy crowd ingest (no title, no ratings). Turning the opt-in off
deletes the id and every observation sent with it. The web routes stay
off (RATE_EXTENSION_OBSERVATIONS), so nothing is stored until JOY turns
them on.

The opt-in text, the consent page, the privacy policy (vi, en, cs),
CHROMEWEBSTORE.md and the changelog name the v1 fields, the submitter id
and the salted network-range hash with its retention; a test ties the
texts to the v1 keys.

Refs #1606

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nt v2

- observation-client.ts runs register, send and erase on one chain: two
  product pages register one id, and an opt-out waits for a send in
  flight; an id issued after the opt-out is erased at once.
- Erasures stay queued until the server confirms (200) and are retried
  at start-up and on the next send; the stored id carries the server
  that issued it, and an id from another server is erased there.
- 429 holds for its Retry-After; routes off (404 NOT_ENABLED) or 503
  hold an hour, so opted-in installs stop asking.
- An opt-in given to the older crowd signal is not consent to v1: it
  reads as off, and an update reopens the consent page.
- Disclosures: stock quantity, the server-derived network hash, the IP
  sentence, one name for the contributor ID, the uninstall case, and a
  release test step for erasure.
- Behavioural tests for the client with stubbed storage and fetch.

Refs #1606

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Code review skipped — your organization has no extra usage available to pay for this review.

If your organization's extra usage balance is empty, an organization admin can add extra usage credits at claude.ai/admin-settings/usage. If its monthly spend limit was reached, an admin can raise it on the same page. If neither applies, contact Anthropic support.

Once extra usage is available, someone with write access to this repository can comment @claude review on this pull request to trigger a review.

@vercel

vercel Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
rate Ready Ready Preview Nov 4, 2026 1:40pm UTC

Request Review

@github-actions github-actions Bot added area:frontend Frontend / Next.js / UI area:shared Shared packages / contracts / configs area:extension labels Nov 4, 2026
@coderabbitai

coderabbitai Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Explain Change →

Caution

Review failed

The pull request is closed.

Walkthrough

The extension now sends opted-in listing data as ExtensionObservation v1 through dedicated submitter and observation routes. It manages consent by version and retries submitter erasure. Extension and website disclosures describe the submitted fields, retention periods, and deletion behavior.

Changes: Extension Observation v1

Layer / File(s) Summary
Observation routes and client
packages/shared-data/src/constants/routes.ts, apps/extension/src/background/observation-client.ts, apps/extension/src/security-guards.ts
The route constants add submitter and observation endpoints. ObservationClient registers and reuses submitter IDs, sends observations, and queues erasure requests for retry.
Worker wiring and validation
apps/extension/src/background/service-worker.ts, apps/extension/src/types.ts, apps/extension/src/test-extension-pipeline.ts
The worker sends v1 observations through the client and requires consent version 2. It retries erasures on startup and installation, and asks the client to forget contributions when opt-in is disabled. Tests cover submission, consent, and erasure behavior.
Contribution disclosures
apps/extension/CHROMEWEBSTORE.md, apps/extension/RELEASE.md, apps/extension/src/_locales/*/messages.json, apps/web/locales/*
The disclosures and release notes describe observation fields, submitter IDs, retention periods, and erasure on opt-out. The locale messages also add observation field labels.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant ExtensionServiceWorker
  participant ObservationClient
  participant ExtensionAPI
  ExtensionServiceWorker->>ObservationClient: Send observation
  ObservationClient->>ExtensionAPI: Register submitter if needed
  ObservationClient->>ExtensionAPI: Submit ExtensionObservation v1
  ExtensionServiceWorker->>ObservationClient: Request erasure on opt-out
  ObservationClient->>ExtensionAPI: Delete submitter and retry erasure
Loading

Possibly related issues

  • DOS/Rate issue 1606: The PR implements its described ExtensionObservation v1 submission and disclosure updates. The summaries do not establish that quarantine storage or server-side verification is complete.

Suggested labels: area:extension, area:shared, area:frontend

Suggested reviewers: dos

Merge Risk: Moderate · up to 254f6

Resolve opt-out deletion reliability before merging: failures can leave contributed data unerased after users disable contributions. Correct the related disclosures and bounded submission issues. Server-side storage remains off by default, limiting immediate exposure.

- [ ] Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

apps/extension/src/background/observation-client.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

apps/extension/src/background/service-worker.ts

ESLint skipped: the matched ESLint configuration already failed (missing-dependency).

apps/extension/src/security-guards.ts

ESLint skipped: the matched ESLint configuration already failed (missing-dependency).

  • 3 others

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

dos[bot]
dos Bot approved these changes Nov 4, 2026

@dos dos Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⏱️ Code Review completed (14 eligible files · 63,379 chars · 3 model call(s) · 3 PR unit(s))

Whole diff with bounded context at base 59676e6 / head 254f608.

✅ CLEAN_PASS: Complete diff reviewed

Context investigation was bounded to the explicitly requested snapshot paths.

@JOY
JOY (JOY) merged commit d2445e2 into dev Nov 4, 2026
20 of 21 checks passed
@JOY
JOY (JOY) deleted the feat/1606-extension-v1-switch branch November 4, 2026 13:48

This branch was successfully deployed

1 active deployment
Preview — 254f6085 Deployed Nov 4, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:extension area:frontend Frontend / Next.js / UI area:shared Shared packages / contracts / configs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant