Privacy, telemetry & the community pulse
Participation should be private by default and deliberate. The reviewed server code now enforces a minimal usage-event contract for new writes. This is not a claim that the change is deployed or that historical data was cleaned.
If you opt into browser peer-to-peer sharing, peers can see your IP address. See P2P distribution for its controls and limitations.
What new telemetry writes retain
POST /telemetry accepts at most 4 KiB of strict flat EDN or JSON, with exactly
three string fields: kind, attr, system. Kind must be in_use, installed
or transferred; attribute and system must be an exact pair in the public
package registry. The server stores those canonical values and its own timestamp
and day. It does not retain submitted raw EDN/JSON (the legacy edn column gets
constant empty text), region (NULL), identifiers or nonces. Unknown, duplicate,
nested, missing and malformed fields are rejected, not silently persisted.
X-Region, X-Real-IP and X-Forwarded-For are ignored by this ingest. This is
not evidence that connection IPs are absent from edge/network logs. Application
request-parameter and query-bind logging is disabled on this write path; database
and proxy logging configuration still needs review before deployment.
An anonymous global database quota admits at most 100 well-formed attempts per minute across API replicas, including unknown package pairs. There is no per-client identifier: one source can consume the quota or submit repeated events. This bounds writes, not Sybil attacks, people, machines or deduplicated daily use. Malformed requests are body-bounded but do not consume database quota; network request floods need separately managed ingress limits.
Invalid input returns 400, oversized bodies 413, global quota exhaustion 429,
and actual database failures 503. Only a committed event receives 200. Legacy
build herd_signal and raw-body internal writers fail closed; verified build
evidence stays separate from usage events.
Historical raw EDN and caller-supplied regions may remain. No deletion, historical sanitization, retention job or production deployment is included. A separate 30-day retention proposal covers events, historical PII, backups and logs.
The solnix.privacy.enable master switch
The intended client contract below is private by default. The server review
did not verify the client implementation or delivery behavior; these settings
are not evidence that the current ingest is privacy-safe. Check your client
implementation before relying on them. In this contract, true means
fully private — no phone-home:
Note the inversion — this reads backwards.
solnix.privacy.enable = true(the default) means NO data is sent. To join the community pulse you set it tofalse. Settingenable = falseturns telemetry on, not off. We know this is a footgun; a positively-namedsolnix.telemetry.enable(defaultfalse) is planned to replace it.
solnix.privacy.enable = true; # DEFAULT: fully private, phone-home OFF
A machine whose config never touches this stays completely private. Nothing is sent, nothing is shared.
To opt into the community pulse, set it to false. The individual features then
default on, and you can pare any of them back:
solnix.privacy.enable = false; # opt into the community pulse
# with privacy off, these default true — override any individually:
solnix.telemetry.enable = true; # group: all phone-home
solnix.telemetry.census.enable = true; # platform / active-user census
solnix.telemetry.packages.enable = true; # transferred / installed / in-use
solnix.cache.seed.enable = true; # P2P: seed built packages (BitTorrent)
solnix.site.p2p.enable = true; # P2P: browser WebRTC site sharing
The effective rule, in plain language: a feature is active only when the
master switch is off and its group is on and the feature itself is on —
(not privacy.enable) && telemetry.enable && <feature>.enable. The master
switch always wins. Someone who never touches config is fully private; someone
who opts in gets everything, then can turn off any piece.
See Peer-to-peer distribution for cache.seed and site.p2p.
Public aggregates and proposed safeguards
The metrics changes expose 30-day reported usage-event counts, not numbers of people or machines. Public count fields below five events are suppressed. Five events can all come from one source: this is not k-anonymity of five machines. Ranking still uses the existing weighted score, so count suppression is not a guarantee against inference from rank or inclusion.
Pulse reports rollup refresh time when known, not upstream inventory freshness. Unique-user totals are unmeasured. Small or missing regional buckets do not prove zero usage. Regional buckets can reflect historical, untrusted data only; new writes supply no region. Public metrics views expose aggregates, not raw bodies.
Edge-derived coarse regions, daily deduplication, and machine-based anonymity were design proposals, not implemented guarantees. No rotating nonce or new identifier is introduced by these fixes. Retention limits and any removal of previously retained raw bodies require separate approval.
Both EDN and JSON are supported, posted to
https://search.solnix.io/telemetry, for example:
{:kind "in_use" :attr "hello" :system "x86_64-linux"}
The example pair must actually exist in the public registry. No client timestamp, region, census, nonce, personal field or store path is accepted.
Why report usage
Reported events can help identify packages receiving use or installation reports. They cannot establish community size or growth in distinct users. Participation should be deliberate and private by default; enforceable ingest safeguards must come before an unconditional privacy promise.
Where to see the results
- Community pulse: reported package events, regional event aggregates and rollup refresh time, not an active-machine census.
- Package search: popularity combines reported usage events and dependency information; it is not a count of users.