- From: Martin Thomson <mt@mozilla.com>
- Date: Tue, 26 May 2026 11:45:33 +1000
- To: Tim Geoghegan <timgeog+ietf@gmail.com>
- Cc: public-patwg@w3.org
Hi Tim, thanks for the feedback.
I'll respond to things inline, but most of your questions should be
answered in the spec. If they were, I'll give you citations;
otherwise, you'll find the answer in the pull request. I've split the
changes off where it makes sense to do that, but there's a collection
of minor stuff here:
https://github.com/w3c/attribution/pull/440
On Sat, May 23, 2026 at 2:13 AM Tim Geoghegan <timgeog+ietf@gmail.com> wrote:
> ## Discovering aggregation services
>
> 3.3. Finding a Supported Aggregation Service [3] describes asking the browser for a list of aggregation services with `navigator.attribution?.aggregationServices`. How does an aggregation service get in that list? My expectation would be that ad networks are the ones who are arranging contractual relationships with aggregator operators, not the browsers. When they do that, is there some out-of-band process by which they register their aggregation services with browsers? Is there something akin to web PKI root store programs where browsers vet and monitor aggregation services?
You have the right idea, but this is outside the scope of what this
specification can deal with. That said, we probably owe it to readers
of this specification to spell that out.
https://github.com/w3c/attribution/pull/436
> Next, it appears that an aggregation service is represented as a key-value pair in a dictionary, where the key is a URL like https://aggregator.example/dap and the value is an `AttributionAggregationProtocol` value like `dap-18-histogram`. But in DAP, you need at the very least two URLs, one for each aggregator. In particular, it's important that the DAP client (which in this case is the user-agent) fetches each aggregator's HPKE configuration from a trusted source. Privacy won't hold up if the leader provides the helper's HPKE configuration. So: what exactly is available at https://aggregator.example/dap? Is it maybe a JSON document which in turn contains both aggregator URLs and other config and metadata? If so, what's the schema of that document, and who is responsible for constructing that document and serving it?
We deal with these questions much later in the document. At this
early stage of the document, it's a detail that we don't think that
someone (or at least someone who isn't up on the details of DAP)
should concern themselves with.
See Section 10.4 (as you mention) and
https://w3c.github.io/attribution/#s-mpc:~:text=The%20URL%20for%20%22dap%2D18%2Dhistogram%22%20is%20expected%20to%20identify%20the%20DAP%20Leader%20role
In short, the aggregationService URL is an identifier for an entire
collection of configuration that the browser needs to have available
before this API is called. That includes the details for both leader
and helper, which covers the HPKE config and base URLs. The
information can't be fetched for reasons related to web privacy; the
browser needs to be basically hard-coded with the information for this
to work.
> 10.4. Unconfigured Browsers [5] discusses "prioritiz[ing] the acquisition of aggregation service configuration in a way that is not observable to sites" to avoid introducing a fingerprinting signal. This would include fetching HPKE configurations for DAP aggregators. But in DAP, those HPKE configs have a lifetime indicated by a `Cache-Control` header. So what if HPKE config expiration races with a site measuring a conversion? Is the user-agent required to avoid yielding control to conversion site JavaScript unless the user-agent has satisfied itself that it has an aggregation service config that will remain valid long enough for the site JS to run?
The entire point of that section is to highlight that browsers can't
just do inline fetching of HPKE configuration. They need to have some
process that is not vulnerable to this sort of leakage. If that means
proactive fetching rather than waiting for the cached value to expire,
that's their problem to solve.
If cache-control lifetimes on the resources served by DAP aggregators
is too short for browsers to handle properly, then that might be
something to add to browser requirements for their trust program.
From my perspective, the implementation here is pretty simple. If the
browser is fetching the HPKE configuration in real time (and that is
not my preferred outcome) then it needs to fetch proactively such that
there is virtually no risk that it ever lacks the necessary HPKE
configuration to answer requests.
In practice, the best implementation strategy is to use whatever
remote configuration system the browser already has. The system in
Firefox is called Remote Settings, and it allows us to deliver
time-sensitive configuration information like this. (We have not yet
worked out what to do for those people who choose to disable this
capability. That's going to disable the API in a way that is visible
to sites if we're not careful. That visibility would be bad for
privacy; the design is such that it should be impossible for a site to
detect that the API is disabled.)
> ## DAP integration
>
> 7.1.2. DAP Extensions [7] discusses extensions AL1 needs. Having clicked through to draft-thomson-ppm-dap-attribution-01, I'm not sure I understand the purpose of the `budget_source` extension. It's described as being opaque, so much so that the contents of the extension aren't even specified. But more to the point, it's not clear what, if anything, DAP aggregators are meant to do with this extension. If the idea is to introduce some kind of domain separation between one task and the next, couldn't that be achieved without an extension, for instance by using `task_info` or the task ID? [8] That being said, I don't object to a task extension that does nothing but indicate that all participants agree that some mechanism is in use.
The mechanism is buried here:
https://w3c.github.io/attribution/#dap-extensions:~:text=The%20extension%20codepoint%20for%20privacy%20budget%20source%2C%20mapped%20to%20the%20serialized%20host%20component%20of%20topLevelSite
It doesn't carry a lot of explanation, but I don't think that there is
much point in going into the design details here. The plan was to
have the explanation in the IETF doc, but I concede that it's a bit
sparse there. I'll juice that up a little.
https://github.com/martinthomson/dap-attribution/pull/13
Task diversity/domain separation is about all this achieves. The
privacy architecture requires that reports for different sites cannot
be aggregated together. That means binding the identity of the thing
that controls the privacy budget to the task. The task_info parameter
could be used for this much, but we have identified that as something
that we might want to give sites access to (see
https://github.com/w3c/attribution/issues/363 for discussion of that),
so in the interests of keeping it clear this is moved to an extension.
Given that these parameters might never traverse the wire anywhere, is
there any reason why this is a poor design choice?
> Also, 7.1.2. says the extensions are "necessary for this application". Is "necessary" enough or should this be an RFC 2119 MUST to ensure applications do the right thing w.r.t. constructing task extensions and HPKE AAD?
This is a style/norms difference. W3C specs generally don't both with
MUST for the stuff that is described with algorithms, as much of the
processing here is. Implementations are required to run the
algorithms, or an equivalent that is functionally indistinguishable to
sites. So yes, you can't implement this without this extension; the
steps ensure that there is effectively a MUST-level requirement. The
"necessary" is explanation only.
> Much of 7.1.3. Report Encryption for DAP [9] restates things already explicitly laid out in documents like draft-ietf-ppm-dap or draft-ietf-ppm-l1-bound-sum. For example, there's no need to dictate the use of Field128, because Prio3L1BoundSum is already defined to use that field and no other. Or in step 11, you say to construct report IDs by sampling 16 bytes from a CSPRNG, which duplicates the prescription in DAP 4.4.2 [10].
This is one of the hard parts of this style of specification. We have
to spell out the algorithm in enough detail that it is complete. The
current approach is slightly overly-detailed as a result. I've wound
some of it back, but there's a ragged edge to deal with. DAP doesn't
have a clean interface I can invoke. It's almost fractal in nature,
even. (Nor would it be clean if it existed: there are far too many
parameters.)
In any case, after reviewing, I think that this is as close as a W3C
spec can get to saying "do DAP".
> I'm concerned about the risk of this document and PPM documents accidentally disagreeing, which is a headache for implementers and requires errata to clean up. Could AL1 say approximately "do the upload interaction per DAP section 4.4, using the Prio3L1BoundSum VDAF"?
I share that concern. But when I reviewed the procedures, the amount
of DAP/PPM stuff that was duplicated was pretty small. Some small
pieces, but always in service of being able to say where the AL1
pieces go. The field point you make is a clear example of something
that doesn't need to be fixed, but I couldn't see cuts that work
elsewhere. Without the small overlap, there's no way to explain how
different values slot into the DAP/PPM pieces.
I've opened a PR to tweak some of this:
https://github.com/w3c/attribution/pull/437
> `min_batch_size` is fixed to 20. Can you comment on this choice? Why is it fixed, and why 20? Same goes for the `time_precision` of 5.
These are basically arbitrary values. Unlike other DAP uses -- those
that rely on shuffle DP, in particular -- the batch size doesn't
really form any load-bearing part of the privacy architecture, so the
value was chosen after not a lot of consideration and a short
conversation with the group. It could be 1, but my sense was that
people might be uncomfortable with that.
The time precision is, again, arbitrary. We need a fixed value and
this seemed like a reasonable choice. I can't imagine any way in
which this matters for anyone, except when you are attempting to use
time in anti-replay in the aggregators.
If you have suggestions for rationale that might guide this decision
in a more principled fashion, I'm open to anything you might have. I
couldn't come up with anything.
> `batch_mode` is TBD. OK, fine, the document isn't finished, but I'm curious: draft-thomson-ppm-dap-attribution-01 defines a "collector chosen" batch mode. Is the intent that AL1 will require that batch mode unconditionally or will it allow multiple batch modes? I think either is fine.
It's only the code point that is TBD. This fixes the batch mode to
the "collector selected" mode. No other mode is compatible with the
architecture we've defined. No hard reason to deny other modes, but
they don't lead to better outcomes.
> ## Histogram layout consensus
>
> I'm going to guess this has been argued about a bunch already. But here I go anyway.
>
> In 3.4. Saving Impressions [4], we learn that the `saveImpression` side indicates the `histogramIndex` into which the impression should be recorded. 4.1. Impression Store [18] tells us that the user-agent is meant to store both that index and the impression site where `saveImpression` was called.
>
> IIUC, the histogram buckets are labeled by site. So a particular `saveImpression` call creates an association between a histogram index and a site, right?
That's only an example. Sites need to work out what histogram
labeling they want to use.
The spec says:
> Different groupings might be used for different purposes. For instance, grouping by creative (the content of an ad) might be used to learn which creative works best.
> The measurement site doesn't need to agree on the histogram layout, but it does have to indicate the size of the histogram [12].
>
> All this means that every impression site has to share a consistent view of the histogram layout, and conversion sites at least have to agree with other sites on the size.
Consistent use of indices is a natural requirement for this setup to
work. But it's not quite "every impression site". It's "every
impression that might be selected for attribution by a single
conversion". A site can use other fields, like the `matchValue`, to
separate impressions out into different histograms. For instance, you
could have `matchValue=2` correspond to an impression site breakdown,
`matchValue=3` correspond to a creative breakdown, and `matchValue=4`
correspond to a combination of age demographic and impression site.
(Just an aside: I note you say "conversion sites", plural.
Impressions can target multiple impression sites, but that's a pretty
advanced usage. We do expect most ads to target one destination in
most cases. The coordination problems in usage here are already
tricky enough.)
> What if `a.example` and `b.example` both use the same index? Or what if two `saveImpression` calls from `a.example` use different indices? What if the conversion site comes along and indicates a histogram size smaller than what the impression sites expected? Do impressions whose indices are too large just get invalidated and discarded?
As noted above, what an index means needs to be consistent only within
the scope of impressions that are selected for attribution with a
specific conversion. So a.example and b.example can use the same
indices, if that is what they want to do. If they are saving
impressions for different advertisers, there is no need to coordinate
indices. If the goal is to measure something other than the site in
the histogram (because you want to know which creative performs best,
for instance), then they will definitely want to use the same index,
even if the impressions are expected to be included in the same
histogram. And there is always the `matchValue` that could be used to
have the impressions fit into different histograms.
All of this is just a toolkit, to be used any way the sites choose, so
there is no clear set of rules to follow. The explanation in this
document is intended to help people start to understand how to use the
API. More advanced uses are very much possible.
> I'm concerned about cases where impression sites disagreeing on histogram layout could undetectably corrupt aggregate output.
Yes, this is probably the most obvious way in which the API could end
up being not useful. Multi-party coordination is a hard operational
problem.
The way we have designed the intermediary usage of the API is intended
to mitigate against some of that. An intermediary (read: ad tech) can
avoid this problem by doing all of the work itself. The impression
saving and conversion measurement, when run by the same entity, is far
less error prone in the way you identify. Our expectation is that
this will be the way that the API is first used.
The other way to manage this is to have the source of ads provide all
the necessary `saveImpression()` values. That switches things back to
a single point of control, reducing the likelihood that an error
occurs. Of course, this assumes a simpler deployment architecture
than is typically part of the ad buy and distribution architectures
out there, so even this is a non-trivial undertaking.
It's important to recognize that we don't want to dismiss the
operational challenges in getting this API to work well. But there is
only so much a specification can do with hand-holding.
> What if the `saveImpression` and `measureConversion` API calls included a byte string identifying a particular histogram layout? Off the top of my head, it could be the digest of the bucket labels, ordered by histogram index. You could then also fold that histogram layout hash into the DAP task configuration, so that some site using stale JavaScript for whatever reason doesn't contaminate aggregations.
That's not a bad idea. This is, to a very rough approximation, what
the `matchValue` field can achieve. We discussed having that be a
byte string for basically that reason. But we concluded that an
integer is easier to reason about for basic uses. It loses some of
the accidental misuse properties that you get from a checksum, but it
takes less effort to propagate through various ad tech systems.
See https://github.com/w3c/attribution/issues/58, which we still have
open for consideration in a future iteration of the API design. There
are links to several related discussions.
> ## Saving impressions
>
> 3.4. Saving Impressions [4] discusses the `options.priority` parameter, which is set by the impression site and later used to allocate credit at conversion measurement time (4.4.2.1). Aren't impression sites incentivized to exaggerate their priority to get more credit?
>
> This goes to a bigger question: what's the security model here? Are the impression and conversion sites adversaries, or is there an assumption of control by a common actor?
We had some text about that already under the Ad Fraud section
(https://w3c.github.io/attribution/#security-ad-fraud). But I realize
that this was a bit lightweight.
https://github.com/w3c/attribution/pull/438 expands on that a little more.
> It makes intuitive sense that impression sites may wish to constrain which sites can measure conversions via `options.{conversionSites, conversionCallers}`. But since the impression and conversion can be something like a week apart, does this pose an operational problem in deployments? What if the set of legitimate conversion sites has changed since the impression occurred?
Yes, this goes back to the discussion above regarding the need for
coordination. Not a lot to be done to improve things.
> ## Measuring conversions
>
> 2. Overview of Operation [11] says:
>
> > If the query found no impressions, or the privacy budget for the site is exhausted, a histogram consisting entirely of zeros (0) is constructed.
>
> We've done this in other DAP deployments, and it worked as far as hiding the fact that some event hadn't occurred. But the extra workload from summing zeroes was not insignificant, so much so that it wound up being a good tradeoff to deploy Oblivious HTTP to conceal the absence of an event instead of synthesizing empty contributions. That wouldn't work here, because you're trying to hide the absence of an event from the local JavaScript rather than some remote server. Anyway -- do you have a sense of what the proportion of zero to nonzero reports will be? i.e. how much work will be wasted? If the contributions are overwhelmingly zeroes, then it might be worth doing something about that.
The overall volume corresponds directly to the number of conversions.
So the expectation is that the cost is something that a site can
directly account for. For instance, if the conversion is a sale, then
there is some amount of overhead in each sale to account for the cost
of this attribution. My understanding is that, while the cost might
seem pretty big from your perspective, as a potential operator of the
aggregation service, the overall cost is negligible relative to a sale
or even account sign-up. (Cost for customer acquisition through
advertising varies greatly, but it is orders of magnitude greater than
the cost of attribution.)
> 3.5. Requesting Attribution for a Conversion [12] describes the `measureConversion` API. What happens if a conversion site calls `measureConversion` multiple times --
>
> - if the parameters are exactly the same both times?
> - Do both calls succeed, yielding identical histograms or does the second call fail?
> - Or does the second call yield (unbeknownst to the caller) an all-zeroes histogram because the first call consumed the impressions?
Both should succeed, but the second might have an all-zero histogram.
Not because the impressions got consumed, but because some amount of
privacy budget is expended on both. If the first spent all of the
budget, the second is guaranteed to fail.
> - if the two calls have distinct `options.aggregationService` values?
> - Is it allowed to measure the same conversions in two different aggregation services, e.g. to check that a TEE and an MPC compute the same results?
> - If you do allow that, does each call consume some epsilon?
Yes, both queries would consume from the same budget. Aggregation
services are expected to follow the same basic differential privacy
budget enforcement rules. Of course, we don't have a TEE version
specified today.
We've discussed reliability and concluded that sites could manage
this. See https://github.com/w3c/attribution/issues/60
> ## Document scope
>
> 1.5. Collective Privacy Effect [13] discusses the use of central differential privacy, calling it "the mathematical basis for the privacy design used in this specification". The nuts and bolts of central DP are out of scope of the document, instead being described in the privacy budget report extension report extension [14]. That's fair enough. But does that mean implementations can't or shouldn't do local DP?
Local DP, if implemented without a way of knowing that there is extra
noise, would constitute spoiling of the results. None of the
participants in the process indicated that they thought the
privacy/utility balance of local DP would work. In practice, we end
up with shuffle DP, which is still not as good as the "central" DP we
get from this.
> Are there any collector<->aggregator interactions that need to be described? I suppose those are out of this document's scope, but where do they get written down?
I think that DAP has that covered well enough.
> ## Ticky-tacky gripes and typos
>
> 4.4.2.1. Last-N-Touch Attribution [15]:
>
> > Let sumCredit be the sum of credit’s items.
>
> Does this mean `sumCredit` is not necessarily 1.0f? What are the upper and lower bounds on `sumCredit`?
It only needs to be finite. You can split credit using something that
sums to 1 perfectly [0.3, 0.7] or just make it whatever [1e3, 2e-3].
The algorithm copes nicely and distributes the value proportionally,
no matter the sum.
I've opened https://github.com/w3c/attribution/pull/439
> 1.5. Collective Privacy Effect [13]:
>
> > If attribution is justified, both these factors motivate the enablement of attribution for all users.
>
> To be clear, this means this feature is on by default in user-agents?
That's not something that has been decided. Nor is it something that
the specification can mandate. Each user agent will have to make
their own determination about how the API is enabled and disabled.
> 4.2. State For Privacy Budget Management [17] discusses the boolean attribution budget lock. I'm surprised the spec has to get this specific, and indeed there's a note saying "yeah you might not actually use a boolean". The relevant APIs return `Promise<Whatever>` anyway, so can't you just say "accessing the store must be threadsafe" and let implementations do the right thing? This is probably a W3C/web platform norm I'm unfamiliar with.
Indeed it is. I hate having to do this, but the specifications
require that you have algorithms that clearly describe everything.
(In writing this up, I realized that the implementation might be able
to use a boolean in practice, rather than something like a mutex,
because the web platform does tend to rely on thread confinement for a
lot of things. Still, the note is there for exactly that reason.)
> In various places, the specification requires implementations to "attempt to mitigate side channels". This is good advice. What's explicitly discussed is timing side channels, but I wonder whether this prescription also includes keeping encryption keys or plaintext data out of the address space where impression or conversion site code runs? Perhaps that kind of separation is assumed in the web platform.
Yeah, that's a very basic assumption that tends to hold generically.
> That's it for now. I look forward to further discussion.
Thanks again!
~~Martin
Received on Tuesday, 26 May 2026 01:45:50 UTC