Skip to content

Add the integration response-header hook and the provider documentation set - #1047

Open
jwrosewell wants to merge 5 commits into
IABTechLab:mainfrom
jwrosewell:split/5-response-hook-docs
Open

Add the integration response-header hook and the provider documentation set#1047
jwrosewell wants to merge 5 commits into
IABTechLab:mainfrom
jwrosewell:split/5-response-hook-docs

Conversation

@jwrosewell

Copy link
Copy Markdown
Contributor

Fifth and final PR decomposing #838 (see #1043 for the series description). Stacked on #1046: until predecessors merge, this PR displays the cumulative diff, so review the top commit only.

What this PR contains

  • IntegrationResponseMutator lets an integration add headers (for example Accept-CH or detection-result headers) to outbound responses, applied on the platform-neutral response before the host adapter takes over.
  • The documentation set for the provider model: configuration reference for the [ec], [device], and [geo] sections, the Edge Cookie guide rewritten around providers and the permission model, setup and error-reference updates, and the permission-model guide joining the docs navigation.
  • The Fastly EC lifecycle integration-test job joins the test workflow, and the example configuration documents every provider selector.

With this PR merged, the five-PR series reproduces #838's head byte for byte (verified by merging the series in sequence and diffing the result against #838), and #838 can close as superseded.

Verification

Full gate green: fmt, clippy x6 with -D warnings, all four adapters' test suites, cross-adapter parity. The combined series merged onto today's main also passes the full suite (1,984 core tests), with one prepared resolution where the cargo aliases overlap #1004.

Produced with AI assistance under James Rosewell's direction; needs human review.

…ider

First slice of the PR 838 decomposition (one PR per feature, Edge Cookie
provider first). Introduces the EdgeCookieProvider trait and routes Edge
Cookie minting, cookie read-back, and KV keying through the selected
provider, so a vendor identifier round-trips verbatim instead of being
dropped by the built-in shape check.

- [ec] provider selector with per-provider [ec.providers.<key>] blocks;
  the hmac block carries the passphrase that previously lived on [ec].
  With no provider selected, no Edge Cookie is generated.
- Built-in provider: hmac (HMAC over client IP, preserves today's
  identity). The host-signal provider follows in the device slice,
  which supplies the host fingerprints it needs.
- Request evidence abstraction (crate::evidence) giving providers
  read access to the client IP, headers (including cookies), URL path,
  and query parameters.
- Adapter injection seam: RuntimeServices carries an optional vendor
  provider, so a vendor provider lives in its own crate and core never
  names it.
- Provider-declared identifier semantics: accepts_id gates cookie
  read-back and withdrawal, normalize_id_for_kv controls the KV key, so
  an opaque identifier survives byte for byte as both cookie value and
  storage key. Tests cover the verbatim round-trip with a non-default
  provider, evidence access at generate time, and opaque KV persistence.

Edge Cookie creation and use stay gated by the existing consent context
exactly as before, including with no provider selected; the permission
model replaces that input in a later slice. The client-set resolve path
and the response-header hook are later slices.

Config migration: [ec] passphrase is rejected as an unknown field; move
it to [ec.providers.hmac] and select provider = "hmac" to keep minting.
@jwrosewell
jwrosewell force-pushed the split/5-response-hook-docs branch from 35b9fbc to e1926b7 Compare August 20, 2026 01:47
…e provider

Second slice of the PR 838 decomposition. Device classification and
geolocation become selectable providers, mirroring the Edge Cookie
provider seam:

- [device] provider selects the classifier. The built-in default reads
  the User-Agent alone and makes no host call; the opt-in fastly
  provider strengthens the browser/bot gate with the host's TLS JA4 and
  HTTP/2 fingerprints (crates/device/fastly).
- [geo] provider selects geolocation. No provider is the default and
  resolves no location; provider = "platform" opts into the host's
  lookup (crates/geo/fastly wraps the Fastly host lookup behind the
  PlatformGeo trait).
- The host-signal Edge Cookie provider arrives with the capability it
  needs: the Fastly adapter injects the TLS/HTTP-2 fingerprints as a
  HostSignals service, and the provider mints from them plus the client
  IP. With no fingerprint at all it defers with a warning rather than
  degrading to an IP-only identifier.
- Device signals move to a field-based DeviceSignals derived in the
  adapter (derive_ua_only for hosts without fingerprints).
- The new crates join the fastly cargo aliases so they build, lint, and
  test in CI rather than compiling only transitively.
Third slice of the PR 838 decomposition. Permissions become the
primitive that gates identity features; consent is one of several ways
a permission is established:

- permissions.rs resolves a per-request PermissionState from the
  country/region baseline in permissions.yaml augmented by the session's
  signals (TCF, GPP, GPC, US Privacy). Permission names follow the
  Privacy Taxonomy Data Uses.
- The Edge Cookie gate moves from raw consent to the permission model:
  a provider declares required_permissions() and core executes it only
  when every one is set. With no provider selected the gate stays
  closed and identifiers are treated as absent, matching the stateless
  contract established in the first slice.
- Withdrawal separates from mere not-permitted: ec_storage_withdrawn
  (an explicit signal) expires the cookie and writes tombstones, while
  a pre-consent or fail-closed state only strips EC response headers.
- Bidstream EIDs gate on the permission model (gate_eids_by_permissions)
  instead of raw TCF consent.
- [geo] default_country becomes required: it names the permissions.yaml
  rule that applies when the geo provider leaves a request unmatched,
  validated at startup, so there is always a defined permission
  baseline.
- The consent module keeps building the ConsentContext; its EC-specific
  gating helpers move behind the permission model.
Fourth slice of the PR 838 decomposition. A provider that cannot derive
an identifier at the edge can now defer, run its own page script, and
mint from the value the page posts back:

- POST /_ts/api/v1/ec/resolve accepts the client-produced value, runs it
  through the selected provider's resolve_from_client (which must verify
  it before trusting it), and sets the ts-ec cookie from the result. The
  endpoint enforces the provider's required permissions before the
  provider runs.
- ClientFixedProvider is the built-in demonstration provider: it defers
  at the edge, its page script (ec_client_fixed) posts a fixed known
  word, and the endpoint mints when the word verifies. The script rides
  the tsjs bundle only when the provider is selected.
- A test-only TestIdProvider models a client-minted UUID identifier and
  proves the full scenario: organic request defers, the client posts a
  value, the cookie is set, and the identifier round-trips verbatim on
  the next request.
- EcContext keeps the injected provider and host signals for the
  request, so the resolve path rebuilds the provider with the same
  services as the organic path.
…on set

Fifth and final slice of the PR 838 decomposition:

- IntegrationResponseMutator lets an integration add headers (for
  example Accept-CH or detection-result headers) to outbound responses,
  applied on the platform-neutral response before the host adapter takes
  over.
- Documentation for the provider model: configuration reference for the
  [ec], [device], and [geo] sections, the Edge Cookie guide rewritten
  around providers and the permission model, setup and error-reference
  updates, and the permission-model guide joins the docs navigation.
- The Fastly EC lifecycle integration-test job joins the test workflow.
- The example configuration documents every provider selector.
@jwrosewell
jwrosewell force-pushed the split/5-response-hook-docs branch from e1926b7 to 7ebce99 Compare August 20, 2026 02:22
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.

1 participant