Hankweave collects anonymous usage statistics to help improve the tool. This page is the reference for that system: what identity it uses, how to turn it off, what it does and does not send, which events exist, and where the data goes. If you only want to disable telemetry, jump to Opting out; if you want to know what leaves your machine before deciding, start with Privacy principles in practice.
Identity and reset#
Telemetry gives each machine an anonymous random UUID v4, called clientId, and persists it in a local identity file. The file contains clientId and createdAt, plus optional noticeShownAt and firstSuccessAt timestamps. firstSuccessAt records the first successful hank run and is also used by the TUI's one-time star nudge.
export interface TelemetryIdentity {
clientId: string;
createdAt: string;
noticeShownAt?: string;
firstSuccessAt?: string; // Tracks first successful hank run (for star nudge)
}
The default path is ~/.hankweave/telemetry.json. Set $HANKWEAVE_CACHE_DIR to use <cache-dir>/telemetry.json instead. The path resolution lives in two small functions:
function getTelemetryDir(): string {
const cacheDir = process.env.HANKWEAVE_CACHE_DIR;
if (cacheDir) {
return cacheDir;
}
return path.join(os.homedir(), ".hankweave");
}
function getTelemetryFilePath(): string {
return path.join(getTelemetryDir(), "telemetry.json");
}
getOrCreateClientId() creates the UUID and file on first use, then returns the stored ID. isFirstRun() is true when the file is absent; isFirstSuccess() is true when firstSuccessAt has not been recorded. Delete ~/.hankweave/telemetry.json, or the corresponding file under $HANKWEAVE_CACHE_DIR, to generate a new identity on the next run.
See Execution directory for related local paths and First run for the first-run flow.
Opting out before sending#
Telemetry is enabled by default, but several mechanisms can disable it, and they have a fixed precedence. The resolver applies the first matching rule below; later settings cannot re-enable telemetry after an earlier rule disables it.
export function resolveTelemetryConfig(fileConfig?: TelemetryConfig): ResolvedTelemetryConfig {
// Priority 1: DO_NOT_TRACK=1 (universal standard)
if (process.env.DO_NOT_TRACK === "1") {
return {
enabled: false,
endpoint: DEFAULT_POSTHOG_HOST,
debug: false,
disabledReason: "DO_NOT_TRACK=1",
};
}
// Priority 2: HANKWEAVE_TELEMETRY=0 or HANKWEAVE_TELEMETRY=false
const telemetryEnv = process.env.HANKWEAVE_TELEMETRY;
if (telemetryEnv === "0" || telemetryEnv === "false") {
return {
enabled: false,
endpoint: DEFAULT_POSTHOG_HOST,
debug: false,
disabledReason: "HANKWEAVE_TELEMETRY=0",
};
}
// Priority 3: CI detection (cannot be overridden in V1)
if (isCI()) {
return {
enabled: false,
endpoint: DEFAULT_POSTHOG_HOST,
debug: false,
disabledReason: "CI environment detected",
};
}
// Priority 4: Config file
if (fileConfig?.enabled === false) {
return {
enabled: false,
endpoint: fileConfig.endpoint || DEFAULT_POSTHOG_HOST,
debug: fileConfig.debug || false,
disabledReason: "Disabled in config file",
};
}
// Resolve endpoint from env or config
const endpoint =
process.env.HANKWEAVE_TELEMETRY_ENDPOINT || fileConfig?.endpoint || DEFAULT_POSTHOG_HOST;
// Resolve debug from env or config
const debug = process.env.HANKWEAVE_TELEMETRY_DEBUG === "1" || fileConfig?.debug || false;
// Default: ENABLED
return {
enabled: true,
endpoint,
debug,
};
| Priority | Mechanism | Result | disabledReason |
|---|---|---|---|
| 1 | DO_NOT_TRACK=1 | Disabled | DO_NOT_TRACK=1 |
| 2 | HANKWEAVE_TELEMETRY=0 or HANKWEAVE_TELEMETRY=false | Disabled | HANKWEAVE_TELEMETRY=0 |
| 3 | CI environment detected | Disabled | CI environment detected |
| 4 | hankweave.json has telemetry.enabled: false | Disabled | Disabled in config file |
| 5 | No earlier rule matches | Enabled | – |
The table after the code summarizes the same precedence: DO_NOT_TRACK wins over HANKWEAVE_TELEMETRY, which wins over CI detection, which wins over the config file. CI detection treats CI=true and CI=1 as CI, and also treats the presence of GITHUB_ACTIONS, TRAVIS, CIRCLECI, GITLAB_CI, JENKINS_URL, BUILDKITE, DRONE, CI_NAME, CODEBUILD_BUILD_ID, or TF_BUILD as CI. Because this check precedes the file setting, hankweave.json cannot re-enable telemetry through this configuration path while CI is detected.
The telemetry object in hankweave.json accepts optional enabled (boolean), endpoint (URI string), and debug (boolean) properties. Additional properties are rejected by the schema.
| field | type | default | required | constraints | description |
|---|---|---|---|---|---|
telemetry | object | no | Telemetry configuration |
The full object contract belongs to Hankweave JSON; the HANKWEAVE_TELEMETRY* variables belong to Environment variables. One wiring detail matters here: telemetry reads the raw telemetry key from hankweave.json through readFileTelemetryConfig(), because resolveSettings() strips that key from the effective runtime-configuration layer.
For the highest-priority opt-out, run:
DO_NOT_TRACK=1 bunx hankweave@0.10.0
Privacy principles in practice#
The telemetry source documents three transformation principles – content becomes size, paths become counts, and IDs become hashes. An eight-category privacy-handling type applies those decisions field by field; in practice, error messages become failure_type values and environment variables become counts. The header comment and the category type below are the source of those rules:
/**
* Telemetry Types and Schemas
*
* Privacy-preserving types for telemetry events.
* All types follow the principle: content → size, paths → counts, IDs → hashes.
*/
type PrivacyHandling =
| "include" // Include as-is (safe data like model names)
| "hash" // SHA256 hash (for IDs we want to correlate)
| "length" // Replace with character count
| "count" // Replace with item count
| "count_and_size" // Replace with count + byte size
| "type_only" // Keep type/enum, drop details
| "exclude" // Don't include at all
| "nested"; // Has its own privacy-preserving version
| Documented principle or reduction | Recorded form |
|---|---|
| Content → Size | Prompt text becomes length_chars. |
| Paths → Counts | File paths become file_count and total_size_bytes. |
| IDs → Hashes | Codon, run, and loop IDs become SHA256 hashes. |
| In practice: messages → types | Error messages become failure_type enum values. |
| In practice: secrets → counts | Environment variables become { count: N }. |
The eight field-handling categories are include, hash, length, count, count_and_size, type_only, exclude, and nested. TypeScript requires every field on Codon, Loop, and Run to appear in its privacy map; adding an unmapped field causes a compile error. The Codon map excludes name, hashes id, preserves selected configuration fields, counts file and environment-related fields, and turns prompt text into lengths or prompt-file fields into counts plus sizes. The privacy-preserving hank shape adds a SHA256 hank_hash and emits counts and hashes rather than prompt text, paths, or codon names.
For field ownership and execution semantics, see Codons and Events.
What is never collected#
The privacy maps exclude codon and loop name, runFolder, gitBranch, and serverPid. They reduce prompt text, system-prompt text, descriptions, file paths, environment variables, error messages, and rig setup to lengths, counts, hashes, types, or operation counts; raw content does not reach telemetry.
Error tracking has a separate scrubber. Before an error is sent, captureError() removes home-directory paths, API keys and tokens, KEY=VALUE patterns, and absolute paths from the error message and stack trace. This scrubber is distinct from the telemetry privacy maps.
The result is that a prompt appears as a measurement rather than its contents:
{
"prompt": { "length_chars": "<count>" },
"id_hash": "<SHA256 hash>"
}
See Privacy principles and Error tracking and scrubbing.
What is collected#
run_started carries a privacy-preserving hank structure: each item has a type, position, ID hash, model display name, continuation mode, prompt source and size, while the summary contains totals such as codons, loops, models, sentinels, and checkpointing.
run_completed carries a duration bucket (<1m, 1-5m, 5-15m, or 15m+), per-codon status and measurements, aggregate tool counts by name, and model-call counts by model ID. Per-codon measurements include duration, tokens, cost, sentinel measurements, and checkpoint flags.
Token usage is accumulated from token.usage events per codon and per model: input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens, and total_cost. tool.result events supply tool-call and tool-error counts per codon and at run level. Every event receives hankweave_version, os, os_version, arch, node_version, is_ci, and is_compiled as common user properties.
A collected codon therefore uses measurements and labels instead of source content:
{
"type": "codon",
"position": "<position>",
"id_hash": "<SHA256 hash>",
"model": "<model display name>",
"prompt": { "source": "<inline|file|files>", "length_chars": "<count>" },
"checkpointed_files": { "pattern_count": "<count>" }
}
See Events for the canonical event payload schemas and Budgets for budget semantics.
Event catalog and emitter status#
The shipped TelemetryEventName union declares 26 names in nine categories. Twenty-three have emitters; loop_iteration_started, continuation_started, and $ai_span are declared-only in hankweave 0.10.0. The union and the per-category status table below show both:
export type TelemetryEventName =
// CLI events
| "cli_init"
| "cli_validate"
| "cli_cleanup"
| "cli_run"
| "cli_help"
// Run lifecycle
| "run_started"
| "run_completed"
| "run_failed"
| "run_crashed"
// Codon lifecycle
| "codon_started"
| "codon_completed"
| "codon_failed"
| "codon_skipped"
// Loop lifecycle
| "loop_iteration_started"
| "loop_iteration_completed"
// Rig lifecycle
| "rig_setup_completed"
| "rig_setup_failed"
// Recovery
| "checkpoint_created"
| "rollback_completed"
| "continuation_started"
// Sentinel
| "sentinel_triggered"
// Budget
| "budget_set"
| "budget_exceeded"
// PostHog LLM Analytics (special $ prefixed events)
| "$ai_generation"
| "$ai_trace"
| "$ai_span";
| Category | Names | Emitter status |
|---|---|---|
| CLI | cli_init, cli_validate, cli_cleanup, cli_run, cli_help | Emitted |
| Run lifecycle | run_started, run_completed, run_failed, run_crashed | Emitted |
| Codon lifecycle | codon_started, codon_completed, codon_failed, codon_skipped | Emitted |
| Loop lifecycle | loop_iteration_started, loop_iteration_completed | First declared-only; second emitted |
| Rig lifecycle | rig_setup_completed, rig_setup_failed | Emitted |
| Recovery | checkpoint_created, rollback_completed, continuation_started | First two emitted; last declared-only |
| Sentinel | sentinel_triggered | Emitted |
| Budget | budget_set, budget_exceeded | Emitted |
| PostHog LLM Analytics | $ai_generation, $ai_trace, $ai_span | First two emitted; last declared-only |
Emission timing differs by event kind. Early-exit CLI events (cli_init, cli_validate, cli_cleanup, cli_help) are sent immediately through sendCliTelemetry(), which creates a temporary collector and reads hankweave.json from the current directory for opt-out. The normal cli_run event is sent immediately by the main runtime collector's trackCliEvent() after configuration resolution and collector creation. Both paths are fire-and-forget and do not block the CLI. Runtime and codon events are queued from the server event stream and batch-sent at shutdown. $ai_generation is emitted once per completed codon, or once per model when a codon used multiple models; it carries $ai_trace_id, $ai_span_id, $ai_model, $ai_provider, $ai_input_tokens, $ai_output_tokens, $ai_cache_read_input_tokens, $ai_cache_creation_input_tokens, $ai_total_cost_usd, and $ai_latency (codon duration in seconds, or undefined when duration is absent). $ai_trace is emitted at shutdown for the full run with run-level latency and an error flag.
A compact LLM analytics payload has this shape:
{
"event": "$ai_generation",
"properties": {
"$ai_trace_id": "<trace id>",
"$ai_span_id": "<span id>",
"$ai_model": "<model>",
"$ai_provider": "<provider>",
"$ai_input_tokens": "<count>",
"$ai_output_tokens": "<count>",
"$ai_cache_read_input_tokens": "<count>",
"$ai_cache_creation_input_tokens": "<count>",
"$ai_total_cost_usd": "<cost>",
"$ai_latency": "<duration-seconds>"
}
}
The canonical payload definitions are owned by Events. The generated catalog below describes the server events that feed runtime collection; it is not a second list of TelemetryEventName values.
| id | category | journaled | sentinelRouted | payloadFields | receipts |
|---|---|---|---|---|---|
| archive.completed | server-state | true | true | codonId, archivedPaths | schemas/event-schemas.ts:668, schemas/event-schemas.ts:977, schemas/event-schemas.ts:1257, hankweave-runtime.ts:5465 |
| archive.partial | server-state | true | true | codonId, archivedPaths, failedPaths | schemas/event-schemas.ts:673, schemas/event-schemas.ts:978, schemas/event-schemas.ts:1258, hankweave-runtime.ts:5450 |
| assistant.action | agentic-backbone | true | true | codonId, action, content, toolName?, toolInput? | schemas/event-schemas.ts:572, schemas/event-schemas.ts:986, schemas/event-schemas.ts:1237, hankweave-runtime.ts:2777, hankweave-runtime.ts:2789, hankweave-runtime.ts:2824 |
| budget.summary | server-state | true | true | ceiling, allocation, rows, totals | schemas/event-schemas.ts:724, schemas/event-schemas.ts:979, schemas/event-schemas.ts:1267, hankweave-runtime.ts:6478 |
| checkpoint.list | server-state | true | true | runId, checkpoints, currentBranch | schemas/event-schemas.ts:637, schemas/event-schemas.ts:968, schemas/event-schemas.ts:1250, hankweave-runtime.ts:4255 |
| codon.completed | server-state | true | true | codonId, success, cost, duration, exitStatus, failureReason?, failureIgnored?, budgetExceeded? | schemas/event-schemas.ts:560, schemas/event-schemas.ts:961, schemas/event-schemas.ts:1235, hankweave-runtime.ts:1886, hankweave-runtime.ts:2064, hankweave-runtime.ts:2253, hankweave-runtime.ts:3365 |
| codon.extended | server-state | true | true | codonId, codonName, extensionNumber, exhaustWithPrompt, cumulativeTokens, cumulativeCost | schemas/event-schemas.ts:565, schemas/event-schemas.ts:962, schemas/event-schemas.ts:1236, hankweave-runtime.ts:2946 |
| codon.started | server-state | true | true | codonId, codonName, codonDescription?, sessionId, previousSessionId?, startTime, promptMetadata? | schemas/event-schemas.ts:555, schemas/event-schemas.ts:960, schemas/event-schemas.ts:1234, hankweave-runtime.ts:316 |
| error | server-state | true | true | message, codon?, fatal, severity?, context?, code? | schemas/event-schemas.ts:617, schemas/event-schemas.ts:967, schemas/event-schemas.ts:1246, hankweave-runtime.ts:975, hankweave-runtime.ts:995, hankweave-runtime.ts:1019, hankweave-runtime.ts:1062, hankweave-runtime.ts:1090, hankweave-runtime.ts:1116, hankweave-runtime.ts:1814, hankweave-runtime.ts:1867, hankweave-runtime.ts:2045, hankweave-runtime.ts:2236, hankweave-runtime.ts:2609, hankweave-runtime.ts:3981, hankweave-runtime.ts:4177, hankweave-runtime.ts:4273, hankweave-runtime.ts:4349, hankweave-runtime.ts:4512, hankweave-runtime.ts:4536, hankweave-runtime.ts:4599, hankweave-runtime.ts:4769, hankweave-runtime.ts:4804, hankweave-runtime.ts:4896, hankweave-runtime.ts:4918, hankweave-runtime.ts:5016 |
| file.updated | agentic-backbone | true | true | path, filename, content, action | schemas/event-schemas.ts:587, schemas/event-schemas.ts:988, schemas/event-schemas.ts:1240, hankweave-runtime.ts:2357, hankweave-runtime.ts:3927 |
| filetree.updated | agentic-backbone | true | true | tree | schemas/event-schemas.ts:592, schemas/event-schemas.ts:989, schemas/event-schemas.ts:1241, hankweave-runtime.ts:3954 |
| history.batch | connection-state | false | false | events, hasMore | schemas/event-schemas.ts:688, schemas/event-schemas.ts:1012, schemas/event-schemas.ts:1260, hankweave-runtime.ts:1312 |
| incomplete.codon | connection-state | false | false | codonId, codonName, message | schemas/event-schemas.ts:622, schemas/event-schemas.ts:1013, schemas/event-schemas.ts:1247 |
| info | server-state | true | true | message | schemas/event-schemas.ts:627, schemas/event-schemas.ts:966, schemas/event-schemas.ts:1248, hankweave-runtime.ts:1705, hankweave-runtime.ts:1728, hankweave-runtime.ts:1902, hankweave-runtime.ts:1933, hankweave-runtime.ts:1971, hankweave-runtime.ts:2080, hankweave-runtime.ts:2196, hankweave-runtime.ts:2269, hankweave-runtime.ts:2730, hankweave-runtime.ts:3082, hankweave-runtime.ts:3100, hankweave-runtime.ts:3181, hankweave-runtime.ts:3205, hankweave-runtime.ts:3474, hankweave-runtime.ts:3723, hankweave-runtime.ts:3755, hankweave-runtime.ts:4058, hankweave-runtime.ts:4328, hankweave-runtime.ts:6427 |
| loop.iteration.completed | server-state | true | true | loopId, iteration, durationMs, costUsd, tokensUsed, isFinal, terminationReason? | schemas/event-schemas.ts:612, schemas/event-schemas.ts:976, schemas/event-schemas.ts:1245, hankweave-runtime.ts:6035 |
| pong | connection-state | false | false | message, timestamp, clientId? | schemas/event-schemas.ts:683, schemas/event-schemas.ts:1011, schemas/event-schemas.ts:1259, hankweave-runtime.ts:1215, hankweave-runtime.ts:1243 |
| rig.output | agentic-backbone | true | true | codonId, stream, line, commandIndex | schemas/event-schemas.ts:607, schemas/event-schemas.ts:992, schemas/event-schemas.ts:1244, hankweave-runtime.ts:6109 |
| rig.setup.completed | agentic-backbone | true | true | codonId, rigType, commandCount, durationMs, createdCheckpoint | schemas/event-schemas.ts:597, schemas/event-schemas.ts:990, schemas/event-schemas.ts:1242, hankweave-runtime.ts:2160 |
| rig.setup.failed | agentic-backbone | true | true | codonId, failureType, exitCode?, commandIndex?, ignored | schemas/event-schemas.ts:602, schemas/event-schemas.ts:991, schemas/event-schemas.ts:1243, hankweave-runtime.ts:1791 |
| rollback.archiveRestore | server-state | true | true | codonId, restoredPaths, failedPaths?, status | schemas/event-schemas.ts:678, schemas/event-schemas.ts:974, schemas/event-schemas.ts:1256, hankweave-runtime.ts:5557 |
| rollback.codonCheckpoint | server-state | true | true | codonId, codonName, checkpoint, checkpointType, message | schemas/event-schemas.ts:647, schemas/event-schemas.ts:971, schemas/event-schemas.ts:1252, hankweave-runtime.ts:4684, hankweave-runtime.ts:5136, hankweave-runtime.ts:5191 |
| rollback.completed | server-state | true | true | fromRun, toRun, checkpoint, codonId, codonName, checkpointType, autoRestart | schemas/event-schemas.ts:662, schemas/event-schemas.ts:972, schemas/event-schemas.ts:1255, hankweave-runtime.ts:4722, hankweave-runtime.ts:5254 |
| rollback.progress | server-state | true | true | currentStep, totalSteps, message | schemas/event-schemas.ts:657, schemas/event-schemas.ts:970, schemas/event-schemas.ts:1254, hankweave-runtime.ts:5117, hankweave-runtime.ts:5156 |
| rollback.rigCleanup | server-state | true | true | codonId, codonName, directories, status, successfulCleanups?, failedCleanups?, error? | schemas/event-schemas.ts:652, schemas/event-schemas.ts:973, schemas/event-schemas.ts:1253, hankweave-runtime.ts:5624, hankweave-runtime.ts:5662, hankweave-runtime.ts:5678 |
| rollback.started | server-state | true | true | fromRun, fromCodon, toCodon, toCheckpoint, checkpointType, codonsToProcess | schemas/event-schemas.ts:642, schemas/event-schemas.ts:969, schemas/event-schemas.ts:1251, hankweave-runtime.ts:4645, hankweave-runtime.ts:5095 |
| sentinel.error | sentinel | true | false | sentinelId, codonId, errorType, message, retriable, consecutiveFailureCount | schemas/event-schemas.ts:709, schemas/event-schemas.ts:1001, schemas/event-schemas.ts:1264, sentinels/sentinel.ts:576, sentinels/sentinel.ts:663, sentinels/sentinel.ts:798, sentinels/sentinel.ts:905 |
| sentinel.loaded | sentinel | true | false | sentinelId, codonId, model, triggerType, executionStrategy, conversational, source, sourcePath? | schemas/event-schemas.ts:699, schemas/event-schemas.ts:999, schemas/event-schemas.ts:1262, hankweave-runtime.ts:5819 |
| sentinel.output | sentinel | true | false | sentinelId, codonId, triggerNumber, outputType, content, cost, tokens, eventCount | schemas/event-schemas.ts:714, schemas/event-schemas.ts:1002, schemas/event-schemas.ts:1265, sentinels/sentinel.ts:538, sentinels/sentinel.ts:635, sentinels/sentinel.ts:762, sentinels/sentinel.ts:877 |
| sentinel.triggered | sentinel | true | false | sentinelId, codonId, triggerNumber, strategy, eventCount, queueSize | schemas/event-schemas.ts:719, schemas/event-schemas.ts:1003, schemas/event-schemas.ts:1266, sentinels/sentinel.ts:392 |
| sentinel.unloaded | sentinel | true | false | sentinelId, codonId, reason, errorType?, finalCost, llmCallCount | schemas/event-schemas.ts:704, schemas/event-schemas.ts:1000, schemas/event-schemas.ts:1263, sentinels/sentinel-manager.ts:851 |
| server.idle | server-state | true | true | reason, message | schemas/event-schemas.ts:632, schemas/event-schemas.ts:964, schemas/event-schemas.ts:1249, hankweave-runtime.ts:941, hankweave-runtime.ts:3621, hankweave-runtime.ts:3791, hankweave-runtime.ts:3812, hankweave-runtime.ts:4072, hankweave-runtime.ts:4743, hankweave-runtime.ts:5279, hankweave-runtime.ts:5291 |
| server.ready | connection-state | false | false | serverVersion, executionPath, agentRootPath, dataPath, port, proxyPort?, outputDirectory? | schemas/event-schemas.ts:545, schemas/event-schemas.ts:1010, schemas/event-schemas.ts:1232, hankweave-runtime.ts:916 |
| state.snapshot | server-state | true | true | currentCodon?, completedCodons, fileTree, totalCost, totalTime, recentFileAccess?, isRollingBack | schemas/event-schemas.ts:550, schemas/event-schemas.ts:963, schemas/event-schemas.ts:1233, hankweave-runtime.ts:1422 |
| state.transition | server-state | true | true | transitionType, runId?, codonId?, transition, resultingState | schemas/event-schemas.ts:693, schemas/event-schemas.ts:975, schemas/event-schemas.ts:1261, hankweave-runtime.ts:394 |
| token.usage | server-state | true | true | codonId, inputTokens, outputTokens, cacheCreationTokens, cacheReadTokens, totalCost, modelId?, modelUsage? | schemas/event-schemas.ts:577, schemas/event-schemas.ts:965, schemas/event-schemas.ts:1238, hankweave-runtime.ts:2646, hankweave-runtime.ts:2660 |
| tool.result | agentic-backbone | true | true | codonId, toolUseId, toolName, result, truncated, originalLength, executionTimeMs, isError | schemas/event-schemas.ts:582, schemas/event-schemas.ts:987, schemas/event-schemas.ts:1239, hankweave-runtime.ts:2906 |
Network and backend#
The default endpoint is https://hw-telemetry.southbridge.ai, a self-hosted PostHog instance. Override it with HANKWEAVE_TELEMETRY_ENDPOINT or hankweave.json telemetry.endpoint. The embedded PostHog API key, phc_hDo9EY9g5eB18EYqnR2etTcEKXId7Rw971hoKQDxo5A, is public and write-only; replace it with POSTHOG_API_KEY when configuring a different backend.
Telemetry sends are fire-and-forget: a slow or failing backend does not throw or block the user. The PostHog client uses flushAt: 1 and flushInterval: 0. Immediate CLI capture() calls flush rather than batch; runtime events are queued and sent at shutdown with captureMany(). Each flush has a two-second timeout, and failures are silent.
HANKWEAVE_TELEMETRY_ENDPOINT=<endpoint-URI> bunx hankweave@0.10.0
See Troubleshooting for operational symptoms and Environment variables for the variable contract.
Debug mode without sending#
Set HANKWEAVE_TELEMETRY_DEBUG=1 or set hankweave.json telemetry.debug to true. Debug mode prints each event as [TELEMETRY DEBUG] <event_name>: { ... }, appends one JSON object per line to telemetry-debug.jsonl, and does not send the event to PostHog. The file is ~/.hankweave/telemetry-debug.jsonl by default and follows $HANKWEAVE_CACHE_DIR when set; the cache directory is created if needed.
HANKWEAVE_TELEMETRY_DEBUG=1 bunx hankweave@0.10.0
The observable line shape is:
[TELEMETRY DEBUG] <event_name>: { ... }
See Observe and debug and Environment variables.
First-run notice appears once#
After configuration resolution and before execution starts, the first run prints a boxed notice saying that Hankweave collects anonymous usage statistics. It says that personal information, file contents, and prompts are not collected, links to this page, names HANKWEAVE_TELEMETRY=0 as an opt-out, and says the notice will not be shown again. The notice is shown even when telemetry is disabled.
hasNoticeBeenShown() checks noticeShownAt, and markNoticeShown() writes it to the identity file. The source-defined text is:
const NOTICE_TEXT = `
┌─────────────────────────────────────────────────────────────────┐
│ │
│ Hankweave collects anonymous usage statistics to help │
│ improve the tool. No personal information, file contents, │
│ or prompts are collected. │
│ │
│ Learn more: https://docs.hankweave.dev/reference/telemetry │
│ Opt out: export HANKWEAVE_TELEMETRY=0 │
│ │
│ This notice won't be shown again. │
│ │
└─────────────────────────────────────────────────────────────────┘
`;
// =============================================================================
See First run and Identity and reset.
Error tracking and scrubbing#
Uncaught exceptions and unhandled rejections are auto-captured through PostHog's enableExceptionAutocapture: true. Manual captureError() calls add failure and correlation context such as codonStatus, runStatus, errorCode, exitCode, failureType, runIdHash, and codonIdHash.
Before sending, the scrubber removes home-directory paths, API keys and tokens, KEY=VALUE patterns, and absolute paths from error messages and stack traces. It mutates the error's .message and .stack in place. Error tracking uses the same PostHog client as telemetry and flushes at shutdown with its own two-second timeout.
A manual capture can carry safe correlation fields:
captureError(error, {
codonStatus: "<status>",
runStatus: "<status>",
errorCode: "<code>",
runIdHash: "<SHA256 hash>",
codonIdHash: "<SHA256 hash>"
});
See Troubleshooting and Errors and exit codes.
Integration at shutdown#
The CLI creates TelemetryCollector after configuration resolution, passes it to HankweaveRuntime with setTelemetryCollector(), and subscribes the collector to the runtime's server events.
At shutdown, sendRunTelemetry() reads the final run state, composes run_started, queued events, the applicable run_completed or run_failed event, and $ai_trace, then sends the batch with captureMany() before shutting down the client. Telemetry configuration is read directly from hankweave.json; resolveSettings() removes telemetry from the effective runtime-configuration layer.
The order is:
// Pseudocode, not the implementation.
resolve config
create TelemetryCollector
runtime.setTelemetryCollector(collector)
runtime subscribes collector to server events
at shutdown:
read final run state
compose run_started + queued events + terminal event
captureMany()
client.shutdown()
See Runtime architecture, Events, and Hankweave JSON.