Skip to content

Rename Synthetic ID to Edge Cookie (EC) and simplify generation#479

Open
ChristianPavilonis wants to merge 6 commits intomainfrom
feature/ssc-update
Open

Rename Synthetic ID to Edge Cookie (EC) and simplify generation#479
ChristianPavilonis wants to merge 6 commits intomainfrom
feature/ssc-update

Conversation

@ChristianPavilonis
Copy link
Collaborator

@ChristianPavilonis ChristianPavilonis commented Mar 12, 2026

Summary

  • Rename "Synthetic ID" to "Edge Cookie (EC)" across all external-facing identifiers, config, internal Rust code, and documentation
  • Simplify EC hash generation to use only client IP (IPv4 or /64-masked IPv6) with HMAC-SHA256, removing User-Agent, Accept-Language, Accept-Encoding, random_uuid inputs and Handlebars template rendering
  • Downgrade EC ID generation logs to trace level since client IP and EC IDs are sensitive data
  • Remove unused counter_store and opid_store config fields (vestigial from template-based generation)

Changes

File Change
crates/common/src/edge_cookie.rs (new) Renamed from synthetic.rs; simplified hash to IP-only HMAC-SHA256; removed template rendering
crates/common/src/synthetic.rs (deleted) Replaced by edge_cookie.rs
crates/common/src/constants.rs Renamed constants: SYNTHETIC_ID_HEADERHEADER_X_TS_EC, cookie/query names updated
crates/common/src/cookies.rs Updated cookie name synthetic_idts-ec, function names to EC
crates/common/src/error.rs Renamed SyntheticIdEc error variant; removed Template variant
crates/common/src/settings.rs Config section [synthetic][edge_cookie]; removed template, counter_store, opid_store fields
crates/common/src/settings_data.rs Updated settings data references to EC
crates/common/src/lib.rs Module rename syntheticedge_cookie
crates/common/src/http_util.rs Updated header references to EC naming
crates/common/src/openrtb.rs Wire field synthetic_freshec_fresh (breaking wire-protocol change)
crates/common/src/proxy.rs Updated query param synthetic_idts-ec
crates/common/src/publisher.rs Updated EC references
crates/common/src/consent/mod.rs Renamed allows_ssc_creationallows_ec_creation with updated docs and tests
crates/common/src/consent/kv.rs Updated doc comments to EC naming
crates/common/src/auction/formats.rs Response headers: X-Synthetic-IDX-TS-EC, X-Synthetic-FreshX-TS-EC-Fresh
crates/common/src/integrations/*.rs Updated EC references across all integrations
crates/common/src/test_support.rs Updated test helpers for EC naming
crates/common/Cargo.toml Removed handlebars dependency
crates/js/lib/src/integrations/gpt/index.ts Updated header name reference
Cargo.lock Removed handlebars-related entries
crates/integration-tests/Cargo.lock Synced shared dependency versions (derive_more 2.1.1)
trusted-server.toml Config section [synthetic][edge_cookie]; removed template, counter_store, opid_store fields
CLAUDE.md Updated file paths and terminology
docs/guide/edge-cookies.md (new) Replaces synthetic-ids.md
docs/guide/synthetic-ids.md (deleted) Replaced by edge-cookies.md
docs/**/*.md (30+ files) Renamed SyntheticID → Edge Cookie throughout documentation

Breaking changes

This is a clean break with no backward-compatible aliases. All downstream consumers must update in lockstep.

Category Old New Notes
OpenRTB wire field user.ext.synthetic_fresh user.ext.ec_fresh PBS modules and analytics pipelines must update
Response headers X-Synthetic-ID X-TS-EC
X-Synthetic-Fresh X-TS-EC-Fresh
X-Synthetic-Trusted-Server (removed) Duplicated the value of X-Synthetic-ID; no replacement
Cookie name synthetic_id ts-ec Existing cookies silently invalidated on deploy
Query parameter synthetic_id ts-ec Downstream proxies/endpoints must update
Config section [synthetic] [edge_cookie] No serde alias
Env var prefix TRUSTED_SERVER__SYNTHETIC__* TRUSTED_SERVER__EDGE_COOKIE__* Old vars silently ignored
Config fields counter_store, opid_store (removed) Unused after IP-only HMAC simplification
Config field template (removed) Handlebars templating replaced by IP-only HMAC

Closes

Closes #462

Test plan

  • cargo test --workspace — all tests passing
  • cargo clippy --all-targets --all-features -- -D warnings — zero warnings
  • cargo fmt --all -- --check — clean
  • JS tests: cd crates/js/lib && npx vitest run — all tests passing
  • Prettier: cd docs && npx prettier --check . — clean
  • Integration dependency versions synced

