RFC: Proposal for estimating unique Julia clients without tracking

I’ve written up a new protocol that would allow us to estimate how many unique Pkg clients there are for Julia and for each package in a way that doesn’t allow for tracking individual users. This works as a rough proxy for the number of users of Julia and various packages, so it’s of interest for many reasons, including getting funding for packages. It also allows accurately estimating the number of users sliced by OS, Julia version, package versions, regions, etc. — whatever one may care to count, which can help us understand things like the relative importance of different operating systems and versions, and if we’re under-serving certain parts of the world.

I’ve also made a reference implementation of the whole thing, with comprehensive testing to show that it works, and opened a pull request to Pkg, with an implementation of the client-side part of the protocol. The Pkg servers don’t implement the server-side part yet, and until that happens we cannot test end-to-end.

The protocol is fairly simple to implement, but the theory behind why and how it works is involved to require a significant writeup. Here’s what I wrote in the very top of the “Executive Summary”:

I’m proposing a new (as in novel) way to estimate how many unique clients use Julia—and each package, sliced by OS, version, and region—without being able to track anyone. It stacks three layers:

  1. Resource-class sharding
  2. HyperLogLog sampling
  3. RSA encryption

The first two layers provide the privacy: you can’t follow a client across packages, and every client hides in a crowd of lookalikes in most classes. The RSA layer doesn’t change the privacy model, but stops clients from faking their samples to skew the counts. So if you accept the privacy of sharding + HLL and believe two short proofs about the RSA layer, then you should be ok with the whole protocol.

I’d like to implement the client side of this protocol in Julia 1.14, and then use the data the server collects to publish client-count estimates alongside other Pkg server stats.

What I’d live to get is feedback on this idea and for people to review the design and concept and give feedback. For those who can review the proofs in the writeup, I’d very much appreciate checking of the proof. This has all been vetted by a smallish collection of people who hang out no the JuliaLang Slack #triage channel already, and by Claude (Fable, even), and so far there are no soundness issues.

To answer one question that has come up already on PR review: Do other package managers do this? No, they do not; but they also couldn’t since there isn’t any previously existing protocol to estimate user counts without tracking users. This protocol is new in the sense that I created it specifically to do this.

Compared to what others do for telemetry, this is super privacy-complaint! Merci!

Thanks for working on this! Just wanted to link to two prior topics of interest with respect to Julia user population estimates:

In particular, judging from the second, it seems that HyperLogLog sampling is already being used?

Ah yes, this was an early iteration of this same line of thinking, but for estimating counts of unique IP addresses rather than unique clients. Why do we have to estimate the number of unique IPs — don’t we have the actual IP addresses? Well, in order to preserve people’s privacy, we do not store IP addresses. But we wanted to be able to estimate how many unique IP addresses are in any given slice of requests. So, instead of storing IP addresses, we encrypt them using a Feistel cipher that’s the same size as the address (IPv4 or IPv6), and then we throw away all but 28 bits (IIRC), making the mapping irreversible, and use those 28 bits to generate a HyperLogLog sample, which allows estimation. No resource class sharding, and the fancy RSA stuff isn’t necessary since clients aren’t sending the HLL values, so they can’t spoof them.

In the old school Internet, unique IP addresses would have been a good proxy for number of clients. But these days the mapping between clients and IP addresses is so messy due to dynamic IP assignment and NATs that we’ve found IP addresses to be essentially useless for estimating client counts. It’s hard to tell exactly what’s going on since we only have the counts, not the IPs, but it seems like almost everyone gets a new IP address so regularly that they have almost no relation to identity. I would happily get rid of the IP address hashing once we have the client count estimation deployed.

On the RSA-based cryptographic construction:

I don’t feel qualified to give real feedback. LGTM, but that doesn’t mean a lot.

I feel that this would profit from being more connected to the general cryptographic literature. This feels related to oblivious transfer and to oblivious sampling (with the geometric distribution). I think the approach should be generally useful outside of julia, and would deserve some kind of cryptography paper. If one could find a way of doing this over elliptic curves, then this would become much cheaper.

Minor code nitpicks:
The hll_hash_resource_class implementation made me hickup on reading: Concatenating stuff without separators into a hash-function is a big anti-pattern. Your construction is ok because of lots of mitigating circumstances, like client_hash being fixed-sized. Maybe simplify future reviewers jobs by adding an assert on the fixed size of client_hash? Or at least add a source-code comment? Best practice would be just use hmac(; key = client_hash, message = class), that way nobody needs to think about any of the attacks that don’t apply to this situation (and the cost is negligible compared to the RSA operations).

