cratefield-module-telemetry
Aggregate usage telemetry for a Cratefield venture: clients report
batches of counted events to POST /v1/telemetry/events, every field
is validated against a closed grammar — an unknown field or an undeclared
value is a rejection, never a truncation — and the counts accumulate into
buckets in the venture's own database. No third-party analytics service,
no vendor SDK, and no field anywhere a sentence could fit.
use Telemetry;
let module = new
.events
.modules
.retention_days
.max_events_per_batch
.opt_out_command
.status_command;
Both vocabularies default to empty, which fails closed: a venture that forgets to declare them runs a collector that rejects every batch naming an event or a module, not one that counts anything it is sent.
Consent ships as a model, not as wiring: consent::decide is the pure
decision over the local opt-out, DO_NOT_TRACK and CI;
consent::notice generates the first-run text from the payload schema, so
it cannot promise less or more than the parser enforces;
consent::status renders the exact bytes a sender would POST; and a
rotation predicate bounds the install id to 30 days. The crate performs
none of the doing — it does not read the environment in production, print
the notice, persist an opt-out switch, provide the fz telemetry off and
fz telemetry status commands, hold a pending batch, or send anything (it
never opens a connection). That half belongs to the client, which must
wire the primitives so reporting is off with no network and off before the
next batch. The install id is 16 random bytes the caller supplies, never
derived from anything about the machine — pseudonymous, with no stored
link to a person.
Routes: POST /v1/telemetry/events (public, unauthenticated — the
caller is a CLI; the guards are the RateLimiter port, the batch ceiling
and the closed grammar), GET /v1/telemetry/notice (the
machine-readable consent notice, unauthenticated like the privacy
manifest), and GET /v1/telemetry/admin/usage (admin-guarded aggregate
rows; the GROUP BY is the privacy control, so no per-install row exists
to leak).
What is never sent: identifiers or addresses, paths, repository or branch names, prompts, model output, commit or issue text — free text of any kind. Rejection bodies never echo what the client sent.
Emits telemetry.recorded after an accepted batch. Both tables are
declared in personal_data() with subject: "install_id" and
Disposition::Erase, so cratefield-module-privacy can erase exactly
one install's rows from the id its own client prints.
See docs/TELEMETRY.md for the payload contract, the consent model and
the client rules this crate implements.
MIT. Built in the open for Cratefield, a Factory Zero venture.