Checklist

  • Changes follow CLAUDE.md conventions
  • No unwrap() in production code — use expect("should ...")
  • Uses log macros (not println!)
  • New code has tests
  • No secrets or credentials committed

Copy link
Collaborator

@aram356 aram356 left a comment

Choose a reason for hiding this comment

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

Summary

Comprehensive rename of Synthetic ID → Server Side Cookie (SSC) with simplified HMAC generation (client IP only, dropping Handlebars templates). Cookie, header, query parameter, config section, and error variants are all renamed. The handlebars dependency is removed.

Blocking

🔧 wrench

  • Missing new SSC headers in INTERNAL_HEADERS: x-ts-ssc-fresh and x-ts-ssc-trusted-server are defined but not added to the INTERNAL_HEADERS blocklist, allowing potential leak of user-identity headers to third-party origins via copy_custom_headers (crates/common/src/constants.rs:49)

Non-blocking

🤔 thinking

  • Cookie name break: synthetic_idts-ssc silently invalidates existing user cookies (crates/common/src/constants.rs:3)
  • Query parameter break: synthetic_idts-ssc in proxy forwarding may break downstream consumers (crates/common/src/proxy.rs:438)
  • Entropy reduction: HMAC now uses only client IP; users behind same NAT share the HMAC prefix (crates/common/src/ssc.rs:63)

📝 note

  • Silent template field removal: Old configs with template = "..." are silently ignored (no deny_unknown_fields). Operators won't know the field is unused.

CI Status

  • cargo fmt: PASS
  • cargo clippy: PASS
  • cargo test: PASS
  • vitest: PASS
  • format-docs: PASS
  • format-typescript: PASS
  • CodeQL: PASS

prk-Jr
prk-Jr previously requested changes Mar 16, 2026
Copy link
Collaborator

@prk-Jr prk-Jr left a comment

Choose a reason for hiding this comment

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

Summary

This PR completes the Synthetic ID → Server Side Cookie rename and simplifies ID generation, but I found a couple of migration/privacy regressions plus a docs/behavior mismatch that should be fixed before merge.

Blocking

🔧 wrench

  • Docs and implementation diverge on consent + proxy behavior: the new SSC docs state that ID generation/forwarding are consent-gated and that /first-party/proxy generates a new SSC ID when missing, but the current implementation still generates IDs unconditionally in crates/common/src/publisher.rs:239, crates/common/src/integrations/registry.rs:659, and crates/common/src/auction/formats.rs:81, while proxy forwarding only reuses an existing ID in crates/common/src/proxy.rs:427. Either implement the documented rules or update the docs so they match the code.

CI Status

  • fmt: PASS
  • clippy: PASS
  • rust tests: PASS
  • js tests: PASS

@ChristianPavilonis ChristianPavilonis dismissed stale reviews from aram356 and prk-Jr March 16, 2026 16:21

Addressed: added x-ts-ssc-fresh to INTERNAL_HEADERS, removed x-ts-ssc-trusted-server. Non-blocking items are intentional design choices.

@aram356 aram356 marked this pull request as draft March 17, 2026 16:03
@ChristianPavilonis ChristianPavilonis changed the title Rename Synthetic ID to Server Side Cookie (SSC) and simplify generation Rename Synthetic ID to Edge Cookie (EC) and simplify generation Mar 18, 2026
@ChristianPavilonis ChristianPavilonis marked this pull request as ready for review March 19, 2026 14:52
Copy link
Collaborator

@aram356 aram356 left a comment

Choose a reason for hiding this comment

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

Summary

Thorough rename of Synthetic ID → Edge Cookie (EC) across 71 files with a clean simplification of the HMAC generation (removing handlebars templating and multi-field inputs in favor of IP-only). All CI checks pass. No blocking issues found.

Non-blocking

🤔 thinking

  • Client IP logged at debug instead of trace: the old code used log::trace! for the HMAC input string; the new code uses log::debug! for the client IP, which is more likely to be enabled in production (edge_cookie.rs:79)
  • X-Synthetic-Trusted-Server header silently removed: the old auction response set three headers; the new code sets two — the third (which duplicated the first) is dropped. Worth noting in the "Breaking changes" section if any downstream consumer depends on it (formats.rs:312-313)

👍 praise

  • Clean dependency removal: removing handlebars simplifies the pipeline, reduces the dep tree, and eliminates the Template error variant. The entropy trade-off is well-documented in the generate_ec_id doc comment
  • Thorough rename: only one intentional reference to the old name remains (the openrtb.rs breaking-change doc comment). Zero stale references across all crates, configs, docs, and CI scripts
  • DRY improvement in proxy.rs: the duplicated query-param upsert logic is consolidated into upsert_ec_query_param (proxy.rs:446)