Resource sharding:
I think this would profit a lot if you added some clear-cut examples. Say:

Typical “class” values are "/package/9b87118b-4619-50d2-8e1e-99f35a4d4d9d", which means PackageCompiler.jl. In other words, all your downloads of different versions of PackageCompiler.jl will have the same “identity”, but will be separate from other packages.
This allows us to generate not just statistics about “how many people use PackageCompiler”, but also “How many people who first installed PackageCompiler in November '26 have updated their install in January '27, and have not updated their version Feb-June '27”.

FWIW, I think this is very valid data to collect! It is worthwhile for package owners be able to know “lost 3k users, gained 4k users last quarter”, not just “net gained 1k users”.

Log-Based deanonymization:
Suppose you at the julia package-server wanted to track users in a fine-grained way.

Typical package downloads don’t come in single events. Instead, you download / update a bunch of packages at the same time (e.g.: install package X, implicitly also install its dependencies).

Using timing and IP-address information, it would be trivial for you to correlate these events that are part of the same burst.

Now you don’t just get a single (k,d) = kd_sketch(class) value for this user for this package, you get a bunch of different independent (k,d) values for the same user. These are a long-lived unique fingerprint.

To paraphrase Richelieu, give me 6 (k,d)-sketches for different packages (classes), and I will uniquely identify this user.

This is a general issue / bad interaction between fine-grained resource-sharding and hyperloglog. I don’t know how to fix it. Coarse-graining resource-sharding makes some users more identifiable, but alleviates the “perfect fingerprint” issue.

Ultimately, I think a lot will come down to: Pkg servers have public policies on data retention for logs, and people will need to trust that.

PS. An approach that would fix this is if you could obtain the following property:

If the HLL-token (k,d) would increment the current running maximum, then the server learns the (k,d)-pair. If it would not increment the current running tally, then the server learns nothing.

An example for this style of thing would be Yao's Millionaires' problem - Wikipedia
A simpler problem that is actually used in the real world would be Socialist millionaire problem - Wikipedia (that’s basically how OTR / signal messenger works)

But that makes the cryptographic parts even fancier and more complex and I don’t know how to make that property fly :frowning:

Agree, and I’d like to write up an academic paper and submit it somewhere appropriate. I looked at elliptic curves and a few other settings; the issue is that as far as I could figure out, the ring needs to be over an N that’s basically the same as what we’re using here, so we don’t actually save any space that way.

The hll_hash_resource_class implementation made me hickup on reading: Concatenating stuff without separators into a hash-function is a big anti-pattern. Your construction is ok because of lots of mitigating circumstances, like client_hashbeing fixed-sized. Maybe simplify future reviewers jobs by adding an assert on the fixed size of client_hash? Or at least add a source-code comment?

First of all, thank you for the review — exactly what I’m looking for here.

This is a good point, and if you look at the reference implementation of the same function, it uses \0 as a separator — also not strictly safe since the function allows anything at all to be used as salt and class values, but safe in the way it’s actually used.

The current client version is the result have doing a bit of optimization and noting that the string concatenation in that version was a major source of temporary memory allocation on the hll_token hot path. It is safe precisely as you note beacuse the client_hash is fixed size. I’ll add both an assert and a comment.

This allows us to generate not just statistics about “how many people use PackageCompiler”, but also “How many people who first installed PackageCompiler in November '26 have updated their install in January '27, and have not updated their version Feb-June '27”.

FWIW, I think this is very valid data to collect! It is worthwhile for package owners be able to know “lost 3k users, gained 4k users last quarter”, not just “net gained 1k users”.

Sure, this is definitely useful and it’s very cool that we can get that data with this protocol. Worth putting into the writeup.

This is true, but I think the HLL hash we’re getting here is really the least of it—if someone has access to the request logs without any sort of hash, they can already probably identify a user based on things that are pretty much impossible to hide without effectively just deleting the logs. I think the better solution to that might be some other form of dataset anonymization.

