/** * Usage Telemetry Module * * Tracks anonymous scan/usage events to measure adoption and improve the * product. Telemetry is OPT-IN: OFF by default, and sent only when the user * explicitly sets VASPERA_TELEMETRY_ENABLED=1. Even then, DO_NOT_TRACK and test * runs are hard-off. It never sends source code, file contents, or secrets — * only an anonymous install id, a hash of the project path, the version, and * aggregate finding counts. Appropriate for scanning private/regulated code. * See TELEMETRY.md. * * @module telemetry/usage */ import type { Severity, CertificationLevel } from "../certification/types.js"; /** * Telemetry configuration */ export interface TelemetryConfig { /** Whether telemetry is enabled */ enabled: boolean; /** API endpoint for telemetry events */ endpoint?: string; /** Optional API key for authentication */ apiKey?: string; /** Include repository URL (requires explicit opt-in) */ includeRepoUrl?: boolean; /** Include organization name (requires explicit opt-in) */ includeOrgName?: boolean; /** Include user email (requires explicit opt-in) */ includeUserEmail?: boolean; } /** * Telemetry is opt-OUT: on by default, disabled by any of the standard or * Vaspera-specific opt-out signals, and never sent from automated test runs. */ export declare function isTelemetryDisabled(): boolean; /** * Default telemetry configuration (reads from environment) */ export declare const DEFAULT_TELEMETRY_CONFIG: TelemetryConfig; /** * Types of telemetry events */ export type TelemetryEventType = "cli_first_run" | "certification_started" | "certification_completed" | "certification_failed" | "finding_discovered" | "finding_resolved" | "scanner_run" | "badge_generated" | "sbom_generated" | "compliance_report_generated"; /** * Base telemetry event */ export interface BaseTelemetryEvent { /** Event type */ eventType: TelemetryEventType; /** ISO timestamp */ timestamp: string; /** SHA256 hash of project path (anonymized) */ projectHash: string; /** Anonymous install id (random UUID; counts unique installs) */ installId: string; /** Vaspera version */ vasperaVersion: string; /** Platform (darwin, linux, win32) */ platform: string; /** Node.js version */ nodeVersion: string; } /** * Event fired once on the first run of a fresh install. Distinct from * certification_* events: it captures installs that configure the server but may * never complete a full run, so distributed adoption is visible, not just usage. */ export interface CliFirstRunEvent extends BaseTelemetryEvent { eventType: "cli_first_run"; } /** * Event when a certification scan starts */ export interface CertificationStartedEvent extends BaseTelemetryEvent { eventType: "certification_started"; /** Scanners being run */ scanners: string[]; /** Frameworks being assessed */ frameworks: string[]; /** Corpus size setting */ corpusSize?: string; } /** * Event when a certification scan completes */ export interface CertificationCompletedEvent extends BaseTelemetryEvent { eventType: "certification_completed"; /** Certification ID */ certificationId: string; /** Achieved level */ level: CertificationLevel; /** Overall score (0-100) */ score: number; /** Duration in milliseconds */ duration: number; /** Findings count by severity */ severityCounts: Record; /** Total findings */ totalFindings: number; /** Frameworks assessed */ frameworks: string[]; /** Optional: repository URL if user opted in */ repoUrl?: string; /** Optional: organization name if user opted in */ orgName?: string; /** Optional: user email if user opted in */ userEmail?: string; } /** * Event when a certification scan fails */ export interface CertificationFailedEvent extends BaseTelemetryEvent { eventType: "certification_failed"; /** Error message (sanitized) */ errorMessage: string; /** Error category */ errorCategory: "scanner_error" | "config_error" | "timeout" | "unknown"; /** Duration before failure */ duration: number; } /** * Event when a finding is discovered */ export interface FindingDiscoveredEvent extends BaseTelemetryEvent { eventType: "finding_discovered"; /** Finding category */ category: string; /** Severity level */ severity: Severity; /** Scanner that found it */ scanner: string; /** Rule/check ID */ ruleId?: string; } /** * Event when a scanner completes */ export interface ScannerRunEvent extends BaseTelemetryEvent { eventType: "scanner_run"; /** Scanner name */ scanner: string; /** Duration in milliseconds */ duration: number; /** Number of findings */ findingsCount: number; /** Whether it succeeded */ success: boolean; } /** * Union of all telemetry events */ export type TelemetryEvent = CliFirstRunEvent | CertificationStartedEvent | CertificationCompletedEvent | CertificationFailedEvent | FindingDiscoveredEvent | ScannerRunEvent; /** * Initialize telemetry with configuration */ export declare function initTelemetry(config: Partial): void; /** * Check if telemetry is enabled */ export declare function isTelemetryEnabled(): boolean; /** * Print the one-time telemetry notice to stderr on the first run of a new * install. Telemetry is OPT-IN (off by default), so the notice is transparency * only — it states the current state and how to change it; it never means data * is being sent unless the user opted in. stderr only — stdout is the MCP * JSON-RPC channel and must not be polluted. No-op when DO_NOT_TRACK is set * (the user clearly wants no telemetry chatter) or this is not the first run. */ export declare function maybeShowTelemetryNotice(): void; /** * Hash a value for anonymization */ export declare function hashValue(value: string): string; /** * Track a telemetry event */ export declare function trackEvent(event: Omit & { eventType: TelemetryEventType; projectPath: string; }): Promise; /** * Flush buffered events to the telemetry endpoint */ export declare function flushEvents(): Promise; /** * Shutdown telemetry (flush remaining events) */ export declare function shutdownTelemetry(): Promise; /** * Track the first run of a fresh install (adoption signal). Fires at most once * per install — guarded by the same ~/.vaspera/install-id first-run marker used * for the opt-out notice, which is memoized per-process. No-op on subsequent * runs and when telemetry is disabled. Fire-and-forget; never blocks startup. */ export declare function trackCliFirstRun(projectPath: string): Promise; /** * Track certification start */ export declare function trackCertificationStarted(projectPath: string, scanners: string[], frameworks: string[], corpusSize?: string): Promise; /** * Track certification completion */ export declare function trackCertificationCompleted(projectPath: string, certificationId: string, level: CertificationLevel, score: number, duration: number, severityCounts: Record, totalFindings: number, frameworks: string[], optionalInfo?: { repoUrl?: string; orgName?: string; userEmail?: string; }): Promise; /** * Track certification failure */ export declare function trackCertificationFailed(projectPath: string, errorMessage: string, errorCategory: CertificationFailedEvent["errorCategory"], duration: number): Promise; /** * Track a finding discovery */ export declare function trackFindingDiscovered(projectPath: string, category: string, severity: Severity, scanner: string, ruleId?: string): Promise; /** * Track a scanner run */ export declare function trackScannerRun(projectPath: string, scanner: string, duration: number, findingsCount: number, success: boolean): Promise; export declare const telemetry: { init: typeof initTelemetry; isEnabled: typeof isTelemetryEnabled; showNotice: typeof maybeShowTelemetryNotice; track: typeof trackEvent; flush: typeof flushEvents; shutdown: typeof shutdownTelemetry; trackCliFirstRun: typeof trackCliFirstRun; trackCertificationStarted: typeof trackCertificationStarted; trackCertificationCompleted: typeof trackCertificationCompleted; trackCertificationFailed: typeof trackCertificationFailed; trackFindingDiscovered: typeof trackFindingDiscovered; trackScannerRun: typeof trackScannerRun; }; //# sourceMappingURL=usage.d.ts.map