Keep Your AI Agent Traces on Your Machine: A Local-First Approach A developer argues that AI agent traces often contain sensitive data and should be kept local by default. They propose a local-first tracing design with explicit capture modes and typed metadata events to control data exposure. The approach prioritizes intentional movement of trace data rather than automatic export to external services. Adding tracing to an AI agent changes more than the debugging experience. It also creates a new data path. A trace can contain user messages, system instructions, retrieved documents, tool arguments, tool results, model output, identifiers, and internal business logic. Sending that trace to an external service may therefore be equivalent to sending the underlying application data. The right first question is not simply, “Does this tracing SDK have a good dashboard?” It is, “What does it capture, where does it go, and who controls it?” A local-first tracing design gives developers useful execution visibility while keeping export and sharing decisions explicit. It is not a rejection of hosted observability. It is a safer default for the development loop and a clearer boundary for sensitive data. Traditional telemetry can also expose sensitive information, but AI traces are unusually payload-rich. The data needed to explain an agent’s behavior is often the same data the agent was asked to process. | Trace source | What it may reveal | |---|---| | User and model messages | Personal data, confidential requests, generated decisions | | System prompts | Internal policies, workflow rules, security assumptions | | Tool calls | Identifiers, query parameters, authorization context | | Tool results | Customer records, database rows, third-party API data | | Retrieval context | Private documents, proprietary knowledge, access-controlled text | | Errors and retries | Stack traces, request fragments, infrastructure details | This does not mean that rich traces are always inappropriate. It means trace data needs an owner, a classification, a retention period, and an approved destination just like any other sensitive dataset. In a local-first workflow, the initial trace is written to a developer machine, an isolated test workspace, a CI runner, or infrastructure controlled by the organization. Nothing is exported merely because tracing was enabled. agent execution | v capture policy | v controlled local sink | +-- local inspection | +-- reviewed, reduced export -- approved shared platform The important property is intentional movement . Developers can inspect the execution locally, reduce the data to what is needed, and share only an approved artifact. Local-first is not the same as automatically secure. A laptop may be compromised, a workspace may be synchronized to a consumer cloud account, and a CI artifact may be visible to more people than expected. Local traces still require access controls, retention rules, and safe defaults. A single on/off tracing switch is too coarse for most agents. Use explicit capture modes instead: | Mode | Captured data | Typical use | |---|---|---| | Metadata | Names, timing, status, token counts, safe categories | Default development and production telemetry | | Selective payload | Approved fields from specific tools or steps | Focused debugging in a controlled environment | | Full fidelity | Prompts, outputs, and raw payloads | Temporary incident investigation with short retention | Full-fidelity capture should require a deliberate configuration change, produce a visible warning, and expire automatically. It should never be enabled silently by a dependency update or a default environment variable. The most reliable redaction strategy is not capturing unnecessary data. A typed metadata event makes the safe path easy and prevents arbitrary payloads from drifting into normal traces. type TraceStatus = 'ok' | 'error'; type TraceKind = 'agent' | 'model' | 'tool' | 'retrieval'; type SafeValue = string | number | boolean | null; export type TraceEvent = { version: 1; traceId: string; spanId: string; parentSpanId?: string; timestamp: string; kind: TraceKind; name: string; status: TraceStatus; durationMs?: number; inputTokens?: number; outputTokens?: number; metadata: Record