There’s also the question of whether we’re really obligated to worry about this. We are not publishing raw logs, and people necessarily leave their footprints all over the place wherever they go on the Internet. We are already going out of our way to protect privacy, meanwhile every company everywhere else is extracting everything they can about everyone and using it for dubious purposes. Do we really need to go further here? If there’s a dataset anonymization technique we can use to address this, I’m willing to consider it, but it just feels a bit absurd that this open source project’s server logs be held to such a higher standard than literally everyone else.

This would be doable if we the client could compute a max in the k component, but I’m not sure that’s possible, and I suspect it’s not—max is usually not a thing you can compute in an additive group. There’s also not a single current running maximum: the whole point of the technique is that you can slice and dice the requests in different ways and get unique count estimates for that particular slice.

Then also add a comment that Length extension attack - Wikipedia are not an issue because of the general context. Everyone who reads crypto_hash(secret || message) will immediately pattern-match to both the fixed-size/separator issue, and “bad MAC construction, use hmac, length extension attacks will kill you”, and will waste time figuring out whether length-extension attacks are a problem.

The hash → HMAC is really cheap, and imo best practice is to save time on the discussion – just use an hmac, don’t hand out gadgets to attackers, even if we all believe that this gadget is safe for attackers to have.

If the current hashing stdlib can’t do hmac without additional allocations, then it probably should offer hmac directly: Otherwise people (like you here) will be tempted into doing other constructions, and somebody less careful than you will get blown up.

I agree! Just don’t overpromise on the privacy guarantee to users.

The privacy we actually get is imo good enough. But between reading your proposal and coming up with the hard fingerprinting via correlated requests, I was left with the impression of stricter privacy promises, and was somewhat disappointed after thinking it through.

Well, secure multiparty computation says that this is possible. Typically impractical though, unless you figure out a clever shortcut (like your RSA-based construction).

PS, a little gem for fun reading: phrack 68-14: Secure Function Evaluation vs. Deniability in OTR and similar protocols

