Documentation
¶
Overview ¶
Package telemetry is ovdb's opt-in usage statistics (spec/features/ telemetry-consent, decision 0009): the closed event set, the consent state stored in config.yaml, the forced-off conditions evaluated in the sending process, channel detection and a small synchronous PostHog (EU) batch sender. Nothing is sent unless a person turned it on.
Every outbound telemetry request originates here. Events are values with unexported fields, built only by the constructors below, which map every input onto an allowlisted enum, boolean or number — so no database id, path, URL, query, error message or other user-entered text can reach the wire (REQ:closed-event-set, REQ:never-collected).
Index ¶
- Constants
- Variables
- func AgentGuidance() string
- func Available() bool
- func Collected() []string
- func ForcedOff(getenv func(string) string) string
- func Key() string
- func NeverCollected() []string
- func NewInstallID() string
- func OptOutVariables() []string
- func Payload(key string, events []Event, meta Meta) []byte
- func ReasonText(reason string) string
- func ValidInstallID(id string) bool
- func Version(v string) string
- type Change
- type Channel
- type Consent
- type Decision
- type Document
- type Event
- func FromWire(w Wire) (Event, bool)
- func NewConsentEnabled() Event
- func NewDatabaseConnected(engine string, success bool, d time.Duration) Event
- func NewDatabaseCreated(engine string, success bool, d time.Duration) Event
- func NewDemoInstalled(success, alreadyInstalled bool) Event
- func NewDemoOpened(success bool) Event
- func NewEngineSelected(engine string) Event
- func NewExploreDataSelected(target string, datatugFound bool) Event
- func NewOnboardingCompleted(step string) Event
- func NewOnboardingError(step string, err error) Event
- func NewOnboardingStarted() Event
- func NewOptionSelected(option string) Event
- func NewServerStarted(success, portIsDefault bool, d time.Duration) Event
- func NewSkillInstalled(skill, harness string, success bool) Event
- type Meta
- type Name
- type Recorder
- func (r *Recorder) Available() bool
- func (r *Recorder) Consented()
- func (r *Recorder) Decide() Decision
- func (r *Recorder) Discard()
- func (r *Recorder) Drain(ctx context.Context)
- func (r *Recorder) Exit(ctx context.Context)
- func (r *Recorder) Flush(ctx context.Context)
- func (r *Recorder) FlushInBackground()
- func (r *Recorder) Pending() int
- func (r *Recorder) Record(events ...Event)
- type Status
- type Wire
Constants ¶
const ( StateNotAsked = "not_asked" StateEnabled = "enabled" StateDisabled = "disabled" )
Consent states (REQ:opt-in-state). The stored empty state is not_asked.
const ( ReasonEnvOVDB = "OVDB_TELEMETRY" ReasonDoNotTrack = "DO_NOT_TRACK" ReasonCI = "CI" ReasonNotAsked = StateNotAsked ReasonDisabled = StateDisabled ReasonConfigBroken = "config_unreadable" )
Reasons telemetry does not send. The forced-off ones name the variable that forced it (REQ:sender-process-decides).
const EnableCommand = "ovdb telemetry enable"
EnableCommand turns telemetry on in a terminal, asking the person first. The relay flag is named only in AgentGuidance (review F6).
const EnvTelemetry = "OVDB_TELEMETRY"
EnvTelemetry is OVDB's own switch; only "0" means anything (off). No variable can turn telemetry on (REQ:enable-requires-a-person).
const IPPlaceholder = "0.0.0.0"
IPPlaceholder is sent as $ip on every event: PostHog uses the client IP address only when $ip isn't passed in the properties (posthog.com tutorials/web-redact-properties, docs/product-analytics/privacy), so it stores the placeholder. The address still reaches PostHog's servers; the project's "Discard client IP data" setting stays a release precondition in case a transformation reads the connection address.
const MaxBuffered = 100
MaxBuffered is the pre-consent buffer's size (REQ:pre-consent-buffer).
const MaxQueuedBatches = 16
MaxQueuedBatches bounds the background sender's queue; a batch that finds it full is dropped, never blocking the caller.
const Other = "other"
Other replaces any value outside an enum.
const Timeout = 2 * time.Second
Timeout bounds one batch send, connection included (REQ:bounded-synchronous-sender).
const UserAgent = "ovdb"
UserAgent is the only client header ovdb sets: no version, OS user or host name.
Variables ¶
var ConsentStates = []string{StateEnabled}
ConsentStates are the telemetry_consent_changed states (enabled only: turning it off sends nothing).
var Engines = []string{"ingitdb", "sqlite", "firestore", "mysql", "postgres"}
Engines are the storage engine ids ovdb offers.
var Harnesses = func() []string { ids := make([]string, 0, len(cobracmd.DefaultHarnesses)) for _, h := range cobracmd.DefaultHarnesses { ids = append(ids, h.ID) } return ids }()
Harnesses are the skillsync harness ids skill installs target (cobracmd.DefaultHarnesses, the list `ovdb skills install --harness` accepts); a custom --dir is "other".
var Names = []Name{ OnboardingStarted, OnboardingOptionSelected, EngineSelected, DatabaseCreated, ExistingDatabaseConnected, ServerStarted, DemoInstalled, DemoOpened, SkillInstalled, ExploreDataSelected, TelemetryConsentChanged, OnboardingCompleted, OnboardingError, }
Names lists every event, in the spec's order.
var Options = []string{"demo", "create", "connect", "server", "browse", "explore", "skills", "settings", "databases"}
Options are the onboarding choices, also used as error and completion steps.
var Skills = []string{"openvaultdb", "todo-demo"}
Skills are the skills ovdb installs.
var Targets = []string{"datatug_cli", "datatug_web"}
Targets are the Explore data choices.
Functions ¶
func AgentGuidance ¶
func AgentGuidance() string
AgentGuidance is the JSON-only instruction for AI agents.
func Collected ¶
func Collected() []string
Collected is what is collected, as every interface lists it.
func ForcedOff ¶
ForcedOff is the forced-off reason in the environment getenv reads, or "" (the opt-out precedence of specscore-cli's ResolveOptOut, rungs 2 and 3: explicit variables, then CI). DO_NOT_TRACK counts when set to anything but "0" or "false" (telemetry-consent States table).
func NewInstallID ¶
func NewInstallID() string
NewInstallID is a random UUID v4, unrelated to anything on the machine.
func OptOutVariables ¶
func OptOutVariables() []string
OptOutVariables lists every variable ForcedOff reads.
func ReasonText ¶
ReasonText is the sentence for reason, "" when it needs none.
func ValidInstallID ¶
ValidInstallID reports whether id is an install id ovdb generated.
Types ¶
type Change ¶
type Change struct {
State string `json:"state"` // enabled or disabled
// ConfirmedByUser must be true to enable: the person said yes
// (REQ:enable-requires-a-person).
ConfirmedByUser bool `json:"confirmed_by_user,omitempty"`
// Channel is the deciding interface: cli, tui or agent, declared by the
// owner's local process with the instance secret and validated by the
// server; a console session is always web (review M1).
Channel string `json:"channel,omitempty"`
}
Change is the body of PUT /api/local/v1/telemetry.
type Channel ¶
type Channel string
Channel is the interface an event or a decision came from.
const ( ChannelCLI Channel = "cli" ChannelTUI Channel = "tui" ChannelWeb Channel = "web" ChannelAgent Channel = "agent" )
Channels (REQ:channel-detection).
func DetectChannel ¶
DetectChannel is agent when a known agent harness variable is present, otherwise cli (REQ:channel-detection). environ lists KEY=VALUE pairs, os.Environ when nil.
func ParseChannel ¶
ParseChannel is c when it is a channel, otherwise cli.
type Consent ¶
type Consent struct {
State string `yaml:"state,omitempty"`
DecidedAt string `yaml:"decided_at,omitempty"`
Channel string `yaml:"channel,omitempty"`
// InstallID exists only while enabled: created on enable, removed on
// disable.
InstallID string `yaml:"install_id,omitempty"`
}
Consent is the telemetry section of config.yaml.
func LoadConsent ¶
LoadConsent reads the telemetry section of <home>/config.yaml; a missing file is not_asked.
func (Consent) EffectiveState ¶
EffectiveState is c's state, not_asked when unset or unrecognised.
type Document ¶
type Document struct {
Schema int `json:"schema"`
Telemetry Status `json:"telemetry"`
Changed *bool `json:"changed,omitempty"`
// Backup is where an unreadable config.yaml was saved before disable
// rewrote it.
Backup string `json:"backup,omitempty"`
Next []envelope.Next `json:"next"`
}
Document is the body of GET/PUT /api/local/v1/telemetry and the --json output of `ovdb telemetry status|enable|disable`.
func NewDocument ¶
NewDocument describes d for a process whose build has key.
type Event ¶
type Event struct {
// contains filtered or unexported fields
}
Event is one closed event. Build it with the constructors.
func NewConsentEnabled ¶
func NewConsentEnabled() Event
NewConsentEnabled is telemetry_consent_changed, which is only ever sent for turning telemetry on.
func NewDatabaseConnected ¶
NewDatabaseConnected is existing_database_connected.
func NewDatabaseCreated ¶
NewDatabaseCreated is database_created.
func NewDemoInstalled ¶
NewDemoInstalled is demo_installed.
func NewEngineSelected ¶
NewEngineSelected is engine_selected.
func NewExploreDataSelected ¶
NewExploreDataSelected is explore_data_selected.
func NewOnboardingCompleted ¶
NewOnboardingCompleted is onboarding_completed.
func NewOnboardingError ¶
NewOnboardingError is onboarding_error; err's envelope code is the only thing taken from it.
func NewOnboardingStarted ¶
func NewOnboardingStarted() Event
NewOnboardingStarted is onboarding_started.
func NewOptionSelected ¶
NewOptionSelected is onboarding_option_selected.
func NewServerStarted ¶
NewServerStarted is server_started.
func NewSkillInstalled ¶
NewSkillInstalled is skill_installed.
type Name ¶
type Name string
Name is one event of the closed set.
const ( OnboardingStarted Name = "onboarding_started" OnboardingOptionSelected Name = "onboarding_option_selected" EngineSelected Name = "engine_selected" DatabaseCreated Name = "database_created" ExistingDatabaseConnected Name = "existing_database_connected" ServerStarted Name = "server_started" DemoInstalled Name = "demo_installed" DemoOpened Name = "demo_opened" SkillInstalled Name = "skill_installed" ExploreDataSelected Name = "explore_data_selected" TelemetryConsentChanged Name = "telemetry_consent_changed" OnboardingCompleted Name = "onboarding_completed" OnboardingError Name = "onboarding_error" )
The closed event set (REQ:closed-event-set). Adding one is a spec change.
type Recorder ¶
type Recorder struct {
// Channel is the sending interface; detected from Getenv and Environ
// at send time when empty (cli or agent).
Channel Channel
Environ func() []string // os.Environ when nil
Home string
Version string
Getenv func(string) string // os.Getenv when nil
// Buffer keeps events in memory while not_asked (the TUI process),
// for Flush after Turn on or Discard after No thanks. The CLI and the
// server never buffer.
Buffer bool
// Key and Endpoint default to the build's values; tests set them.
Key, Endpoint string
Client *http.Client
// contains filtered or unexported fields
}
Recorder collects one process's events and sends them in one batch per command or step. A nil *Recorder records nothing.
func (*Recorder) Consented ¶
func (r *Recorder) Consented()
Consented is this session's own Turn on: the events it buffered while not_asked may now be sent, by the next Flush.
func (*Recorder) Discard ¶
func (r *Recorder) Discard()
Discard drops every pending event (No thanks, dismissal, exit).
func (*Recorder) Drain ¶
Drain waits for queued background batches, at most until ctx ends (the server's shutdown).
func (*Recorder) Exit ¶
Exit ends a buffering process's session (No thanks, dismissal and exit all end the same way): events buffered without this session's Turn on are dropped, even when another process enabled telemetry meanwhile; the rest is sent when this process may send.
func (*Recorder) Flush ¶
Flush sends the pending events in one synchronous POST bounded by Timeout, when this process may send. A buffering recorder keeps its events while still not_asked; otherwise they are dropped. Failures are silent.
func (*Recorder) FlushInBackground ¶
func (r *Recorder) FlushInBackground()
FlushInBackground is Flush without waiting: the batch goes to a bounded queue that one worker sends, each batch within Timeout (review M5). The server uses it so a console action never waits for PostHog.
type Status ¶
type Status struct {
State string `json:"state"` // not_asked, enabled, disabled
// Sending is whether the process that answered sends events now.
Sending bool `json:"sending"`
// Reason is why it does not send: a forced-off variable, unavailable,
// not_asked or disabled.
Reason string `json:"reason,omitempty"`
ReasonText string `json:"reason_text,omitempty"`
// Available is false in a build without a PostHog key.
Available bool `json:"available"`
Provider string `json:"provider"`
DecidedAt string `json:"decided_at,omitempty"`
Channel string `json:"channel,omitempty"`
HasInstallID bool `json:"has_install_id"`
// AgentGuidance tells AI agents how to relay a person's answer; JSON
// only, never printed for people.
AgentGuidance string `json:"agent_guidance,omitempty"`
Collected []string `json:"collected"`
NeverCollected []string `json:"never_collected"`
}
Status is the shared telemetry state every interface shows (REQ:parity-of-controls).
type Wire ¶
type Wire struct {
Event string `json:"event"`
Option string `json:"option,omitempty"`
Engine string `json:"engine,omitempty"`
Skill string `json:"skill,omitempty"`
Harness string `json:"harness,omitempty"`
Target string `json:"target,omitempty"`
Step string `json:"step,omitempty"`
ErrorCode string `json:"error_code,omitempty"`
Success bool `json:"success,omitempty"`
PortIsDefault bool `json:"port_is_default,omitempty"`
AlreadyInstalled bool `json:"already_installed,omitempty"`
DataTugFound bool `json:"datatug_found,omitempty"`
DurationMS int64 `json:"duration_ms,omitempty"`
}
Wire is an event as the web page posts it (POST /api/local/v1/telemetry/ events). It is decoded, then rebuilt through the constructors, so its strings never reach the wire unchecked.