Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/tracking/spec/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ Versioning for the tracking specification itself, independent of the `@codefast/

Because the extension documents cite fast-moving law and platform contracts, a **citation refresh** (re-verifying a source, correcting an access-dated fact) is a patch even when it changes a jurisdictional claim — the _engineering_ contract is unchanged. When a re-verified legal fact changes what an implementation must _do_ (e.g. a region's `optOutSignalBinding` flips), that is a minor.

## 1.0.1 — 2026-07-18

### spec-data-subject-rights — citation refresh (patch)

- **GA4 User Deletion — corrected to the current API.** The legacy v3 `userDeletionRequests:upsert` cited in §3 and vector DSR-V3 was sunset with Universal Analytics. Updated to the Analytics Admin API `properties.submitUserDeletion` (`analytics.edit` scope), keyed by a flat `clientId`. DSR-V3 and `data-subject-rights.json` now snapshot `{ body: { clientId }, url }` at `.../v1alpha/properties/{propertyId}:submitUserDeletion`. Implemented in the reference package as `buildGa4UserDeletionRequest` / `submitGa4UserDeletion`.

## 1.0.0 — 2026-07-18

Initial published specification.
Expand Down
8 changes: 4 additions & 4 deletions packages/tracking/spec/spec-data-subject-rights.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ Actors: **System** = this library (near-stateless; forwards to destinations, own

Platforms ship their own DSR tooling; the system MUST delegate rather than reimplement.

- **GA4 — a real per-visitor deletion API exists.** The User Deletion API accepts `CLIENT_ID` / `USER_ID` / `APP_INSTANCE_ID`; GA4's `client_id` (in the `_ga` cookie) is the per-device analog of an analytics id ([developers.google.com/analytics/devguides/config/userdeletion/v3](https://developers.google.com/analytics/devguides/config/userdeletion/v3)). Limits the spec MUST state honestly: removed from the Individual User report within ~72 hours, purged at the next ~bimonthly run, and it does **not** reach previously-aggregated reports or BigQuery exports.
- **GA4 — a real per-visitor deletion API exists.** GA4's `client_id` (in the `_ga` cookie) is the per-device analog of an analytics id. The legacy v3 `userDeletionRequests:upsert` was **sunset with Universal Analytics**; the current path is the Analytics Admin API `properties.submitUserDeletion` ([developers.google.com/analytics/devguides/config/userdeletion/migration](https://developers.google.com/analytics/devguides/config/userdeletion/migration)), which keys the subject with a flat `clientId` (a oneof with `userId` / `appInstanceId` / `userProvidedData`) and needs the `analytics.edit` OAuth scope. Limits the spec MUST state honestly: removed from the Individual User report within ~72 hours, purged at the next ~bimonthly run, and it does **not** reach previously-aggregated reports or BigQuery exports.
- **Meta — no per-browser-UUID deletion API.** Meta's Data Deletion Request Callback is Facebook-Login-specific (an app-scoped `user_id` in a `signed_request`); it does **not** delete pixel/Conversions-API browser data. The system's realistic lever is **stop-send (gate) + clear Meta cookies** (spec-destinations §5). **The spec MUST NOT imply a Meta per-visitor deletion hook exists.**

**Posture:** the destination interface gains a generic `onErasure(id)` capability; a **GA4 reference binding** calls the User Deletion API with the destination `client_id`; the **Meta reference behavior** is cookie-clear + stop-send, documented as "no per-visitor deletion API upstream." Do **not** build a bespoke deletion store.
**Posture:** the destination interface gains a generic `onErasure(id)` capability; a **GA4 reference binding** calls the Admin API `submitUserDeletion` with the destination `client_id`; the **Meta reference behavior** is cookie-clear + stop-send, documented as "no per-visitor deletion API upstream." Do **not** build a bespoke deletion store.

## 4. Retention and rotation

Expand All @@ -54,7 +54,7 @@ The spec asserts only:

- **(a)** the id is a pseudonymous device identifier (personal data / unique identifier) — stated plainly for the controller's privacy notice;
- **(b)** the system's DSR surface is exactly the gate, `clear()` + server expiry, a per-destination erasure hook, and id exposure;
- **(c)** access, portability, and destination-data deletion are delegated to destinations (GA4 reference binding calls the User Deletion API; Meta = stop-send + cookie-clear);
- **(c)** access, portability, and destination-data deletion are delegated to destinations (GA4 reference binding calls the Admin API `submitUserDeletion`; Meta = stop-send + cookie-clear);
- **(d)** rectification and portability are not applicable at the system layer — say so, do not build them;
- **(e)** retention is a controller proportionality choice (1-year default configurable; rotation optional and noted in tension with the ITP re-issue).

Expand All @@ -64,7 +64,7 @@ The spec asserts only:

**DSR-V2 — withdrawal triggers destination erasure hook.** On withdrawal, each registered destination's `onErasure(id)` is invoked exactly once with the resolved destination identifier, and swallows failures (tracking never breaks the app).

**DSR-V3 — GA4 delete binding.** Given a `client_id`, the GA4 destination emits a User Deletion API request with `id.type = CLIENT_ID`, `id.userId = <client_id>` — snapshot the request shape (no network).
**DSR-V3 — GA4 delete binding.** Given a `client_id` and GA4 `propertyId`, `buildGa4UserDeletionRequest` emits an Admin API `submitUserDeletion` request — `POST .../v1alpha/properties/{propertyId}:submitUserDeletion` with body `{ "clientId": "<client_id>" }` — snapshot the request shape (no network). The legacy v3 `{ id: { type: "CLIENT_ID", userId } }` shape is retired.

**DSR-V4 — Meta erasure is cookie-clear + stop-send only.** The Meta destination `onErasure` clears Meta cookies and halts sending, and does **not** fabricate a non-existent per-visitor deletion API call.

Expand Down
11 changes: 8 additions & 3 deletions packages/tracking/spec/vectors/data-subject-rights.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,16 @@
{
"id": "dsr.ga4.delete-binding",
"source": "DSR-V3",
"summary": "the GA4 destination emits a User Deletion API request keyed on the client_id",
"summary": "buildGa4UserDeletionRequest emits an Admin API submitUserDeletion request keyed on the client_id",
"kind": "pure",
"operation": "buildGa4UserDeletionRequest",
"input": { "clientId": "GA1.2.123.456" },
"expect": { "request": { "id": { "type": "CLIENT_ID", "userId": "GA1.2.123.456" } } }
"input": { "clientId": "123.456", "propertyId": "1234567" },
"expect": {
"request": {
"body": { "clientId": "123.456" },
"url": "https://analyticsadmin.googleapis.com/v1alpha/properties/1234567:submitUserDeletion"
}
}
},
{
"id": "dsr.meta.erasure-is-cookie-clear-and-stop-send",
Expand Down