⛏ nitpick

  • PR checklist mentions tracing: the checklist says "Uses tracing macros" but the project convention and actual code use log macros
  • Indentation on HMAC error context: the .change_context(TrustedServerError::Ec { block reads oddly — cargo fmt accepts it but a manual tweak would improve readability (edge_cookie.rs:82)

CI Status

  • cargo fmt: PASS
  • cargo clippy: PASS
  • cargo test: PASS
  • vitest: PASS
  • format-docs: PASS
  • format-typescript: PASS
  • integration tests: PASS
  • browser integration tests: PASS
  • CodeQL: PASS

Copy link
Collaborator

@aram356 aram356 left a comment

Choose a reason for hiding this comment

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

Summary

Large rename from "Synthetic ID" → "Edge Cookie (EC)" across ~71 files. Removes synthetic.rs and replaces it with a simplified HMAC-based edge_cookie.rs. Also renames wire-format fields (OpenRTB, HTTP headers, query params) and the [synthetic][edge_cookie] config section. All CI checks pass.

Blocking

🔧 wrench

  • Wire-format breaking changes need migration strategy: This PR introduces several wire-format breaking changes that will affect downstream consumers:

    1. OpenRTB user.ext.synthetic_freshuser.ext.ec_fresh (crates/common/src/openrtb.rs:56) — Any PBS module or analytics pipeline reading synthetic_fresh will silently lose this signal.
    2. Query param synthetic_idts-ec (crates/common/src/proxy.rs:453) — Any downstream endpoint parsing synthetic_id from the URL will stop receiving the ID.
    3. Response headers X-Synthetic-ID / X-Synthetic-Fresh / X-Synthetic-Trusted-ServerX-TS-EC / X-TS-EC-Fresh (crates/common/src/auction/formats.rs:309) — Also removes X-Synthetic-Trusted-Server entirely.
    4. Config section [synthetic][edge_cookie] — Existing deployments with TRUSTED_SERVER__SYNTHETIC__* env vars will silently lose their overrides.

    Recommendations:

    • Consider #[serde(alias = "synthetic_fresh")] on ec_fresh for backward-compatible deserialization
    • For query params, consider accepting both synthetic_id and ts-ec during a transition period
    • Document the env var migration: TRUSTED_SERVER__SYNTHETIC__*TRUSTED_SERVER__EDGE_COOKIE__*
    • Confirm whether any live PBS modules or analytics consumers depend on these field names

❓ question

  • template field removed from config (crates/common/src/settings.rs): The old Synthetic struct had a validated template: String field. The new EdgeCookie removes it and hardcodes HMAC-based generation. Was the template used in any production deployment? Is there a migration path for publishers who customized their ID template?

Non-blocking

🤔 thinking

  • EC ID logged at debug level (crates/common/src/edge_cookie.rs:92): Full EC ID is logged. In Fastly production logs this could surface user-trackable identifiers. See inline comment.

⛏ nitpick

  • Indentation of .change_context (crates/common/src/edge_cookie.rs:82-84): Struct literal closing brace misaligned. See inline comment.

📝 note

  • counter_store and opid_store fields remain in EdgeCookie (crates/common/src/settings.rs): These fields exist in the config struct but are not used by edge_cookie.rs (which only reads secret_key). If they are still used elsewhere (e.g., crates/fastly) this is fine — if not, they could be cleaned up.

CI Status

  • fmt: PASS
  • clippy: PASS
  • cargo test: PASS
  • vitest: PASS
  • integration tests: PASS
  • browser integration tests: PASS
  • CodeQL: PASS

Copy link
Collaborator

@prk-Jr prk-Jr left a comment

Choose a reason for hiding this comment

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

Summary

This PR does the EC rename thoroughly and CI is currently passing, but I found a few behavior and upgrade issues that should be addressed before merge. The most important ones are consent bypass on integration routes, increased logging of sensitive identifier inputs/outputs, and the hard config/env break with no compatibility shim.

Blocking

🔧 wrench

  • Config/env compatibility break: The runtime now only deserializes edge_cookie config and TRUSTED_SERVER__EDGE_COOKIE__* env vars, so existing deployments using [synthetic] / TRUSTED_SERVER__SYNTHETIC__* will silently stop populating this section unless they are migrated in lockstep. Consider keeping a one-release alias for the old names or emitting a targeted migration error so upgrades fail clearly instead of implicitly. (settings.rs, settings.rs)

Non-blocking

♻️ refactor

  • Remove or justify dead EC config surface: counter_store and opid_store still appear required in runtime config and docs, but after the IP-only simplification I couldn't find any runtime reads of those fields. If they are no longer needed, removing them will simplify operator setup and prevent confusion. If they are planned for follow-up work, the docs should say that explicitly. (settings.rs, configuration.md)

CI

GitHub checks are currently passing (cargo test, cargo fmt, vitest, integration tests, browser integration tests, CodeQL, and analysis jobs).

- Rename all external identifiers: x-synthetic-id → x-ts-ec, synthetic_id
  cookie → ts-ec, synthetic_fresh → ec_fresh
- Simplify hash generation to use only client IP with HMAC-SHA256, removing
  User-Agent, Accept-Language, Accept-Encoding, and template rendering
- Rename config section [synthetic] → [ec] with backward-compat alias
- Rename ec.rs to edge_cookie.rs for clarity
- Remove handlebars dependency (and transitive deps)
- Add x-ts-ec-fresh to internal headers blocklist
- Update all docs with new Edge Cookie (EC) terminology
- Fix review findings: remove redundant serde rename, stale optimization
  entry, leftover 'synthetic' references in agent configs and docs

Closes #462
- Rename allows_ssc_creation → allows_ec_creation and update all doc
  comments, test names, and assertion messages to use Edge Cookie (EC)
- Fix intra-doc link [`ec`] → [`edge_cookie`] in lib.rs
- Downgrade test log from info to debug in edge_cookie.rs for consistency
- Add fallback comment and wire-protocol breaking-change doc in openrtb.rs
- Run prettier --write on 3 doc files to fix format-docs CI
- Update integration-tests Cargo.lock to sync derive_more 2.1.1
…ences

- Rename TRUSTED_SERVER__SYNTHETIC__SECRET_KEY to TRUSTED_SERVER__EC__SECRET_KEY
  in CI action and local integration test scripts (root cause of CI failure:
  Viceroy could not start without the EC secret key)
- Update stale doc reference synthetic.secret_key → ec.secret_key
- Update stale comments in consent_config.rs and consent/types.rs
- Rename struct Ec → EdgeCookie, field settings.ec → settings.edge_cookie
- Add serde alias "ec" for backward compatibility with existing configs
- Update all TOML configs, env vars, CI actions, scripts, and docs
- TRUSTED_SERVER__EC__* env vars → TRUSTED_SERVER__EDGE_COOKIE__*
- Validation messages now reference edge_cookie.secret_key
- Downgrade EC ID/IP log statements from debug to trace to prevent
  sensitive data appearing in production logs (edge_cookie.rs)
- Fix .change_context indentation in HMAC error handling
- Remove unused counter_store and opid_store fields from EdgeCookie
  config struct, all test fixtures, TOML configs, and documentation
- Add serialization test asserting ec_fresh wire field name
- Fix edge-cookies.md config section reference and consent language
- Update error-reference.md to reflect HMAC-based generation
- Update configuration.md to remove dead KV store field docs
@ChristianPavilonis
Copy link
Collaborator Author

Addressing review feedback

@aram356 @prk-Jr — pushed a commit addressing the actionable items from your reviews:

Fixed

  • Logging privacy regression — All log::debug! calls in edge_cookie.rs that logged client IPs or EC IDs are now log::trace! (lines 79, 92, 111, 119, 152)
  • .change_context indentation — Fixed the struct literal alignment in the HMAC error handling block
  • Removed dead counter_store/opid_store config — These vestigial fields have been removed from the struct, all test fixtures, TOML configs, and documentation
  • ec_fresh serialization test — Added user_ext_serializes_ec_fresh_not_synthetic_fresh in openrtb.rs
  • Docs accuracy — Fixed edge-cookies.md config section reference (ec[edge_cookie]), corrected consent language, updated error-reference.md to reflect HMAC-based generation
  • PR description — Added a full breaking changes table covering all wire-format, header, cookie, query param, config, and env var changes. Fixed checklist (tracinglog)

Acknowledged / deferred

  • Consent gate on integration routes (prk-Jr) — This is a broader design question we'll address in a follow-up PR
  • Wire-format backward compatibility (aram356) — This is an intentional clean break; no serde aliases or dual-accept. Not in production yet, so no downstream consumers to migrate

All CI checks pass (fmt, clippy, 707 Rust tests, 282 JS tests).

Copy link
Collaborator

@prk-Jr prk-Jr left a comment

Choose a reason for hiding this comment

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

👍 Looks good

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Edge Cookie (formerly SSC (formerly synthetic ID)) updates

3 participants