PPS. A reasonable data-retention promise to make would be:
After 30 days, exact logs are purged (at least: IP address replaced by very rough geographic information, exact headers reduced to OS/arch + julia version (in case of nightly / custom builds for either julia or packages, coarsen to 1.14.3-DEV or something like that), and access times are coarsened to the week.

Since very long-time tracking is very scary, maybe decay the fingerprints / hll-tokens? E.g. each julia install gets 1% per week chance of forgetting / regenerating its fingerprints. (with appropriate handing of julia not running for a week… so if you don’t boot an old laptop for 2 years and then start it, then its pkg-update will be a new identity with probability ~ 1-e).

I think it should be possible to adjust statistics for such a known decay rate? Someone better at stats than me needs to do that calculation. That would alleviate a lot of privacy concerns.

This is because combinations of fingerprints / (k,d) pairs are very unique: Even entirely discarding the d part, 3 fingerprints have 36 bits, which is enough to uniquely identify the user. Typical usage sessions first request the registry, then one plus one dependency. If the server decided to maliciously correlate these today, they get a unique fingerprint; and if the server again decides to maliciously correlate them 5 years in the future, they get to recognize this specific old user.

(why only purge after some grace period? Because you want exact logs for incident response in case of attacks. Why purge at all? A promise “we won’t do that now, nor within 30 days, and will then delete all relevent data” requires us to trust the people running the server from now until 30 days in the future; a promise “we won’t do that now, nor ever, but we will keep the logs indefinitely” requires us to extend the trust into the indefinite future – e.g. that the people who get control of the logs after some hypothetical bankruptcy proceeding will honor the promise made by the current people. Yeah, lol nope.)

Honestly, I’m not entirely sure why we don’t care about this aside from the fact that there’s not really anything useful that can be accomplished by the client constructing a hash collision. The hashing is to protect the client, not the server.

Unfortunately, AFAIK we do not have an HMAC readily available to Base code. Yes, I could add an HMAC implementation somewhere, but this whole business is already involved enough and I’d rather not get into that. The good news is that if one does become available, we can switch to it with no worries: how a client hashes its secret and resource class to an exponent is inherently client-specific and does not need to be stable — if we change it, we’re essentially just flushing and regenerating all the client secrets, which is fine, although we’ll want to remember that we did that (there will be an false spike in unique clients around that point in time).

The simplest fix would be adding a \0 terminator to the class, since the class string comes from a URL resource, which cannot, by construction, contain \0. Another possibility would be reversing the order and including the client hash after the resource class, which I believe also prevents length extension attacks.

Aside from the coarsening, this is already what happens. We don’t keep IP addresses except for a hash that’s not useful besides for estimating uniques, and we don’t send nightly/custom builds in the first place, only 1.14.3-DEV. We could consider coarsening timestamps as well.

I think this may be overly complicated. The simpler thing to do would be to include the UTC calendar year in the hash function so clients get new HLL values in all categories every year. I’m not sure we really need to estimate unique clients over a longer horizon than a year.

Destroying long-term history is a good way of alleviating that thing. That costs the julia project all this long-term history. Not my call to make guesses about the business value of long-term usage history.

A hard cutoff loses the capability to detect facts like “there is a big yearly cohort of users of FooBaz.jl who start in september, and stop using FooBaz.jl next february, probably students using FooBaz.jl for coursework in the fall semester”.

For the implementation, consider adding the best-by date to the secret, and regenerating it if it’s too old. If somebody leaks their client-secret, then their differential privacy will be impacted. Discarding and regenerating the client secrets will cure that after the year is over; and it cures that in a way that laypeople can understand. However, there is a consideration about: What do we want, in terms of statistics, if I freeze a docker image or VM in 2026, and start 10 instances in 2027? Should these 10 instances be the same user in '27, or 10 different users?

Resource-sharding wants different resources to have independent keys. A length-extension attack breaks this: If the client leaks sha256(client_secret, codeunits(some_class)), then they have also leaked the per-class keys for all other classes that are sha256-length-extensions of this class (there is annoying padding stuff to deal with).

One mitigating circumstance is: The way you generate the classes makes it somewhat unlikely that some of them are length-extensions of the others, and these are not really under attacker control (they aren’t, right? or can people come up with novelty uuids?). Another mitigating circumstance is: Clients are not supposed to leak their keys.

A very strong mitigating circumstance is: You’re discarding half the size, i.e. your effective key is actually truncate128(sha256(client_secret, codeunits(resource_class))). There is no good way for clients to leak the other half of the sha256-hash. And the function msg -> truncate128(sha256(secret, msg)) is not vulnerable to length-extension attacks. (it isn’t, right? maybe quickly google for a source to cite…)

…but ask yourself: Would it be easier to write down a water-tight explanation why length-extension attacks are not an issue here, or to just use / code hmac?

In other words: Using naked sha256 leaves you with a resource-sharding implementation that is correct for complicated reasons; better to have an implementation that is correct for simple reasons.

Alternatively, use sha3 or sha2_512_256. These ones are not vulnerable to length-extension attacks. (we have learned since the early 2000s. Today nobody would ever certify a secure hash that allows length-extension attacks)

As you noted, this resets history of julia package usage statistics. Doing that impacts business (“oh no, please don’t reset statistics right now, can’t you wait until Q4, we need the numbers for our grant application”, “oh no, I’m writing my bachelor’s thesis on a comparative study on the uptake speed for security patches in various open-source ecosystems, could you wait 4 more weeks?”).

You don’t want to impact business for fixing something that is probably not an issue (I mean we’re pretty sure that this is not an issue, right?).

Such things have a tendency to ossify.

Just on this note, I wonder if Keccak might actually be quite a neat fit for several parts of this construction, rather than just avoiding the SHA-2 length-extension issue. A small Keccak sponge implementation could provide:

  • KMAC for the keyed resource-class derivation;
  • SHAKE for hll_hash_into_ring, instead of manually extending SHA-512 with hash(prefix || counter);
  • clean domain separation / structured hashing (TupleHash) for the various distinct hash roles.

I will confess that I don’t have a full understanding of the proposal here (haven’t sat down and thought about the whole); this might just be rose-tinted glasses after gaining an appreciation for the algorithmic elegance of Keccak/SHA3 in the course of implementing KangarooTwelve.jl :sweat_smile:

Reading through this thread, the discussion seems completely buried in technical discussions about cryptographic primitives debating SHA-2 length-extension attacks, salt rotations, Keccak, and KMAC. I am a chemist, not a cryptographer, so I will not pretend to have the slightest idea what half of those words even mean or will in any way try to comment on it because I truly can not speak about this.

But frankly, the cryptography is completely irrelevant to the core issue. My objection is about governance and consent.

Even if the math perfectly anonymizes the payload, this proposal still fundamentally alters Pkg to default to enlisting user hardware as a telemetry node, and buries that fact in a changelog. When someone installs Julia in the future, they will never ever see that changelog. The chance of a regular user digging into random backend documentation to figure out how to disable telemetry is close to 0%. Does anyone right after installing Julia decide to read the complete documentation of Pkg.jl? Because I surely did not and nobody does that.

I do not want to be forced to trust that a corporate entity (JuliaHub) won’t further alter the deal for this server-side implementation or its logging protocols in the future, nor do I want to generate business intelligence for them. The recent change to the ModelingToolkit/SciML AGPL license has already severely damaged my trust in the independence of this ecosystem. Pushing opt-out telemetry further deepens that distrust.

If you want this data, ask for it. Make it a transparent, opt-in choice at installation. Math does not override the basic requirement for user consent.

Furthermore, even with consent, I do not believe this should be merged. It introduces an unnecessary network and cryptographic attack surface to core FOSS tooling, exposing it to malicious actors and future corporate overreach, all for very little actual gain to the average user, and it has not even been peer-reviewed by a reputable peer-reviewed cryptographic journal.

As I understand it, this is exactly the adversarial scenario the proposed cryptographic protocol is designed to prevent, so user privacy should not degrade regardless of how JuliaHub behaves or how its logging changes over time. If that holds, why would consent be needed at all, given that GDPR is already satisfied for the small amount of additional metadata attached to the server requests?

I wonder what “independence of this ecosystem” would even mean. As someone contributing a lot to open source Julia code (the ecosystem so to speak), I could not do it to the extent I do if I wasn’t being paid for it. So yes, obviously my employer will have a business incentive for me writing certain kinds of code and I’m therefore not independent. It’s the same for many or most of the core contributors.

The MTK split was clearly motivated as necessary to continue development at all, given economic realities for JuliaHub. For me it doesn’t mean a breach of trust, it’s more like, if I want the thing (free open source software) to continue and the thing is being funded by commercial interests, then I want the commercial interests being able to continue, too. It’s annoying for people who had bet on being able to use continually developed code with a more permissive license, but not more than that.

Similarly here, if a small bit of metadata that is not attributable to me personally (if I trust the cryptographic reasoning) increases the chance for Julia software projects to get funding, because they can show that they have users, gain users, etc., I find that highly desirable. Because I know how difficult it is to get funding and to keep continuing open source projects.

I appreciate the response, but your argument fundamentally is conflating cryptographic anonymity with user autonomy.

If we go ahead and assume the protocol is flawless (which is a big ask since it is not published in a peer-reviewed journal and people here have brought up problems with it already) and the server cannot deanonymize the payload (again a big ask), that completely misses my point of why consent is actually required.

The package manager I am running on my machine is using my compute cycles on my utility bill to generate metrics for a third party. “The data is anonymous and is not going to hurt you directly” does not lead to “Therefore, we have the right to take it without asking you”.

The argument that this protocol is actually adversarial against JuliaHub ignores the fact that JuliaHub and its owners control large parts of the ecosystem. If a user does not trust JuliaHub, which I don’t, they therefore have zero reason to trust that entity to not quietly expand the client-side telemetry later. This is what I meant by my Star Wars reference of altering the deal. Cryptographic guarantees today do not protect against a governance overreach tomorrow (a similar playbook used by JuliaHub against MTK before).

And I am sorry, but saying that GDPR is already satisfied and that we do not need to ask for user consent is a baffling stance to take in open source, in my eyes. GDPR is the absolute bare legal minimum requirement to avoid being fined by the EU. I mean, idk I like my package manager not to be fined by the EU, but again, it is a bare minimum that you have to fulfill. Open source has historically operated on values of user agency, transparency, and privacy. If truly the only thing we can say about our ecosystem is that we manage to be privacy-preserving enough to be compliant with GDPR, then I am sorry, but then in my eyes, we have fundamentally failed as a FOSS community.

I will not pretend that I understand this protocol because I genuinely do not. I cannot talk to you about its technical feasibility, which is why I have to basically trust JuliaHub to have implemented this correctly, which I just very much do not. We also open ourselves up to a huge attack surface because if this thing does not work, then that is pretty bad. And we open ourselves up to this risk for what? Better info JuliaHub can use for their Series C funding round? Slightly better funding proposals for NumFocus grants? And if this protocol fails, what is the worst-case scenario?

I think there are two easy solutions to this:

1. Do not merge this ever. Again, this opens up an attack surface and requires me to trust JuliaHub with this implementation, which I do not. Even if you trusted JuliaHub, it would mean trusting even more things from a package manager than we already have to. In my eyes, the less we have to trust package managers, the better. (Pretty much every single package manager that there is has recently been hammered with a seemingly endless wave of supply chain attacks)

  1. Make this opt-in after getting it published in a reputable journal (This is, in my eyes, the absolute bare minimum if this should ever get merged)

Making it opt-out, I want to reiterate, does not mean people will opt out of this because nobody reads the documentation of Pkg.jl directly after installing Julia. Literally nobody. If this is such a great protocol, then it should be no problem to convince people to use it and to allow them to make the choice for themselves.

I understand the economic realities of FOSS, and sure I want these projects to secure funding too. I want more FOSS in the world. But economic necessity does not entitle a corporation to harvest hardware telemetry without asking, and if the data is truly vital for grants, making it a transparent advertised opt-in choice that will allow the community to willingly support it.

What are the worse case scenario ? Someone can get info like “company or lab X uses packages A,B,C ?” and “some user could fake/inflate package user counts” ?

As far as I understand it, because of the resource class sharding, you could say actor X uses package A if you see a rare hyper-log-log value again over time, or if the class has very few members overall (making all the values rather rare). But you could not create a pattern of activity across resources because they do not all report the same hyper-log-log values for a client, each one is different.

I feel like you could do more if you used the IP connection logs, but this is already a risk now for the “company may do something bad with existing infra later” scenario so I’m not sure this metadata here adds that much.

Okay, but the privacy concern most people have with collecting telemetry data is that it can be linked to you, as a person. Exposing what you are working on, what you are interested in etc. The way I understand is that the proposal mitigates all of this, so it amounts to collecting population statistics. Which is already happening everywhere, all the time, without your consent. It does not expose anything about you personally.

This I disagree with. Several people in this thread will tell you that it is incredibly hard to fund FOSS, and so telemetry data to show what people are actually using in the field / industry is incredibly important to secure funding. This is also mentioned in the OP:

@Antipath1 made some specific comments on the PR and I responded there, so I’ll just repost that here rather than write another post with roughly the same content.


@Antipath1: Knowing the exact size, geographic distribution, OS breakdown, packages and version usage of the Julia user base is incredibly valuable business intelligence for JuliaHub. Pretending this massive infrastructure shift is about better funding proposals for packages is incredibly transparent, deeply frustrating and insulting to our intelligence.

Several points here:

  • This infrastructure isn’t owned or operated by JuliaHub.
  • We (the JuliaLang project) already have rough geographic distribution, OS breakdown, packages and version usage data by request count, just not by number of unique clients.
  • This information has zero business value to JuliaHub aside from the fact that being able to show that Julia is popular overall is useful to reassure people that a Julia-focused business is legitimate.

Frankly, these days since Julia is regularly in the top 20 on various language indices and our rough estimates of the number of Julia users from other sources have climbed into the millions, showing overall Julia usage is just not that much of an issue. To the extent that this helps demonstrate Julia popularity, this provides the same value to JuliaHub as it does to any company or organization betting on Julia. What is much harder is showing the popularity of individual Julia projects, and many of them have really struggled with this, which is what this aims to solve.

Also: how do we know that there are millions of Julia users? Well, the Julia VS Code extension has over a million users, which we know thanks to user-level telemetry collected by Microsoft, which somehow no one seems particularly upset about. That is the same company that also knows every time you clone a GitHub repo. Will they tell the Julia community that info? Nope.

Or maybe other FOSS package managers haven’t tried to implement this not because they were waiting for a stroke of cryptographic genius, but because they actually want users to trust their package managers not to phone home and respect them enough to not turn their open source package managers into corporate spyware.

Python published raw PyPI request logs with IP addresses unmasked for decades until GDPR made it illegal to do so. Go’s package servers are literally hosted and run by Google and they collect the level of telemetry you’d expect based on that (they don’t actually tell you what they collect). Our stance before and after this is significantly more privacy-respecting than most package managers. And again, this is the Julia project; JuliaHub has nothing to do with it; this doesn’t benefit JuliaHub specifically in any way.

This should not be buried in a changelog. If this massive shift in the ecosystem’s trust model is ever forced through (which it absolutely should not ever be), it needs to be at the very top of the list in every single Julia forum.

A fundamental change to telemetry requires dedicated, standalone announcements on Discourse, Slack, and Zulip and not be hidden as a small bullet point in a file full of Pkg.jl updates. At a bare minimum, this should print a big red warning in the REPL so users actually know they are being enlisted as telemetry nodes. At the minimum.

It’s not like this changelog is the only place it’s mentioned — I’m not exactly trying to keep this a secret. I’ve posted about it on Slack and Discourse. I’m even giving a JuliaCon talk about it. Is there somewhere else you think I should post it to?

Shipping this as an opt-out default is a massive violation of user trust. I am completely opposed to this ever being merged, but pushing it through without cryptographic peer review from actual, credentialed cryptographers is completely reckless. (Vetting a novel cryptographic protocol via a Discourse thread, a Slack channel and the LLM Claude is not a security audit).

I’m definitely planning on submitting it for peer review. That takes a long time, unfortunately, and one of the major issues with deploying something in Julia clients is that the lead time on that is long and unlike servers, we cannot simply deploy new code and have it take effect immediately. If it’s what the community seems to prefer, we can ship this in clients without turning the server-side on until it’s peer reviewed. If it passes peer review, then we can turn on the server side and instantly be getting client count estimates from all Julia versions since 1.14. If we wait until peer review happens and it’s published and then implement it in clients, we won’t be getting any data for about five years. (Note that people can easily verify that severs are not doing the protocol by checking if the https://pkg.julialang.org/hll_rsa.toml endpoint actually exists or not; or snooping on their own actual Pkg traffic with something like WireShark, to see if the headers are being sent.)

A word about peer review in general. Yes, it’s a good idea and it might catch something, but having three typically somewhat disinterested reviewers peruse a paper seems to me significantly less likely to catch issues than having a largish community of interested and highly mathematically minded people scrutinize it. So sure, peer review is a thing we should do, but it’s not some magical process that is guaranteed to catch problems. Research papers with massive issues get published all the time even in respectable journals.

Regarding opt-out vs opt-in: if it’s opt-in, it has zero value and there’s no point in doing it. Why? Because approximately nobody changes defaults for anything. Some tiny percentage of users will opt-in. If we knew what percentage opted in, then yes, we could estimate from there. But we don’t know what the percentage is, so if this were opt-in it would be totally useless.

This proposal is frankly a massive overreach. As a chemistry student whose research relies heavily on this language, I have invested immense trust into this ecosystem, and I absolutely refuse to blindly trust that JuliaHub won’t use my machine as a data-harvesting node. I do not care how mathematically sound the client-side cryptography is, because you cannot fix a massive institutional trust deficit using math. I massively distrust JuliaHub’s motivations here, and forcing this into the ecosystem, especially as an opt-out, is a terrible idea.

But even with maximum visibility, I fundamentally oppose merging this at all ever.

I hear you loud and clear and respect that position and appreciate you expressing it here. If this turns out to be the viewpoint of even a significant minority of the community, we will not ship this.

Regarding “I absolutely refuse to blindly trust that JuliaHub won’t use my machine as a data-harvesting node”: as I’ve said above, this isn’t being done by JuliaHub and doesn’t particularly benefit JuliaHub; there’s also no blind trust involved — this is open source and you can see exactly what the code on your system is doing. Calling this “data-harvesting node” seems like a pretty significant overstatement, given that this is explicitly and carefully designed to respect and preserve individual privacy as much as possible (more even—literally more than would previously have been possible while getting this info).

Meanwhile, do you use any closed source software? Google? If not, respect, but I bet you do. You use GitHub at least. You have no idea what that software is doing and it is actually turning all your devices into data harvesting nodes. But the recipients of that data are companies, and they don’t tell you what they’re collecting or how, let alone show you the code and explain what they’re doing, or allow it to benefit open source projects and communities.

To offset a bit all the FUD being spread out of fear and ignorance. I wholeheartedly support this, and we’re lucky that we have a community that brings such proposals out in the open to be discussed and even takes so much care to respect user privacy and security to such extents.

I unfortunately don’t have the expertise to give meaningful comments on the implementation, but I understand enough to know that the process being implemented here goes over and beyond what is being done in the industry at large. Should it be the norm? Yes. Is it? No. And I am glad that we’re one of the few that do it and properly care about it.