Docs/Reference
Fleet Analytics V1
The exact bucket, metric, privacy, coverage and archive rules that every V1 producer, importer and aggregator must enforce.The checked-in Protobuf, Parquet schema and golden fixtures are executable parts of this contract. Connector framing, GreptimeDB tables and SQL remain internal implementation details.
Compatibility identity
| Layer | V1 identity | Purpose |
|---|---|---|
| Protobuf | rustyauth.analytics.v1 | Message and closed-enum compatibility |
| Batch | transport_schema_version = 1 | Delivery framing and resource limits |
| Metrics | METRIC_SCHEMA_VERSION_V1 | Names, units, dimensions and arithmetic |
| Parquet | rustyauth-metric-bucket-v1 | Portable archive and deterministic repair |
| Manifest | manifest_schema_version = 1 | Integrity, identity and import idempotency |
A receiver rejects an unknown version or unknown V1 field. It never guesses a schema from release metadata, silently maps an unknown enum to “other,” or reinterprets incompatible input as V1.
Window and retry contract
- Every bucket is exactly five minutes, UTC-aligned and left-closed/right-open.
- Event time selects the bucket; receipt time never rewrites it.
- V1 sends closed buckets only. The default close grace is two minutes.
- The first revision is
1. A correction increments the revision and resends the complete snapshot. - A retry reuses the exact canonical key and revision; it never sends a counter delta.
- A batch carries at most 288 buckets and encodes to at most 256 KiB.
(realm_id, assignment_epoch, bucket_start_unix_milliseconds,
bucket_width_seconds, metric_schema_version)That tuple identifies the canonical bucket. revision selects the accepted value for it. Event sequence bounds are both zero when no durable event contributed; otherwise they form an ordered positive range.
Metric families and aggregation
An absent family means unsupported or unavailable. A present family of zeros means supported with no observed activity. Registry-backed coverage preserves that distinction for every customer-facing result.
| Family | V1 facts | Aggregation |
|---|---|---|
| Authentication | Attempts, successes, failures, denials, active account observations | Sum counts; derive success rate from summed counts |
| Latency | Authentication, API, SableDB and delivery count/sum/histogram | Merge compatible cumulative histograms before percentiles |
| Registration | Options, ceremonies, responses, completions and expiries | Sum every funnel stage independently |
| Sessions and tokens | Sessions created/revoked; user/service tokens issued | Sum counts |
| Service accounts | Calls, outcomes, denials and credential rotations | Sum counts |
| Webhooks | Deliveries, outcomes, latency and latest backlog | Sum events; latest backlog per realm |
| Platform | API and SableDB requests, errors and latency | Sum counts; merge histograms |
| Realm health | Serving state, backup age, key age and connector lag | Latest realm observation plus coverage |
active_account_observations counts distinct realm accounts within one bucket. Summing it produces account-window observations: the same account can contribute in multiple windows and one person may have accounts in multiple realms.
Required invariants
authentication attempts = successes + failures + denials
service-account calls = successes + failures + denials
webhook deliveries = successes + failures
options_started >= ceremonies_opened
>= responses_returned
>= registrations_completedAuthentication flow and failure-class breakdowns must exactly reproduce their headline totals. Expired registrations are outside the funnel ordering because a challenge may expire in a later window. Counters are capped at 1,000,000,000 per realm bucket, sums use checked arithmetic, and impossible relationships or duplicate enum dimensions fail closed.
Fixed histogram profiles
Each histogram contains cumulative counts for every bound and a final +Inf count equal to count. Counts must be monotonic. Percentiles use the upper bound of the first merged cumulative bucket meeting the nearest-rank threshold.
Interactive milliseconds V1
5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, +InfUsed for authentication, API and SableDB latency.
Delivery milliseconds V1
10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 30000, +InfUsed for webhook delivery latency. A value falling only in +Inf has no finite upper bound.
Bounded dimensions and privacy
V1 has no label map and accepts no caller-named dimension. A realm may send only closed Protobuf enums for authentication flow, failure class, metric family, histogram profile, serving state, acknowledgement and rejection reason, plus its stable realm identity and assignment epoch.
realm_id is the same stable 1–64 character ASCII identifier accepted by realm configuration and Fleet pairing; it is not required to be a UUID. Batch and manifest IDs remain canonical UUID idempotency keys.
Fleet resolves organization, project and environment from the authenticated connection. Those hierarchy values are never trusted from a realm payload.
The following are prohibited in metrics, dimensions, manifests and acknowledgements:
- user, subject, account, credential, challenge, session, token and request IDs;
- email, phone, name, IP address, user agent and arbitrary path values;
- RP assertions, public keys, cookies, secrets and secret hints;
- webhook URLs, error messages and caller-defined labels; and
- presigned URLs or object-store credentials.
Batch and manifest UUIDs are transport idempotency keys, not queryable dimensions. Event sequences are watermarks.
Coverage arithmetic
expected_realms = reporting_realms + stale_realms
total_realms = expected_realms + disabled_realms + unsupported_realms
partial = reporting_realms < expected_realmsCoverage is metric-family-specific. The last complete window is zero when no complete common window exists; otherwise it is a UTC-aligned five-minute instant.
Signed Parquet archive
A V1 archive uses Zstandard Parquet with one row per complete bucket snapshot and revision. Physical Parquet bytes are not golden because writer metadata and page layout may differ; the versioned logical-row schema fixes field IDs, enum numbers and meaning.
After publication, the object is immutable. Its manifest binds the exact schema versions, realm, assignment epoch, credential-free object key, SHA-256 digest, byte length, row count, bucket and sequence ranges, creation time, signing-key ID and raw 64-byte P-256 signature.
rustyauth.analytics.metric-bucket-manifest.v1
NUL
deterministic protobuf manifest with signature unsetThe importer verifies the approved bucket binding, signature, digest, size, row count, time and sequence ranges before reading a row, then records manifest identity and digest for retry idempotency. V1 has no raw-event or subject-level archive row.
Deterministic scope results
- Derive every numerical scope directly from canonical realm buckets for the requested assignment epochs.
- Calculate rates from summed numerators and denominators; never average child percentages.
- Merge compatible cumulative histograms before calculating percentiles.
- Select the latest accepted gauge per realm before scope summation or classification.
- On correction, recompute the affected time range from the highest accepted revisions.
Change policy and executable references
Adding an enum is additive to Protobuf but requires a new metric schema before a realm emits it. A changed unit, histogram boundary, aggregation rule, invariant, Parquet meaning or privacy rule always requires a new metric schema. Field numbers and Parquet field IDs are never reused.