pub struct Telemetry { /* private fields */ }Expand description
Aggregate usage telemetry with a consent model built in (issue #413).
Compose it for the vocabularies a venture wants counted; everything else — routes, grammar, storage, retention — is fixed by the module.
Implementations§
Source§impl Telemetry
impl Telemetry
Sourcepub fn new() -> Self
pub fn new() -> Self
Empty vocabularies (fail closed: every batch naming an event or a
module is rejected until the venture declares them), the 64-event
batch ceiling, 180-day retention, and the fz telemetry commands.
Sourcepub fn events(self, events: impl IntoIterator<Item = impl Into<String>>) -> Self
pub fn events(self, events: impl IntoIterator<Item = impl Into<String>>) -> Self
Declares the whole event vocabulary. Exactly these names may be counted; anything else is a rejection, never a new series (issue #413). Replaces any previous list.
Sourcepub fn modules(
self,
modules: impl IntoIterator<Item = impl Into<String>>,
) -> Self
pub fn modules( self, modules: impl IntoIterator<Item = impl Into<String>>, ) -> Self
Declares the whole composed-module vocabulary a client may report. Replaces any previous list.
Sourcepub fn retention_days(self, days: u32) -> Self
pub fn retention_days(self, days: u32) -> Self
How long buckets live before the scheduled purge deletes them
(default 180). Clamped to 1..=[MAX_RETENTION_DAYS]: a builder
cannot return an error, and zero would delete every bucket the day
it is written while a value past the ceiling would walk the purge’s
date arithmetic off the calendar — so an impossible value is
narrowed to the nearest workable one rather than honoured (issue
#413).
Sourcepub fn max_events_per_batch(self, max: u32) -> Self
pub fn max_events_per_batch(self, max: u32) -> Self
The largest batch the collector parses (default 64). Clamped to
1..=[payload::MAX_EVENTS_PER_BATCH]: “at most this many” is part
of the closed grammar, so a venture may tighten it and never widen
it, and zero — which the clamp also removes — would leave a
collector that rejects every batch with a message about exceeding a
ceiling of nothing (issue #413).
Sourcepub fn opt_out_command(self, command: impl Into<String>) -> Self
pub fn opt_out_command(self, command: impl Into<String>) -> Self
The command a client prints as the one-line way to switch reporting
off (default fz telemetry off).
Sourcepub fn status_command(self, command: impl Into<String>) -> Self
pub fn status_command(self, command: impl Into<String>) -> Self
The command a client prints as the way to see exactly what would be
sent (default fz telemetry status).
Trait Implementations§
Source§impl Module for Telemetry
impl Module for Telemetry
Source§fn requires(&self) -> &'static [Port]
fn requires(&self) -> &'static [Port]
The database is where the aggregates live; there is no mode of this module without it.
Source§fn optional(&self) -> &'static [Port]
fn optional(&self) -> &'static [Port]
The Clock port decides “now” — the day buckets and the retention
cutoff — and is what makes time deterministic under test. The
RateLimiter port is the abuse control the public ingest route
actually has.
Source§fn personal_data(&self) -> &'static [PersonalDataSet]
fn personal_data(&self) -> &'static [PersonalDataSet]
Two aggregate tables, both keyed on the install id, both erased on request (issue #413).
The tension, and the decided answer. The payload carries no
identifier the venture can tie to a person, so the harness cannot
look a subject up from anything else it stores — and a reader might
conclude these tables hold nobody. They do not hold a person, but
they do hold a key: the install id is pseudonymous, with no
stored link to a person, and the person’s own client prints it in
telemetry status. So an erasure request that supplies the id
deletes exactly their rows — the same person can act on their own
data without the venture ever learning who they are. That makes the
subject the install id and the disposition plain Erase;
declaring none here would be the lie that breaks the first
erasure request that names a real id.
The honest consequence, written down rather than buried. The id
rotates every consent::ROTATION_DAYS days and nothing anywhere
stores the link between one id and the next — that is the point of
rotating it — so rows under an id that has already rotated away are
not reachable from a later one. Rotation bounds what an erasure can
reach, and that is the price of not keeping a link between
rotations; the retention purge bounds how long the unreachable tail
outlives the request.
Source§fn emits(&self) -> &'static [&'static str]
fn emits(&self) -> &'static [&'static str]
The event a composed venture can subscribe to: an accepted batch.
Source§fn public_writes(&self) -> bool
fn public_writes(&self) -> bool
The ingest route is public by design and still not an open write
path: it accepts one thing only — a batch whose every value already
passed a closed grammar, capped by the batch ceiling and the
RateLimiter port — and it is how a CLI reports, with no browser and
no session (issue #413, after module-linkedin’s reasoning). It stays
a false here because public_writes feeds the captcha gate in
route_policy.rs, and a captcha on a machine-posted batch is not
satisfiable: the honest controls are the ones the route actually
has, and they are all mechanical.
Source§fn scheduled<'a>(
&'a self,
ctx: &'a ModuleContext,
cron: &'a str,
) -> BoxFuture<'a, Result<(), AnyError>>
fn scheduled<'a>( &'a self, ctx: &'a ModuleContext, cron: &'a str, ) -> BoxFuture<'a, Result<(), AnyError>>
The retention purge: buckets in either table whose day is older than the window are deleted, mirroring the waitlist’s scheduled purge. Idempotent, so running it more often than the cron fires is harmless.
Source§fn tables(&self) -> &'static [&'static str]
fn tables(&self) -> &'static [&'static str]
Source§fn migrations(&self) -> Migrations
fn migrations(&self) -> Migrations
Source§fn validate_config(&self, cfg: &dyn Config) -> Result<(), ConfigError>
fn validate_config(&self, cfg: &dyn Config) -> Result<(), ConfigError>
Source§fn router(&self, ctx: ModuleContext) -> Router
fn router(&self, ctx: ModuleContext) -> Router
/v1/<name>.Source§fn harness_api(&self) -> u32
fn harness_api(&self) -> u32
Harness::build.Source§fn depends_on(&self) -> &'static [&'static str]
fn depends_on(&self) -> &'static [&'static str]
Source§fn public_write_policy(&self) -> RoutePolicy
fn public_write_policy(&self) -> RoutePolicy
RoutePolicy on
(issue #143). Read moreSource§fn signature_verification(&self) -> SignatureVerification
fn signature_verification(&self) -> SignatureVerification
RoutePolicy::Signature
deliveries (issue #533): how a webhook handler checks a machine
signature before acting. Read moreSource§fn max_body_bytes(&self, _cfg: &dyn Config) -> usize
fn max_body_bytes(&self, _cfg: &dyn Config) -> usize
Source§fn streaming_routes(&self) -> &'static [StreamRoute]
fn streaming_routes(&self) -> &'static [StreamRoute]
StreamRoute::max_bytes ceiling. Opt-in: the default
is empty, and every existing route keeps the buffered behaviour it
always had. Read moreSource§fn self_check(&self) -> Vec<String>
fn self_check(&self) -> Vec<String>
Source§fn well_known(&self) -> Option<Router>
fn well_known(&self) -> Option<Router>
/.well-known, for
spec-mandated discovery documents (OIDC openid-configuration,
jwks.json) that must live outside /v1 (issue #46). Paths are
relative to the prefix: register /jwks.json, not
/.well-known/jwks.json. Read moreSource§fn surface(&self) -> Surface
fn surface(&self) -> Surface
Action::input::<Body>()), so the
declaration cannot drift from the route. Harness::build
validates it; GET /__surface serves the composition. Default:
nothing, and a module that declares nothing renders nothing.