Forge analytics module — first-party web analytics (events, visitor identity, rollups). Wraps observability-analytics.
| Tier | application |
| Role | unclassified (baselined) |
| Path | crates/application/analytics |
| Edition | 2024 |
| Targets | application_analytics, disclosure_schema_live |
| Public items | 118 across 18 modules |
| Tests | 108 |
What it is for
application-analytics — first-party web analytics. Wraps observability-analytics.
B-009 Gate-0.5 finding: observability-analytics (crates/observability/analytics) ships no migrations/ directory. PgAnalyticsStore (storage/postgres.rs) is a query/write layer only — its own doc comment states the consumer must already have analytics_events, analytics_conversions, and analytics_daily_rollups — and those tables are in fact owned and created by application-engine's central platform migration set (crates/application/engine/migrations/ 020_analytics.sql, 062_analytics.sql, plus several later ALTERs), not by this library crate. There is no schema here for migrations() to vendor; staying empty is correct. permission_codes() was already declared before this pass.
Capabilities
crate root
application-analytics — first-party web analytics. Wraps observability-analytics.
| Item |
|---|
pub const DISCLOSURE_MIGRATIONS: & (& str, & str) |
AnalyticsModule
application-analytics — first-party web analytics. Wraps observability-analytics.
| Item |
|---|
pub struct AnalyticsModule |
AnalyticsModule :: fn new() -> Self |
AnalyticsModule :: fn name(& self) -> & 'static str |
AnalyticsModule :: fn version(& self) -> & 'static str |
AnalyticsModule :: fn migrations(& self) -> Vec <MigrationSet> |
AnalyticsModule :: async fn migrate_pending(& self, pool : & PgPool) -> std::result::Result <u32, MigrationError> |
AnalyticsModule :: fn permission_codes(& self) -> Vec <String> |
api::analytics_admin (other)
Analytics query admin REST router — Sprint 28 (Track 4).
| Item |
|---|
fn router <S>() -> Router <S> where S : Clone + Send + Sync + 'static, PgPool : FromRef <S>, AnalyticsState : FromRef <S>, |
api::disclosure_ingest (other)
POST /lab/beacon — the disclosure-lab ingest route (Sprint 3.8, T6).
| Item |
|---|
pub const BEACON_PATH: & str |
DisclosureLab
POST /lab/beacon — the disclosure-lab ingest route (Sprint 3.8, T6).
| Item |
|---|
pub struct DisclosureLab |
DisclosureLab :: fn new(ingest_key : IngestKey, settings : & ProbeSettings, probe_config_hash : impl Into <String>, retention : LabRetention, sink : Arc <dyn DisclosureSink>,) -> Self |
DisclosureLabState
POST /lab/beacon — the disclosure-lab ingest route (Sprint 3.8, T6).
| Item |
|---|
pub struct DisclosureLabState |
DisclosureLabState :: fn new(lab : DisclosureLab) -> Self |
async fn ingest_beacon(State(state) : State <DisclosureLabState>, ConnectInfo(peer) : ConnectInfo <SocketAddr>, req : Request,) -> Response |
fn router <S>() -> Router <S> where S : Clone + Send + Sync + 'static, DisclosureLabState : FromRef <S>, |
api::probe_run_ingest (other)
POST /lab/probe-run — the probe battery's beacon (sprint 4.55 task 4).
| Item |
|---|
pub const PROBE_RUN_PATH: & str |
pub const PAGE_TOKEN_HEADER: & str |
PageTokens
POST /lab/probe-run — the probe battery's beacon (sprint 4.55 task 4).
| Item |
|---|
pub trait PageTokens |
ProbeRunIngest
POST /lab/probe-run — the probe battery's beacon (sprint 4.55 task 4).
| Item |
|---|
pub struct ProbeRunIngest |
ProbeRunIngestState
POST /lab/probe-run — the probe battery's beacon (sprint 4.55 task 4).
| Item |
|---|
pub struct ProbeRunIngestState |
ProbeRunIngestState :: fn new(ingest : ProbeRunIngest) -> Self |
async fn ingest_probe_run(State(state) : State <ProbeRunIngestState>, ConnectInfo(peer) : ConnectInfo <SocketAddr>, req : Request,) -> Response |
fn router <S>() -> Router <S> where S : Clone + Send + Sync + 'static, ProbeRunIngestState : FromRef <S>, |
middleware::visitor_id (other)
First-party visitor-identity cookie — Sprint 22 (T266, US-117).
| Item |
|---|
pub const COOKIE_NAME: & str |
async fn visitor_id_layer(secure : bool, req : Request <Body>, next : Next) -> Response |
fn cookie_value <'a>(header : & 'a str, name : & str) -> Option <& 'a str> |
services (other)
Analytics service — first-party event collection + query facade.
| Item |
|---|
fn store(state : & AnalyticsState) -> Option <Arc <observability_analytics::PgAnalyticsStore>> |
fn enabled(state : & AnalyticsState) -> bool |
AnalyticsConfig
Analytics service — first-party event collection + query facade.
| Item |
|---|
pub struct AnalyticsConfig |
AnalyticsConfig :: fn default_excludes() -> Vec <String> |
AnalyticsConfig :: fn default() -> Self |
IdentityLane
Analytics service — first-party event collection + query facade.
| Item |
|---|
pub enum IdentityLane |
EventCollector
Event collector — the unbounded channel + supervised flush worker.
| Item |
|---|
pub struct EventCollector |
EventCollector :: fn start(pool : PgPool, cfg : AnalyticsConfig) -> Self |
EventCollector :: fn start_with_store(store : Arc <dyn AnalyticsStore>, cfg : AnalyticsConfig) -> Self |
EventCollector :: fn submit(& self, event : AnalyticsEvent) -> bool |
EventCollector :: async fn shutdown(& self) |
services::disclosure_lab::auth (other)
Ingest credential handling for the disclosure lab (Sprint 3.8, T6).
| Item |
|---|
pub const INGEST_KEY_HEADER: & str |
pub const MIN_INGEST_KEY_LEN: usize |
pub const CREDENTIAL_QUERY_PARAMS: & & str |
fn constant_time_eq(a : & u8, b : & u8) -> bool |
fn query_carries_credential(query : Option <& str>) -> bool |
IngestKey
Ingest credential handling for the disclosure lab (Sprint 3.8, T6).
| Item |
|---|
pub struct IngestKey |
IngestKey :: fn fmt(& self, f : & mut fmt::Formatter <'_>) -> fmt::Result |
IngestKey :: fn new(raw : & str) -> Result <Self, IngestKeyError> |
IngestKey :: fn matches(& self, presented : & u8) -> bool |
IngestKeyError
Ingest credential handling for the disclosure lab (Sprint 3.8, T6).
| Item |
|---|
pub enum IngestKeyError |
services::disclosure_lab::limits (other)
Input limits for the beacon ingest route (Sprint 3.8, T6).
| Item |
|---|
pub const MAX_BODY_BYTES: usize |
pub const MAX_JSON_DEPTH: usize |
pub const MAX_BATCH_ITEMS: usize |
pub const MAX_DETAIL_LEN: usize |
pub const REQUIRED_CONTENT_TYPE: & str |
fn json_depth(value : & serde_json::Value) -> usize |
LimitViolation
Input limits for the beacon ingest route (Sprint 3.8, T6).
| Item |
|---|
pub enum LimitViolation |
fn check_content_type(value : Option <& str>) -> Result <(), LimitViolation> |
fn check_content_encoding(value : Option <& str>) -> Result <(), LimitViolation> |
fn check_declared_length(value : Option <& str>) -> Result <(), LimitViolation> |
fn check_body_len(len : usize) -> Result <(), LimitViolation> |
fn check_depth(value : & serde_json::Value) -> Result <(), LimitViolation> |
fn check_item_count(found : usize) -> Result <(), LimitViolation> |
services::disclosure_lab::payload (other)
Beacon payload shape, probe gating, and the retention ingest gate
| Item |
|---|
fn safe_probe_key(raw : & str) -> String |
fn gate(beacon : & Beacon, probes : & EnabledProbes,) -> Result <BTreeMap <String, BeaconItem>, GateViolation> |
pub const NO_READING_DETAIL: & str |
fn probe_group_for(key : & str) -> & 'static str |
fn plan(retention : LabRetention, beacon : & Beacon, accepted : & BTreeMap <String, BeaconItem>, probes : & EnabledProbes, probe_config_hash : & str, raw_payload : serde_json::Value,) -> IngestOutcome |
Beacon
Beacon payload shape, probe gating, and the retention ingest gate
| Item |
|---|
pub struct Beacon |
BeaconItem
Beacon payload shape, probe gating, and the retention ingest gate
| Item |
|---|
pub struct BeaconItem |
ClientProbeStatus
Beacon payload shape, probe gating, and the retention ingest gate
| Item |
|---|
pub enum ClientProbeStatus |
EnabledProbes
Beacon payload shape, probe gating, and the retention ingest gate
| Item |
|---|
pub struct EnabledProbes |
EnabledProbes :: fn from_settings(settings : & ProbeSettings) -> Self |
EnabledProbes :: fn state(& self, key : & str) -> ProbeKeyState |
EnabledProbes :: fn keys(& self) -> impl Iterator <Item = & str> |
EnabledProbes :: fn len(& self) -> usize |
EnabledProbes :: fn is_empty(& self) -> bool |
GateViolation
Beacon payload shape, probe gating, and the retention ingest gate
| Item |
|---|
pub enum GateViolation |
IngestOutcome
Beacon payload shape, probe gating, and the retention ingest gate
| Item |
|---|
pub enum IngestOutcome |
PersistPlan
Beacon payload shape, probe gating, and the retention ingest gate
| Item |
|---|
pub struct PersistPlan |
ProbeKeyState
Beacon payload shape, probe gating, and the retention ingest gate
| Item |
|---|
pub enum ProbeKeyState |
ProbeResultRow
Beacon payload shape, probe gating, and the retention ingest gate
| Item |
|---|
pub struct ProbeResultRow |
RawWindow
Beacon payload shape, probe gating, and the retention ingest gate
| Item |
|---|
pub enum RawWindow |
StoredProbeStatus
Beacon payload shape, probe gating, and the retention ingest gate
| Item |
|---|
pub enum StoredProbeStatus |
StoredProbeStatus :: fn as_str(self) -> & 'static str |
StoredProbeStatus :: fn from(v : ClientProbeStatus) -> Self |
services::disclosure_lab::probe_run (other)
The probe-run beacon: one driver's run of the probe page, validated into a
| Item |
|---|
pub const HEADERS_NEVER_STORED: & & str |
fn validate(beacon : & ProbeRunBeacon, max_events : usize) -> Result <(), ProbeRunViolation> |
fn storable_headers <'a>(headers : impl Iterator <Item =(& 'a str, & 'a str)>,) -> BTreeMap <String, String> |
CanaryWire
The probe-run beacon: one driver's run of the probe page, validated into a
| Item |
|---|
pub struct CanaryWire |
EventWire
The probe-run beacon: one driver's run of the probe page, validated into a
| Item |
|---|
pub struct EventWire |
ProbeRunBeacon
The probe-run beacon: one driver's run of the probe page, validated into a
| Item |
|---|
pub struct ProbeRunBeacon |
ProbeRunPlan
The probe-run beacon: one driver's run of the probe page, validated into a
| Item |
|---|
pub struct ProbeRunPlan |
ProbeRunViolation
The probe-run beacon: one driver's run of the probe page, validated into a
| Item |
|---|
pub enum ProbeRunViolation |
PgProbeRunSink
Where a validated probe run goes: the three probe tables, one transaction
| Item |
|---|
pub struct PgProbeRunSink |
PgProbeRunSink :: fn new(pool : PgPool) -> Self |
PgProbeRunSink :: async fn persist_run(& self, plan : & ProbeRunPlan) -> Result <Uuid, SinkError> |
ProbeRunSink
Where a validated probe run goes: the three probe tables, one transaction
| Item |
|---|
pub trait ProbeRunSink |
services::disclosure_lab::rate_limit (other)
Pre-parse rate limiting for the beacon ingest route (Sprint 3.8, T6).
| Item |
|---|
pub const DEFAULT_MAX_PER_WINDOW: u32 |
pub const DEFAULT_WINDOW_SECS: i64 |
pub const DEFAULT_MAX_TRACKED_PEERS: usize |
IngestRateLimiter
Pre-parse rate limiting for the beacon ingest route (Sprint 3.8, T6).
| Item |
|---|
pub struct IngestRateLimiter |
IngestRateLimiter :: fn new(max_per_window : u32, window_secs : i64, max_tracked_peers : usize) -> Self |
IngestRateLimiter :: fn for_lab() -> Self |
IngestRateLimiter :: fn check(& self, peer : IpAddr) -> RateDecision |
IngestRateLimiter :: fn check_at(& self, peer : IpAddr, now : DateTime <Utc>) -> RateDecision |
IngestRateLimiter :: fn tracked_peers(& self) -> usize |
IngestRateLimiter :: fn default() -> Self |
RateDecision
Pre-parse rate limiting for the beacon ingest route (Sprint 3.8, T6).
| Item |
|---|
pub enum RateDecision |
RateDecision :: fn is_allowed(self) -> bool |
RateDenial
Pre-parse rate limiting for the beacon ingest route (Sprint 3.8, T6).
| Item |
|---|
pub enum RateDenial |
services::disclosure_lab::sink (other)
Persistence seam for validated disclosure-lab beacons (Sprint 3.8, T6).
| Item |
|---|
pub const ABSOLUTE_GRACE_MINUTES: i32 |
DisclosureSink
Persistence seam for validated disclosure-lab beacons (Sprint 3.8, T6).
| Item |
|---|
pub trait DisclosureSink |
PgDisclosureSink
Persistence seam for validated disclosure-lab beacons (Sprint 3.8, T6).
| Item |
|---|
pub struct PgDisclosureSink |
PgDisclosureSink :: fn new(pool : PgPool) -> Self |
PgDisclosureSink :: async fn persist(& self, plan : & PersistPlan) -> Result <Uuid, SinkError> |
SinkError
Persistence seam for validated disclosure-lab beacons (Sprint 3.8, T6).
| Item |
|---|
pub enum SinkError |
fn window_minutes(window : RawWindow) -> Result <i32, SinkError> |
services::middleware (other)
Axum middleware that captures every HTTP request as an
| Item |
|---|
async fn analytics_middleware(State(analytics) : State <AnalyticsState>, ConnectInfo(peer) : ConnectInfo <SocketAddr>, req : axum::extract::Request, next : Next,) -> Response |
AnalyticsState
Analytics state bit (Sprint 65: extracted from rust-cms-engine).
| Item |
|---|
pub struct AnalyticsState |
How to use it
No examples/ target and no doctest in this crate's rustdoc. The tests listed under Verification are the closest executable usage.
Module structure
application_analytics
apiapi::analytics_adminapi::disclosure_ingestapi::probe_run_ingestmiddlewaremiddleware::visitor_idservicesservices::collectorservices::disclosure_labservices::disclosure_lab::authservices::disclosure_lab::limitsservices::disclosure_lab::payloadservices::disclosure_lab::probe_runservices::disclosure_lab::probe_run_sinkservices::disclosure_lab::rate_limitservices::disclosure_lab::sinkservices::middlewarestate
flowchart TD n_application_analytics["application_analytics"] n_application_analytics --> n_api["api"] n_api --> n_api__analytics_admin["analytics_admin"] n_api --> n_api__disclosure_ingest["disclosure_ingest"] n_api --> n_api__probe_run_ingest["probe_run_ingest"] n_application_analytics --> n_middleware["middleware"] n_middleware --> n_middleware__visitor_id["visitor_id"] n_application_analytics --> n_services["services"] n_services --> n_services__collector["collector"] n_services --> n_services__disclosure_lab["disclosure_lab"] n_services__disclosure_lab --> n_services__disclosure_lab__auth["auth"] n_services__disclosure_lab --> n_services__disclosure_lab__limits["limits"] n_services__disclosure_lab --> n_services__disclosure_lab__payload["payload"] n_services__disclosure_lab --> n_services__disclosure_lab__probe_run["probe_run"] n_services__disclosure_lab --> n_services__disclosure_lab__probe_run_sink["probe_run_sink"] n_services__disclosure_lab --> n_services__disclosure_lab__rate_limit["rate_limit"] n_services__disclosure_lab --> n_services__disclosure_lab__sink["sink"] n_services --> n_services__middleware["middleware"] n_application_analytics --> n_state["state"]
Public surface
`crate root`
| Item | What it is |
|---|---|
pub const DISCLOSURE_MIGRATIONS: & (& str, & str) | The disclosure lab's migrations, in apply order, embedded at compile time. |
pub struct AnalyticsModule | — |
AnalyticsModule :: fn new() -> Self | — |
AnalyticsModule :: fn name(& self) -> & 'static str | — |
AnalyticsModule :: fn version(& self) -> & 'static str | — |
AnalyticsModule :: fn migrations(& self) -> Vec <MigrationSet> | The analytics tables themselves stay engine-owned (B-009, above) |
AnalyticsModule :: async fn migrate_pending(& self, pool : & PgPool) -> std::result::Result <u32, MigrationError> | — |
AnalyticsModule :: fn permission_codes(& self) -> Vec <String> | — |
`api::analytics_admin`
| Item | What it is |
|---|---|
fn router <S>() -> Router <S> where S : Clone + Send + Sync + 'static, PgPool : FromRef <S>, AnalyticsState : FromRef <S>, | — |
`api::disclosure_ingest`
| Item | What it is |
|---|---|
pub const BEACON_PATH: & str | Path the beacon is POSTed to, relative to wherever the router is mounted. |
pub struct DisclosureLab | Everything the ingest route needs, assembled once at boot. |
DisclosureLab :: fn new(ingest_key : IngestKey, settings : & ProbeSettings, probe_config_hash : impl Into <String>, retention : LabRetention, sink : Arc <dyn DisclosureSink>,) -> Self | Assemble the route's state from the active configuration. |
pub struct DisclosureLabState | Clonable handle a consumer composes into its own application state. |
DisclosureLabState :: fn new(lab : DisclosureLab) -> Self | Wrap an assembled DisclosureLab. |
async fn ingest_beacon(State(state) : State <DisclosureLabState>, ConnectInfo(peer) : ConnectInfo <SocketAddr>, req : Request,) -> Response | POST /lab/beacon |
fn router <S>() -> Router <S> where S : Clone + Send + Sync + 'static, DisclosureLabState : FromRef <S>, | Router carrying BEACON_PATH |
`api::probe_run_ingest`
| Item | What it is |
|---|---|
pub const PROBE_RUN_PATH: & str | Path the probe-run beacon is POSTed to. |
pub const PAGE_TOKEN_HEADER: & str | Header carrying a page token minted by the host when it served the page. |
pub trait PageTokens | A second credential: single-use page tokens |
pub struct ProbeRunIngest | Everything the route needs, assembled once at boot. |
pub struct ProbeRunIngestState | Clonable handle a host composes into its state. |
ProbeRunIngestState :: fn new(ingest : ProbeRunIngest) -> Self | — |
async fn ingest_probe_run(State(state) : State <ProbeRunIngestState>, ConnectInfo(peer) : ConnectInfo <SocketAddr>, req : Request,) -> Response | The handler |
fn router <S>() -> Router <S> where S : Clone + Send + Sync + 'static, ProbeRunIngestState : FromRef <S>, | Router carrying PROBE_RUN_PATH |
`middleware::visitor_id`
| Item | What it is |
|---|---|
pub const COOKIE_NAME: & str | The cookie name — short, first-party, analytics-only. |
async fn visitor_id_layer(secure : bool, req : Request <Body>, next : Next) -> Response | Set hp_vid on public-page responses that lack a valid one |
fn cookie_value <'a>(header : & 'a str, name : & str) -> Option <& 'a str> | Extract a named cookie's value from a Cookie header string. |
`services`
| Item | What it is |
|---|---|
pub enum IdentityLane | Identity-resolution lane (Gate 1.5 R1 D-28.3) |
pub struct AnalyticsConfig | Consumer-supplied analytics configuration |
AnalyticsConfig :: fn default_excludes() -> Vec <String> | The default exclude list — these paths are dropped at the middleware before reaching the channel |
AnalyticsConfig :: fn default() -> Self | — |
fn store(state : & AnalyticsState) -> Option <Arc <observability_analytics::PgAnalyticsStore>> | Look up the consumer's PgAnalyticsStore when analytics is enabled |
fn enabled(state : & AnalyticsState) -> bool | Whether analytics is enabled for this consumer instance. |
`services::collector`
| Item | What it is |
|---|---|
pub struct EventCollector | Owns the analytics ingest channel + the supervised worker that drains it |
EventCollector :: fn start(pool : PgPool, cfg : AnalyticsConfig) -> Self | Spawn the collector |
EventCollector :: fn start_with_store(store : Arc <dyn AnalyticsStore>, cfg : AnalyticsConfig) -> Self | Spawn the collector with a caller-provided store |
EventCollector :: fn submit(& self, event : AnalyticsEvent) -> bool | Push an event onto the channel |
EventCollector :: async fn shutdown(& self) | Signal the worker to drain remaining events and exit |
`services::disclosure_lab::auth`
| Item | What it is |
|---|---|
pub const INGEST_KEY_HEADER: & str | Header the ingest credential is presented in |
pub const MIN_INGEST_KEY_LEN: usize | Shortest credential the lab will accept |
pub const CREDENTIAL_QUERY_PARAMS: & & str | Query-parameter names that would carry a credential |
pub enum IngestKeyError | Why a configured ingest key was refused |
pub struct IngestKey | A validated ingest credential |
IngestKey :: fn fmt(& self, f : & mut fmt::Formatter <'_>) -> fmt::Result | Redacted |
IngestKey :: fn new(raw : & str) -> Result <Self, IngestKeyError> | Validate and wrap a configured key |
IngestKey :: fn matches(& self, presented : & u8) -> bool | Whether a presented credential matches, in constant time. |
fn constant_time_eq(a : & u8, b : & u8) -> bool | Compare two byte strings without an early exit on the first differing byte |
fn query_carries_credential(query : Option <& str>) -> bool | Whether a URL query string carries something shaped like a credential |
`services::disclosure_lab::limits`
| Item | What it is |
|---|---|
pub const MAX_BODY_BYTES: usize | Largest beacon body accepted, in bytes. |
pub const MAX_JSON_DEPTH: usize | Deepest JSON nesting accepted, counting a scalar as depth 1. |
pub const MAX_BATCH_ITEMS: usize | Most probe readings accepted in one beacon batch. |
pub const MAX_DETAIL_LEN: usize | Longest detail string accepted on a probe reading |
pub const REQUIRED_CONTENT_TYPE: & str | The only media type the ingest route accepts. |
pub enum LimitViolation | An input limit that was exceeded, or a required header that was wrong |
fn check_content_type(value : Option <& str>) -> Result <(), LimitViolation> | Require Content-Type: application/json, ignoring parameters |
fn check_content_encoding(value : Option <& str>) -> Result <(), LimitViolation> | Refuse any content coding other than absent or identity |
fn check_declared_length(value : Option <& str>) -> Result <(), LimitViolation> | Reject on the declared length, before any body is read |
fn check_body_len(len : usize) -> Result <(), LimitViolation> | Reject on the actual number of bytes received |
fn json_depth(value : & serde_json::Value) -> usize | Nesting depth of a decoded document: a scalar or empty container is 1, and a non-empty container is one more than its deepest child |
fn check_depth(value : & serde_json::Value) -> Result <(), LimitViolation> | Reject a document nested deeper than MAX_JSON_DEPTH |
fn check_item_count(found : usize) -> Result <(), LimitViolation> | Reject a batch larger than MAX_BATCH_ITEMS |
`services::disclosure_lab::payload`
| Item | What it is |
|---|---|
pub enum ClientProbeStatus | Status a client is permitted to report for a probe |
pub enum StoredProbeStatus | Status as stored, mirroring the migration's dl_probe_status_ck alphabet. |
StoredProbeStatus :: fn as_str(self) -> & 'static str | The literal the database column takes. |
StoredProbeStatus :: fn from(v : ClientProbeStatus) -> Self | — |
pub struct BeaconItem | One probe reading as sent by the probe page. |
pub struct Beacon | One beacon POST |
pub enum ProbeKeyState | Whether a probe key is known to the active config, and if so whether it is on. |
pub struct EnabledProbes | The active config, projected to the only question the ingest route asks of it: is this key on? Built by serialising ProbeSettings rather than by a hand-written match |
EnabledProbes :: fn from_settings(settings : & ProbeSettings) -> Self | Project the active ProbeSettings |
EnabledProbes :: fn state(& self, key : & str) -> ProbeKeyState | Classify one probe key. |
EnabledProbes :: fn keys(& self) -> impl Iterator <Item = & str> | Every probe key in the active config, enabled or not |
EnabledProbes :: fn len(& self) -> usize | How many probes the config knows about. |
EnabledProbes :: fn is_empty(& self) -> bool | Whether the config knows about no probes at all — the fail-closed state, in which every beacon is refused. |
pub enum GateViolation | A beacon that the server refuses |
fn safe_probe_key(raw : & str) -> String | Render a probe key safe to put in a response body or a log line |
fn gate(beacon : & Beacon, probes : & EnabledProbes,) -> Result <BTreeMap <String, BeaconItem>, GateViolation> | Server-side re-check of a decoded beacon against the active config |
pub struct ProbeResultRow | One row destined for disclosure_lab_probe_results. |
pub const NO_READING_DETAIL: & str | Detail recorded when an enabled probe sent no reading at all |
pub enum RawWindow | How long the raw payload is kept, as the plan expresses it. |
pub struct PersistPlan | Everything the sink needs, with nothing left to decide. |
pub enum IngestOutcome | What the ingest route should do with a validated beacon. |
fn probe_group_for(key : & str) -> & 'static str | Group letter for a probe key, per §10's battery. |
fn plan(retention : LabRetention, beacon : & Beacon, accepted : & BTreeMap <String, BeaconItem>, probes : & EnabledProbes, probe_config_hash : & str, raw_payload : serde_json::Value,) -> IngestOutcome | Decide what to persist for a gated beacon |
`services::disclosure_lab::probe_run`
| Item | What it is |
|---|---|
pub struct EventWire | One DOM event as the page recorded it |
pub struct CanaryWire | — |
pub struct ProbeRunBeacon | The beacon body. |
pub enum ProbeRunViolation | Why a beacon is refused before any row is written. |
pub const HEADERS_NEVER_STORED: & & str | Request headers the sink may store |
pub struct ProbeRunPlan | Everything the sink needs, with nothing left to decide. |
fn validate(beacon : & ProbeRunBeacon, max_events : usize) -> Result <(), ProbeRunViolation> | Validate a decoded beacon against the lab's caps |
fn storable_headers <'a>(headers : impl Iterator <Item =(& 'a str, & 'a str)>,) -> BTreeMap <String, String> | Keep only the headers the sink may store. |
`services::disclosure_lab::probe_run_sink`
| Item | What it is |
|---|---|
pub trait ProbeRunSink | A destination for a probe run. |
pub struct PgProbeRunSink | The Postgres sink over the lab's own database. |
PgProbeRunSink :: fn new(pool : PgPool) -> Self | — |
PgProbeRunSink :: async fn persist_run(& self, plan : & ProbeRunPlan) -> Result <Uuid, SinkError> | — |
`services::disclosure_lab::rate_limit`
| Item | What it is |
|---|---|
pub const DEFAULT_MAX_PER_WINDOW: u32 | Requests per window, per peer, in the lab's default posture. |
pub const DEFAULT_WINDOW_SECS: i64 | Window length in seconds, in the lab's default posture. |
pub const DEFAULT_MAX_TRACKED_PEERS: usize | Most distinct peers tracked at once, in the lab's default posture. |
pub enum RateDenial | Why a request was refused by the limiter. |
pub enum RateDecision | The limiter's answer for one request. |
RateDecision :: fn is_allowed(self) -> bool | Whether the request may proceed. |
pub struct IngestRateLimiter | Fixed-window per-peer limiter |
IngestRateLimiter :: fn new(max_per_window : u32, window_secs : i64, max_tracked_peers : usize) -> Self | Build a limiter |
IngestRateLimiter :: fn for_lab() -> Self | The lab's default posture: DEFAULT_MAX_PER_WINDOW per DEFAULT_WINDOW_SECS, tracking DEFAULT_MAX_TRACKED_PEERS peers. |
IngestRateLimiter :: fn check(& self, peer : IpAddr) -> RateDecision | Decide, using the wall clock. |
IngestRateLimiter :: fn check_at(& self, peer : IpAddr, now : DateTime <Utc>) -> RateDecision | Decide, at an explicit instant |
IngestRateLimiter :: fn tracked_peers(& self) -> usize | How many peers are currently tracked |
IngestRateLimiter :: fn default() -> Self | — |
`services::disclosure_lab::sink`
| Item | What it is |
|---|---|
pub const ABSOLUTE_GRACE_MINUTES: i32 | Grace between raw_expires_at and the deadline nothing extends |
pub enum SinkError | Why a validated plan could not be stored |
pub trait DisclosureSink | Where a validated beacon goes. |
fn window_minutes(window : RawWindow) -> Result <i32, SinkError> | Project a plan's retention window onto the schema's raw_window_minutes column |
pub struct PgDisclosureSink | Postgres implementation over migration 122. |
PgDisclosureSink :: fn new(pool : PgPool) -> Self | Wrap a pool. |
PgDisclosureSink :: async fn persist(& self, plan : & PersistPlan) -> Result <Uuid, SinkError> | — |
`services::middleware`
| Item | What it is |
|---|---|
async fn analytics_middleware(State(analytics) : State <AnalyticsState>, ConnectInfo(peer) : ConnectInfo <SocketAddr>, req : axum::extract::Request, next : Next,) -> Response | Self-scoped analytics capture middleware |
`state`
| Item | What it is |
|---|---|
pub struct AnalyticsState | The analytics-specific slice of consumer state |
Re-exports. Exported here, defined elsewhere.
| Export | Defined in |
|---|---|
AnalyticsState | state::AnalyticsState |
EventCollector | collector::EventCollector |
analytics_middleware | middleware::analytics_middleware |
disclosure_lab | services::disclosure_lab |
{AnalyticsConfig,EventCollector,analytics_middleware} | services::{AnalyticsConfig,EventCollector,analytics_middleware} |
{BEACON_PATHasDISCLOSURE_BEACON_PATH,DisclosureLab,DisclosureLabState,routerasdisclosure_ingest_router,} | api::disclosure_ingest::{BEACON_PATHasDISCLOSURE_BEACON_PATH,DisclosureLab,DisclosureLabState,routerasdisclosure_ingest_router,} |
{Beacon,BeaconItem,ClientProbeStatus,EnabledProbes,GateViolation,IngestOutcome,PersistPlan,ProbeKeyState,ProbeResultRow,RawWindow,StoredProbeStatus,gate,plan,} | payload::{Beacon,BeaconItem,ClientProbeStatus,EnabledProbes,GateViolation,IngestOutcome,PersistPlan,ProbeKeyState,ProbeResultRow,RawWindow,StoredProbeStatus,gate,plan,} |
{DisclosureSink,PgDisclosureSink,SinkError} | sink::{DisclosureSink,PgDisclosureSink,SinkError} |
{INGEST_KEY_HEADER,IngestKey,IngestKeyError,constant_time_eq,query_carries_credential,} | auth::{INGEST_KEY_HEADER,IngestKey,IngestKeyError,constant_time_eq,query_carries_credential,} |
{IngestRateLimiter,RateDecision,RateDenial} | rate_limit::{IngestRateLimiter,RateDecision,RateDenial} |
{LimitViolation,MAX_BATCH_ITEMS,MAX_BODY_BYTES,MAX_DETAIL_LEN,MAX_JSON_DEPTH} | limits::{LimitViolation,MAX_BATCH_ITEMS,MAX_BODY_BYTES,MAX_DETAIL_LEN,MAX_JSON_DEPTH} |
{PAGE_TOKEN_HEADER,PROBE_RUN_PATH,PageTokens,ProbeRunIngest,ProbeRunIngestState,routerasprobe_run_ingest_router,} | api::probe_run_ingest::{PAGE_TOKEN_HEADER,PROBE_RUN_PATH,PageTokens,ProbeRunIngest,ProbeRunIngestState,routerasprobe_run_ingest_router,} |
Boundary
Reaches into observability, platform.
Shares tier application with 120 other crates: application-agreements, application-ai, application-approvals, application-assessments, application-audit-log, application-auth, application-billing, application-browser-automation, … (120 total).
_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._
Where it sits
| Tier (ontology) | application |
| Architectural role (taxonomy) | unclassified (baselined) |
| Location | crates/application/analytics |
| Vocabulary in force (lexicon) | current |
Tier flow. Which tiers this crate's own edges cross.
flowchart LR n_application["application"] --> n_observability["observability"] n_application["application"] --> n_platform["platform"]
Dependencies
Runtime, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `application-core` | application | no | always |
| `application-rbac` | application | no | always |
| `observability-analytics` | observability | no | always |
| `platform-dto` | platform | no | always |
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
async-trait | ^0.1 | — | no | always |
axum | ^0.7 | multipart | no | always |
chrono | ^0.4 | serde | no | always |
futures-util | ^0.3 | — | no | always |
serde | ^1 | derive | no | always |
serde_json | ^1 | — | no | always |
sqlx | ^0.8 | runtime-tokio, postgres, chrono, uuid, json | no | always |
thiserror | ^2 | — | no | always |
tokio | ^1 | full | no | always |
tower-sessions | ^0.13 | — | no | always |
tracing | ^0.1 | — | no | always |
uuid | ^1 | v4, v7, serde, js | no | always |
Development, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
tower | ^0.5 | — | no | always |
Build. None.
Depended on by. 1 workspace crate.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR n_platform_disclosure_lab["platform-disclosure-lab"] -->|uses| SELF SELF["application-analytics"] SELF -->|runtime| n_application_core["application-core"] SELF -->|runtime| n_application_rbac["application-rbac"] SELF -->|runtime| n_observability_analytics["observability-analytics"] SELF -->|runtime| n_platform_dto["platform-dto"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
No Cargo features are defined: every capability is unconditional, so no consumer can receive a half-wired crate.
Targets
| Kind | Name | Source |
|---|---|---|
| lib | application_analytics | `src/lib.rs` |
| test | disclosure_schema_live | `tests/disclosure_schema_live.rs` |
Error model
| Error type | Named by |
|---|---|
IngestKeyError | declared, no public signature returns it |
SinkError | window_minutes |
Operational characteristics
| Property | Evidence |
|---|---|
| async public surface | yes |
| async runtime | yes |
| database access | yes |
| network I/O | yes |
| unsafe code | none detected |
| environment variables | yes |
No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.
Configuration
| Variable | Read in |
|---|---|
CARGO_PKG_VERSION | src/lib.rs |
Related capabilities
1 workspace crate depends on this one: platform-disclosure-lab.
Verification
| Kind | Count |
|---|---|
| Unit tests | 104 |
| Integration tests | 4 |
| Examples | 0 |
| Doctests | 0 |
Evidence by module. How often each public module is named by something executable.
| Module | Tests | Examples | Consumers |
|---|---|---|---|
crate root | 2 | 0 | 1 |
api::analytics_admin | 1 | 0 | 1 |
api::disclosure_ingest | 5 | 0 | 1 |
api::probe_run_ingest | 7 | 0 | 6 |
middleware::visitor_id | 3 | 0 | 0 |
services | 4 | 0 | 1 |
services::collector | 1 | 0 | 0 |
services::disclosure_lab::auth | 7 | 0 | 1 |
services::disclosure_lab::limits | 13 | 0 | 0 |
services::disclosure_lab::payload | 16 | 0 | 1 |
services::disclosure_lab::probe_run | 8 | 0 | 0 |
services::disclosure_lab::probe_run_sink | 2 | 0 | 2 |
services::disclosure_lab::rate_limit | 6 | 0 | 1 |
services::disclosure_lab::sink | 5 | 0 | 0 |
services::middleware | 1 | 0 | 0 |
state | 1 | 0 | 0 |
What the tests establish, by name:
observations_refuse_update_and_delete—tests/disclosure_schema_live.rsthe_module_creates_the_lab_tables_and_a_second_run_applies_nothing—tests/disclosure_schema_live.rsthe_pg_sink_writes_run_events_and_canaries_in_one_transaction—tests/disclosure_schema_live.rsthe_vocabulary_is_enforced_by_the_schema—tests/disclosure_schema_live.rsa_compressed_body_is_415_rather_than_decompressed—src/api/disclosure_ingest.rsa_credential_in_the_query_string_is_refused_even_when_correct—src/api/disclosure_ingest.rsa_document_nested_past_the_depth_limit_is_400—src/api/disclosure_ingest.rsa_non_json_content_type_is_415—src/api/disclosure_ingest.rsa_reading_for_a_disabled_probe_is_400_and_stores_nothing—src/api/disclosure_ingest.rsa_rejection_does_not_echo_the_captured_value—src/api/disclosure_ingest.rsa_rejection_sanitises_a_hostile_probe_key—src/api/disclosure_ingest.rsa_sink_failure_is_500_and_says_nothing_about_the_payload—src/api/disclosure_ingest.rsa_valid_beacon_is_200_and_reaches_the_sink—src/api/disclosure_ingest.rsa_wrong_credential_is_also_401—src/api/disclosure_ingest.rsan_oversize_body_is_400—src/api/disclosure_ingest.rsan_oversize_declared_length_is_400_before_the_body_is_read—src/api/disclosure_ingest.rsan_unknown_probe_key_is_400—src/api/disclosure_ingest.rsmalformed_json_is_400_without_quoting_the_input—src/api/disclosure_ingest.rsmore_than_the_batch_limit_of_items_is_400—src/api/disclosure_ingest.rsno_credential_is_401—src/api/disclosure_ingest.rsretention_none_never_reaches_the_sink—src/api/disclosure_ingest.rsthe_default_retention_stores_nothing—src/api/disclosure_ingest.rsthe_rate_limit_applies_to_unauthenticated_requests—src/api/disclosure_ingest.rsthe_response_does_not_disclose_whether_anything_was_stored—src/api/disclosure_ingest.rsa_credential_in_the_query_is_refused—src/api/probe_run_ingest.rsa_failing_sink_is_a_500_and_says_nothing_was_stored—src/api/probe_run_ingest.rsa_page_token_is_spent_exactly_once—src/api/probe_run_ingest.rsa_valid_beacon_persists_once_with_the_peer_and_without_the_key—src/api/probe_run_ingest.rsno_credential_is_refused_and_nothing_is_written—src/api/probe_run_ingest.rsover_the_event_cap_is_refused_before_the_sink—src/api/probe_run_ingest.rs- _… 78 more_
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 103 | 118 |
Public modules with a //! block | 18 | 18 |
pie showData
title Public items with rustdoc
"Documented" : 103
"No rustdoc detected" : 15
Metrics
| Metric | Value |
|---|---|
| Rust source files | 19 |
| Source lines | 5404 |
| Code lines | 3886 |
| Public API items | 118 |
| Public modules | 18 |
| Tests | 108 |
| Examples | 0 |
| Cargo features | 0 |
| Direct runtime dependencies | 16 |
| Workspace reverse dependencies | 1 |
pie showData
title Public API by kind
"constant" : 19
"enum" : 13
"function" : 26
"method" : 36
"struct" : 21
"trait" : 3
pie showData
title Rust source composition
"Code" : 3886
"Blank or comment" : 1518
Generation
Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.